dep: Add rapidyaml

This commit is contained in:
Stenzek
2024-02-04 02:18:15 +10:00
parent f7bed69e7c
commit 5c08fa9d00
68 changed files with 25758 additions and 0 deletions

View File

@ -0,0 +1,124 @@
#ifndef _C4_BASE64_HPP_
#define _C4_BASE64_HPP_
/** @file base64.hpp encoding/decoding for base64.
* @see https://en.wikipedia.org/wiki/Base64
* @see https://www.base64encode.org/
* */
#include "c4/charconv.hpp"
#include "c4/blob.hpp"
namespace c4 {
/** check that the given buffer is a valid base64 encoding
* @see https://en.wikipedia.org/wiki/Base64 */
C4CORE_EXPORT bool base64_valid(csubstr encoded);
/** base64-encode binary data.
* @param encoded [out] output buffer for encoded data
* @param data [in] the input buffer with the binary data
*
* @return the number of bytes needed to return the output (ie the
* required size for @p encoded). No writes occur beyond the end of
* the output buffer, so it is safe to do a speculative call where the
* encoded buffer is empty, or maybe too small. The caller should
* ensure that the returned size is smaller than the size of the
* encoded buffer.
*
* @note the result depends on endianness. If transfer between
* little/big endian systems is desired, the caller should normalize
* @p data before encoding.
*
* @see https://en.wikipedia.org/wiki/Base64 */
C4CORE_EXPORT size_t base64_encode(substr encoded, cblob data);
/** decode the base64 encoding in the given buffer
* @param encoded [in] the encoded base64
* @param data [out] the output buffer
*
* @return the number of bytes needed to return the output (ie the
* required size for @p data). No writes occur beyond the end of the
* output buffer, so it is safe to do a speculative call where the
* data buffer is empty, or maybe too small. The caller should ensure
* that the returned size is smaller than the size of the data buffer.
*
* @note the result depends on endianness. If transfer between
* little/big endian systems is desired, the caller should normalize
* @p data after decoding.
*
* @see https://en.wikipedia.org/wiki/Base64 */
C4CORE_EXPORT size_t base64_decode(csubstr encoded, blob data);
namespace fmt {
template<typename CharOrConstChar>
struct base64_wrapper_
{
blob_<CharOrConstChar> data;
base64_wrapper_() : data() {}
base64_wrapper_(blob_<CharOrConstChar> blob) : data(blob) {}
};
/** a tag type to mark a payload as base64-encoded */
using const_base64_wrapper = base64_wrapper_<cbyte>;
/** a tag type to mark a payload to be encoded as base64 */
using base64_wrapper = base64_wrapper_<byte>;
/** mark a variable to be written in base64 format */
template<class ...Args>
C4_ALWAYS_INLINE const_base64_wrapper cbase64(Args const& C4_RESTRICT ...args)
{
return const_base64_wrapper(cblob(args...));
}
/** mark a csubstr to be written in base64 format */
C4_ALWAYS_INLINE const_base64_wrapper cbase64(csubstr s)
{
return const_base64_wrapper(cblob(s.str, s.len));
}
/** mark a variable to be written in base64 format */
template<class ...Args>
C4_ALWAYS_INLINE const_base64_wrapper base64(Args const& C4_RESTRICT ...args)
{
return const_base64_wrapper(cblob(args...));
}
/** mark a csubstr to be written in base64 format */
C4_ALWAYS_INLINE const_base64_wrapper base64(csubstr s)
{
return const_base64_wrapper(cblob(s.str, s.len));
}
/** mark a variable to be read in base64 format */
template<class ...Args>
C4_ALWAYS_INLINE base64_wrapper base64(Args &... args)
{
return base64_wrapper(blob(args...));
}
/** mark a variable to be read in base64 format */
C4_ALWAYS_INLINE base64_wrapper base64(substr s)
{
return base64_wrapper(blob(s.str, s.len));
}
} // namespace fmt
/** write a variable in base64 format */
inline size_t to_chars(substr buf, fmt::const_base64_wrapper b)
{
return base64_encode(buf, b.data);
}
/** read a variable in base64 format */
inline size_t from_chars(csubstr buf, fmt::base64_wrapper *b)
{
return base64_decode(buf, b->data);
}
} // namespace c4
#endif /* _C4_BASE64_HPP_ */

View File

@ -0,0 +1,67 @@
#ifndef _C4_BLOB_HPP_
#define _C4_BLOB_HPP_
#include "c4/types.hpp"
#include "c4/error.hpp"
/** @file blob.hpp Mutable and immutable binary data blobs.
*/
namespace c4 {
template<class T>
struct blob_;
namespace detail {
template<class T> struct is_blob_type : std::integral_constant<bool, false> {};
template<class T> struct is_blob_type<blob_<T>> : std::integral_constant<bool, true> {};
template<class T> struct is_blob_value_type : std::integral_constant<bool, (std::is_fundamental<T>::value || std::is_trivially_copyable<T>::value)> {};
} // namespace
template<class T>
struct blob_
{
static_assert(std::is_same<T, byte>::value || std::is_same<T, cbyte>::value, "must be either byte or cbyte");
static_assert(sizeof(T) == 1u, "must be either byte or cbyte");
public:
T * buf;
size_t len;
public:
C4_ALWAYS_INLINE blob_() noexcept = default;
C4_ALWAYS_INLINE blob_(blob_ const& that) noexcept = default;
C4_ALWAYS_INLINE blob_(blob_ && that) noexcept = default;
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; }
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) {}
#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; }
#undef _C4_REQUIRE_BLOBTYPE
};
/** an immutable binary blob */
using cblob = blob_<cbyte>;
/** a mutable binary blob */
using blob = blob_< byte>;
C4_MUST_BE_TRIVIAL_COPY(blob);
C4_MUST_BE_TRIVIAL_COPY(cblob);
} // namespace c4
#endif // _C4_BLOB_HPP_

View File

@ -0,0 +1,168 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Very good intro:
@see https://code.msdn.microsoft.com/windowsdesktop/Writing-type-visualizers-2eae77a2
See also:
@see http://blogs.msdn.com/b/vcblog/archive/2013/06/28/using-visual-studio-2013-to-write-maintainable-native-visualizations-natvis.aspx?PageIndex=2
@see http://blogs.msdn.com/b/vcblog/archive/2015/09/28/debug-visualizers-in-visual-c-2015.aspx
@see http://stackoverflow.com/questions/36883414/limit-display-of-char-in-natvis-file-to-specific-length
-->
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="c4::basic_substring&lt;*&gt;">
<DisplayString>{str,[len]} (sz={len})</DisplayString>
<StringView>str,[len]</StringView>
<Expand>
<Item Name="[size]">len</Item>
<ArrayItems>
<Size>len</Size>
<ValuePointer>str</ValuePointer>
</ArrayItems>
</Expand>
</Type>
<Type Name="c4::span&lt;*&gt;">
<DisplayString>{m_ptr,[m_size]} (sz={m_size})</DisplayString>
<Expand>
<Item Name="[size]">m_size</Item>
<ArrayItems>
<Size>m_size</Size>
<ValuePointer>m_ptr</ValuePointer>
</ArrayItems>
</Expand>
</Type>
<Type Name="c4::spanrs&lt;*&gt;">
<DisplayString>{m_ptr,[m_size]} (sz={m_size}, cap={m_capacity})</DisplayString>
<Expand>
<Item Name="[size]">m_size</Item>
<Item Name="[capacity]">m_capacity</Item>
<ArrayItems>
<Size>m_size</Size>
<ValuePointer>m_ptr</ValuePointer>
</ArrayItems>
</Expand>
</Type>
<!-- display span<char>/span<const char> as a string too -->
<Type Name="c4::span&lt;char,*&gt;">
<DisplayString>{m_ptr,[m_size]} (sz={m_size})</DisplayString>
<StringView>m_ptr,[m_size]</StringView>
<Expand>
<Item Name="[size]">m_size</Item>
<ArrayItems>
<Size>m_size</Size>
<ValuePointer>m_ptr</ValuePointer>
</ArrayItems>
</Expand>
</Type>
<Type Name="c4::span&lt;const char,*&gt;">
<DisplayString>{m_ptr,[m_size]} (sz={m_size})</DisplayString>
<StringView>m_ptr,[m_size]</StringView>
<Expand>
<Item Name="[size]">m_size</Item>
<ArrayItems>
<Size>m_size</Size>
<ValuePointer>m_ptr</ValuePointer>
</ArrayItems>
</Expand>
</Type>
<!-- display spanrs<char>/spanrs<const char> as a string too -->
<Type Name="c4::spanrs&lt;char,*&gt;">
<DisplayString>{m_ptr,[m_size]} (sz={m_size}, cap={m_capacity})</DisplayString>
<StringView>m_ptr,[m_size]</StringView>
<Expand>
<Item Name="[size]">m_size</Item>
<Item Name="[capacity]">m_capacity</Item>
<ArrayItems>
<Size>m_size</Size>
<ValuePointer>m_ptr</ValuePointer>
</ArrayItems>
</Expand>
</Type>
<Type Name="c4::spanrs&lt;const char,*&gt;">
<DisplayString>{m_ptr,[m_size]} (sz={m_size}, cap={m_capacity})</DisplayString>
<StringView>m_ptr,[m_size]</StringView>
<Expand>
<Item Name="[size]">m_size</Item>
<Item Name="[capacity]">m_capacity</Item>
<ArrayItems>
<Size>m_size</Size>
<ValuePointer>m_ptr</ValuePointer>
</ArrayItems>
</Expand>
</Type>
<!-- =========================================================================================== -->
<Type Name="c4::string_impl&lt;*,*,*,*&gt;">
<DisplayString>{(($T3*)this)->m_str,[(($T3*)this)->m_size]} (sz={(($T3*)this)->m_size})</DisplayString>
<StringView>(($T3*)this)->m_str,[(($T3*)this)->m_size]</StringView>
<Expand>
<Synthetic Name="m_str">
<DisplayString>{(($T3*)this)->m_str,[(($T3*)this)->m_size]}</DisplayString>
<StringView>(($T3*)this)->m_str,[(($T3*)this)->m_size]</StringView>
</Synthetic>
<Synthetic Name="m_size">
<DisplayString>{(($T3*)this)->m_size}</DisplayString>
</Synthetic>
</Expand>
</Type>
<Type Name="c4::basic_substring&lt;*,*&gt;">
<DisplayString>{m_str,[m_size]} (sz={m_size})</DisplayString>
<StringView>m_str,[m_size]</StringView>
<Expand>
<Synthetic Name="[size]">
<DisplayString>{m_size}</DisplayString>
</Synthetic>
</Expand>
</Type>
<Type Name="c4::basic_substringrs&lt;*,*&gt;">
<DisplayString>{m_str,[m_size]} (sz={m_size},cap={m_capacity})</DisplayString>
<StringView>m_str,[m_size]</StringView>
<Expand>
<Synthetic Name="[size]">
<DisplayString>{m_size}</DisplayString>
</Synthetic>
<Synthetic Name="[capacity]">
<DisplayString>{m_capacity}</DisplayString>
</Synthetic>
<Synthetic Name="[full]">
<DisplayString>{m_str,[m_capacity]}</DisplayString>
<StringView>m_str,[m_capacity]</StringView>
</Synthetic>
</Expand>
</Type>
<Type Name="c4::basic_string&lt;*,*,*&gt;">
<DisplayString>{m_str,[m_size]} (sz={m_size},cap={m_capacity})</DisplayString>
<StringView>m_str,[m_size]</StringView>
<Expand>
<Synthetic Name="[size]">
<DisplayString>{m_size}</DisplayString>
</Synthetic>
<Synthetic Name="[full]">
<DisplayString>{m_str,[m_capacity]}</DisplayString>
<StringView>m_str,[m_capacity]</StringView>
</Synthetic>
</Expand>
</Type>
<!-- enum symbols -->
<Type Name="c4::EnumSymbols&lt;*&gt;::Sym">
<DisplayString>{value} - {name}</DisplayString>
<Expand>
<Item Name="[value]">value</Item>
<Item Name="[name]">name</Item>
</Expand>
</Type>
<Type Name="c4::EnumSymbols&lt;*&gt;">
<DisplayString>{m_symbols,[m_num]} (sz={m_num})</DisplayString>
<Expand>
<Item Name="[size]">m_num</Item>
<ArrayItems>
<Size>m_num</Size>
<ValuePointer>m_symbols</ValuePointer>
</ArrayItems>
</Expand>
</Type>
</AutoVisualizer>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,117 @@
#ifndef _C4_COMPILER_HPP_
#define _C4_COMPILER_HPP_
/** @file compiler.hpp Provides compiler information macros
* @ingroup basic_headers */
#include "c4/platform.hpp"
// Compilers:
// C4_MSVC
// Visual Studio 2022: MSVC++ 17, 1930
// Visual Studio 2019: MSVC++ 16, 1920
// Visual Studio 2017: MSVC++ 15
// Visual Studio 2015: MSVC++ 14
// Visual Studio 2013: MSVC++ 13
// Visual Studio 2013: MSVC++ 12
// Visual Studio 2012: MSVC++ 11
// Visual Studio 2010: MSVC++ 10
// Visual Studio 2008: MSVC++ 09
// Visual Studio 2005: MSVC++ 08
// C4_CLANG
// C4_GCC
// C4_ICC (intel compiler)
/** @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__)
# define C4_MSVC
# define C4_MSVC_VERSION_2022 17
# define C4_MSVC_VERSION_2019 16
# define C4_MSVC_VERSION_2017 15
# define C4_MSVC_VERSION_2015 14
# define C4_MSVC_VERSION_2013 12
# define C4_MSVC_VERSION_2012 11
# if _MSC_VER >= 1930
# define C4_MSVC_VERSION C4_MSVC_VERSION_2022 // visual studio 2022
# define C4_MSVC_2022
# elif _MSC_VER >= 1920
# define C4_MSVC_VERSION C_4MSVC_VERSION_2019 // visual studio 2019
# define C4_MSVC_2019
# elif _MSC_VER >= 1910
# define C4_MSVC_VERSION C4_MSVC_VERSION_2017 // visual studio 2017
# define C4_MSVC_2017
# elif _MSC_VER == 1900
# define C4_MSVC_VERSION C4_MSVC_VERSION_2015 // visual studio 2015
# define C4_MSVC_2015
# elif _MSC_VER == 1800
# error "MSVC version not supported"
# define C4_MSVC_VERSION C4_MSVC_VERSION_2013 // visual studio 2013
# define C4_MSVC_2013
# elif _MSC_VER == 1700
# error "MSVC version not supported"
# define C4_MSVC_VERSION C4_MSVC_VERSION_2012 // visual studio 2012
# define C4_MSVC_2012
# elif _MSC_VER == 1600
# error "MSVC version not supported"
# define C4_MSVC_VERSION 10 // visual studio 2010
# define C4_MSVC_2010
# elif _MSC_VER == 1500
# error "MSVC version not supported"
# define C4_MSVC_VERSION 09 // visual studio 2008
# define C4_MSVC_2008
# elif _MSC_VER == 1400
# error "MSVC version not supported"
# define C4_MSVC_VERSION 08 // visual studio 2005
# define C4_MSVC_2005
# else
# error "MSVC version not supported"
# endif // _MSC_VER
#else
# define C4_MSVC_VERSION 0 // visual studio not present
# define C4_GCC_LIKE
# ifdef __INTEL_COMPILER // check ICC before checking GCC, as ICC defines __GNUC__ too
# define C4_ICC
# define C4_ICC_VERSION __INTEL_COMPILER
# elif defined(__APPLE_CC__)
# define C4_XCODE
# if defined(__clang__)
# define C4_CLANG
# ifndef __apple_build_version__
# define C4_CLANG_VERSION C4_VERSION_ENCODED(__clang_major__, __clang_minor__, __clang_patchlevel__)
# else
# define C4_CLANG_VERSION __apple_build_version__
# endif
# else
# define C4_XCODE_VERSION __APPLE_CC__
# endif
# elif defined(__clang__)
# define C4_CLANG
# ifndef __apple_build_version__
# define C4_CLANG_VERSION C4_VERSION_ENCODED(__clang_major__, __clang_minor__, __clang_patchlevel__)
# else
# define C4_CLANG_VERSION __apple_build_version__
# endif
# elif defined(__GNUC__)
# define C4_GCC
# if defined(__GNUC_PATCHLEVEL__)
# define C4_GCC_VERSION C4_VERSION_ENCODED(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
# else
# define C4_GCC_VERSION C4_VERSION_ENCODED(__GNUC__, __GNUC_MINOR__, 0)
# endif
# if __GNUC__ < 5
# if __GNUC__ == 4 && __GNUC_MINOR__ >= 8
// provided by cmake sub-project
# include "c4/gcc-4.8.hpp"
# else
// we do not support GCC < 4.8:
// * misses std::is_trivially_copyable
// * misses std::align
// * -Wshadow has false positives when a local function parameter has the same name as a method
# error "GCC < 4.8 is not supported"
# endif
# endif
# endif
#endif // defined(C4_WIN) && defined(_MSC_VER)
#endif /* _C4_COMPILER_HPP_ */

View File

@ -0,0 +1,39 @@
#ifndef _C4_CONFIG_HPP_
#define _C4_CONFIG_HPP_
/** @defgroup basic_headers Basic headers
* @brief Headers providing basic macros, platform+cpu+compiler information,
* C++ facilities and basic typedefs. */
/** @file config.hpp Contains configuration defines and includes the basic_headers.
* @ingroup basic_headers */
//#define C4_DEBUG
#define C4_ERROR_SHOWS_FILELINE
//#define C4_ERROR_SHOWS_FUNC
//#define C4_ERROR_THROWS_EXCEPTION
//#define C4_NO_ALLOC_DEFAULTS
//#define C4_REDEFINE_CPPNEW
#ifndef C4_SIZE_TYPE
# define C4_SIZE_TYPE size_t
#endif
#ifndef C4_STR_SIZE_TYPE
# define C4_STR_SIZE_TYPE C4_SIZE_TYPE
#endif
#ifndef C4_TIME_TYPE
# define C4_TIME_TYPE double
#endif
#include "c4/export.hpp"
#include "c4/preprocessor.hpp"
#include "c4/platform.hpp"
#include "c4/cpu.hpp"
#include "c4/compiler.hpp"
#include "c4/language.hpp"
#include "c4/types.hpp"
#endif // _C4_CONFIG_HPP_

View File

@ -0,0 +1,149 @@
#ifndef _C4_CPU_HPP_
#define _C4_CPU_HPP_
/** @file cpu.hpp Provides processor information macros
* @ingroup basic_headers */
// 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
#ifdef __ORDER_LITTLE_ENDIAN__
# define _C4EL __ORDER_LITTLE_ENDIAN__
#else
# define _C4EL 1234
#endif
#ifdef __ORDER_BIG_ENDIAN__
# define _C4EB __ORDER_BIG_ENDIAN__
#else
# define _C4EB 4321
#endif
// mixed byte order (eg, PowerPC or ia64)
#define _C4EM 1111
#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64) || defined(_M_X64)
# define C4_CPU_X86_64
# define C4_WORDSIZE 8
# define C4_BYTE_ORDER _C4EL
#elif defined(__i386) || defined(__i386__) || defined(_M_IX86)
# define C4_CPU_X86
# define C4_WORDSIZE 4
# define C4_BYTE_ORDER _C4EL
#elif defined(__arm__) || defined(_M_ARM) \
|| defined(__TARGET_ARCH_ARM) || defined(__aarch64__) || defined(_M_ARM64)
# if defined(__aarch64__) || defined(_M_ARM64)
# define C4_CPU_ARM64
# define C4_CPU_ARMV8
# define C4_WORDSIZE 8
# else
# define C4_CPU_ARM
# define C4_WORDSIZE 4
# if defined(__ARM_ARCH_8__) || defined(__ARM_ARCH_8A__) \
|| (defined(__ARCH_ARM) && __ARCH_ARM >= 8) \
|| (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM >= 8)
# define C4_CPU_ARMV8
# elif defined(__ARM_ARCH_7__) || defined(_ARM_ARCH_7) \
|| defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) \
|| defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7S__) \
|| defined(__ARM_ARCH_7EM__) \
|| (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM >= 7) \
|| (defined(_M_ARM) && _M_ARM >= 7)
# define C4_CPU_ARMV7
# elif defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) \
|| defined(__ARM_ARCH_6T2__) || defined(__ARM_ARCH_6Z__) \
|| defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6ZK__) \
|| 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__) \
|| defined(__ARM_ARCH_5TE__) \
|| (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM >= 5)
# define C4_CPU_ARMV5
# elif defined(__ARM_ARCH_4T__) \
|| (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM >= 4)
# define C4_CPU_ARMV4
# else
# error "unknown CPU architecture: ARM"
# endif
# endif
# if defined(__ARMEL__) || defined(__LITTLE_ENDIAN__) || defined(__AARCH64EL__) \
|| (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) \
|| defined(_MSC_VER) // winarm64 does not provide any of the above macros,
// but advises little-endianess:
// https://docs.microsoft.com/en-us/cpp/build/overview-of-arm-abi-conventions?view=msvc-170
// So if it is visual studio compiling, we'll assume little endian.
# define C4_BYTE_ORDER _C4EL
# elif defined(__ARMEB__) || defined(__BIG_ENDIAN__) || defined(__AARCH64EB__) \
|| (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))
# define C4_BYTE_ORDER _C4EB
# elif defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_PDP_ENDIAN__)
# define C4_BYTE_ORDER _C4EM
# else
# error "unknown endianness"
# endif
#elif defined(__ia64) || defined(__ia64__) || defined(_M_IA64)
# define C4_CPU_IA64
# define C4_WORDSIZE 8
# define C4_BYTE_ORDER _C4EM
// itanium is bi-endian - check byte order below
#elif defined(__ppc__) || defined(__ppc) || defined(__powerpc__) \
|| defined(_ARCH_COM) || defined(_ARCH_PWR) || defined(_ARCH_PPC) \
|| defined(_M_MPPC) || defined(_M_PPC)
# if defined(__ppc64__) || defined(__powerpc64__) || defined(__64BIT__)
# define C4_CPU_PPC64
# define C4_WORDSIZE 8
# else
# define C4_CPU_PPC
# define C4_WORDSIZE 4
# endif
# define C4_BYTE_ORDER _C4EM
// ppc is bi-endian - check byte order below
#elif defined(__s390x__) || defined(__zarch__) || defined(__SYSC_ZARCH_)
# define C4_CPU_S390_X
# define C4_WORDSIZE 8
# define C4_BYTE_ORDER _C4EB
#elif defined(__xtensa__) || defined(__XTENSA__)
# define C4_CPU_XTENSA
# define C4_WORDSIZE 4
// not sure about this...
# if defined(__XTENSA_EL__) || defined(__xtensa_el__)
# define C4_BYTE_ORDER _C4EL
# else
# define C4_BYTE_ORDER _C4EB
# endif
#elif defined(__riscv)
# if __riscv_xlen == 64
# define C4_CPU_RISCV64
# define C4_WORDSIZE 8
# else
# define C4_CPU_RISCV32
# define C4_WORDSIZE 4
# endif
# define C4_BYTE_ORDER _C4EL
#elif defined(__EMSCRIPTEN__)
# define C4_BYTE_ORDER _C4EL
# define C4_WORDSIZE 4
#elif defined(SWIG)
# error "please define CPU architecture macros when compiling with swig"
#else
# error "unknown CPU architecture"
#endif
#define C4_LITTLE_ENDIAN (C4_BYTE_ORDER == _C4EL)
#define C4_BIG_ENDIAN (C4_BYTE_ORDER == _C4EB)
#define C4_MIXED_ENDIAN (C4_BYTE_ORDER == _C4EM)
#endif /* _C4_CPU_HPP_ */

View File

@ -0,0 +1,582 @@
#ifndef C4_DUMP_HPP_
#define C4_DUMP_HPP_
#include <c4/substr.hpp>
namespace c4 {
C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wold-style-cast")
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** type of the function to dump characters */
using DumperPfn = void (*)(csubstr buf);
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
template<DumperPfn dumpfn, class Arg>
inline size_t dump(substr buf, Arg const& a)
{
size_t sz = to_chars(buf, a); // need to serialize to the buffer
if(C4_LIKELY(sz <= buf.len))
dumpfn(buf.first(sz));
return sz;
}
template<class DumperFn, class Arg>
inline size_t dump(DumperFn &&dumpfn, substr buf, Arg const& a)
{
size_t sz = to_chars(buf, a); // need to serialize to the buffer
if(C4_LIKELY(sz <= buf.len))
dumpfn(buf.first(sz));
return sz;
}
template<DumperPfn dumpfn>
inline size_t dump(substr buf, csubstr a)
{
if(buf.len)
dumpfn(a); // dump directly, no need to serialize to the buffer
return 0; // no space was used in the buffer
}
template<class DumperFn>
inline size_t dump(DumperFn &&dumpfn, substr buf, csubstr a)
{
if(buf.len)
dumpfn(a); // dump directly, no need to serialize to the buffer
return 0; // no space was used in the buffer
}
template<DumperPfn dumpfn, size_t N>
inline size_t dump(substr buf, const char (&a)[N])
{
if(buf.len)
dumpfn(csubstr(a)); // dump directly, no need to serialize to the buffer
return 0; // no space was used in the buffer
}
template<class DumperFn, size_t N>
inline size_t dump(DumperFn &&dumpfn, substr buf, const char (&a)[N])
{
if(buf.len)
dumpfn(csubstr(a)); // dump directly, no need to serialize to the buffer
return 0; // no space was used in the buffer
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** */
struct DumpResults
{
enum : size_t { noarg = (size_t)-1 };
size_t bufsize = 0;
size_t lastok = noarg;
bool success_until(size_t expected) const { return lastok == noarg ? false : lastok >= expected; }
bool write_arg(size_t arg) const { return lastok == noarg || arg > lastok; }
size_t argfail() const { return lastok + 1; }
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/// @cond dev
// terminates the variadic recursion
template<class DumperFn>
size_t cat_dump(DumperFn &&, substr)
{
return 0;
}
// terminates the variadic recursion
template<DumperPfn dumpfn>
size_t cat_dump(substr)
{
return 0;
}
/// @endcond
/** take the function pointer as a function argument */
template<class DumperFn, class Arg, class... Args>
size_t cat_dump(DumperFn &&dumpfn, substr buf, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
size_t size_for_a = dump(dumpfn, buf, a);
if(C4_UNLIKELY(size_for_a > buf.len))
buf = buf.first(0); // ensure no more calls
size_t size_for_more = cat_dump(dumpfn, buf, more...);
return size_for_more > size_for_a ? size_for_more : size_for_a;
}
/** take the function pointer as a template argument */
template<DumperPfn dumpfn,class Arg, class... Args>
size_t cat_dump(substr buf, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
size_t size_for_a = dump<dumpfn>(buf, a);
if(C4_LIKELY(size_for_a > buf.len))
buf = buf.first(0); // ensure no more calls
size_t size_for_more = cat_dump<dumpfn>(buf, more...);
return size_for_more > size_for_a ? size_for_more : size_for_a;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/// @cond dev
namespace detail {
// terminates the variadic recursion
template<DumperPfn dumpfn, class Arg>
DumpResults cat_dump_resume(size_t currarg, DumpResults results, substr buf, Arg const& C4_RESTRICT a)
{
if(C4_LIKELY(results.write_arg(currarg)))
{
size_t sz = dump<dumpfn>(buf, a); // yield to the specialized function
if(currarg == results.lastok + 1 && sz <= buf.len)
results.lastok = currarg;
results.bufsize = sz > results.bufsize ? sz : results.bufsize;
}
return results;
}
// terminates the variadic recursion
template<class DumperFn, class Arg>
DumpResults cat_dump_resume(size_t currarg, DumperFn &&dumpfn, DumpResults results, substr buf, Arg const& C4_RESTRICT a)
{
if(C4_LIKELY(results.write_arg(currarg)))
{
size_t sz = dump(dumpfn, buf, a); // yield to the specialized function
if(currarg == results.lastok + 1 && sz <= buf.len)
results.lastok = currarg;
results.bufsize = sz > results.bufsize ? sz : results.bufsize;
}
return results;
}
template<DumperPfn dumpfn, class Arg, class... Args>
DumpResults cat_dump_resume(size_t currarg, DumpResults results, substr buf, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
results = detail::cat_dump_resume<dumpfn>(currarg, results, buf, a);
return detail::cat_dump_resume<dumpfn>(currarg + 1u, results, buf, more...);
}
template<class DumperFn, class Arg, class... Args>
DumpResults cat_dump_resume(size_t currarg, DumperFn &&dumpfn, DumpResults results, substr buf, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
results = detail::cat_dump_resume(currarg, dumpfn, results, buf, a);
return detail::cat_dump_resume(currarg + 1u, dumpfn, results, buf, more...);
}
} // namespace detail
/// @endcond
template<DumperPfn dumpfn, class Arg, class... Args>
C4_ALWAYS_INLINE DumpResults cat_dump_resume(DumpResults results, substr buf, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
if(results.bufsize > buf.len)
return results;
return detail::cat_dump_resume<dumpfn>(0u, results, buf, a, more...);
}
template<class DumperFn, class Arg, class... Args>
C4_ALWAYS_INLINE DumpResults cat_dump_resume(DumperFn &&dumpfn, DumpResults results, substr buf, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
if(results.bufsize > buf.len)
return results;
return detail::cat_dump_resume(0u, dumpfn, results, buf, a, more...);
}
template<DumperPfn dumpfn, class Arg, class... Args>
C4_ALWAYS_INLINE DumpResults cat_dump_resume(substr buf, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
return detail::cat_dump_resume<dumpfn>(0u, DumpResults{}, buf, a, more...);
}
template<class DumperFn, class Arg, class... Args>
C4_ALWAYS_INLINE DumpResults cat_dump_resume(DumperFn &&dumpfn, substr buf, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
return detail::cat_dump_resume(0u, dumpfn, DumpResults{}, buf, a, more...);
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/// @cond dev
// terminate the recursion
template<class DumperFn, class Sep>
size_t catsep_dump(DumperFn &&, substr, Sep const& C4_RESTRICT)
{
return 0;
}
// terminate the recursion
template<DumperPfn dumpfn, class Sep>
size_t catsep_dump(substr, Sep const& C4_RESTRICT)
{
return 0;
}
/// @endcond
/** take the function pointer as a function argument */
template<class DumperFn, class Sep, class Arg, class... Args>
size_t catsep_dump(DumperFn &&dumpfn, substr buf, Sep const& C4_RESTRICT sep, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
size_t sz = dump(dumpfn, buf, a);
if(C4_UNLIKELY(sz > buf.len))
buf = buf.first(0); // ensure no more calls
if C4_IF_CONSTEXPR (sizeof...(more) > 0)
{
size_t szsep = dump(dumpfn, buf, sep);
if(C4_UNLIKELY(szsep > buf.len))
buf = buf.first(0); // ensure no more calls
sz = sz > szsep ? sz : szsep;
}
size_t size_for_more = catsep_dump(dumpfn, buf, sep, more...);
return size_for_more > sz ? size_for_more : sz;
}
/** take the function pointer as a template argument */
template<DumperPfn dumpfn, class Sep, class Arg, class... Args>
size_t catsep_dump(substr buf, Sep const& C4_RESTRICT sep, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
size_t sz = dump<dumpfn>(buf, a);
if(C4_UNLIKELY(sz > buf.len))
buf = buf.first(0); // ensure no more calls
if C4_IF_CONSTEXPR (sizeof...(more) > 0)
{
size_t szsep = dump<dumpfn>(buf, sep);
if(C4_UNLIKELY(szsep > buf.len))
buf = buf.first(0); // ensure no more calls
sz = sz > szsep ? sz : szsep;
}
size_t size_for_more = catsep_dump<dumpfn>(buf, sep, more...);
return size_for_more > sz ? size_for_more : sz;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/// @cond dev
namespace detail {
template<DumperPfn dumpfn, class Arg>
void catsep_dump_resume_(size_t currarg, DumpResults *C4_RESTRICT results, substr *C4_RESTRICT buf, Arg const& C4_RESTRICT a)
{
if(C4_LIKELY(results->write_arg(currarg)))
{
size_t sz = dump<dumpfn>(*buf, a);
results->bufsize = sz > results->bufsize ? sz : results->bufsize;
if(C4_LIKELY(sz <= buf->len))
results->lastok = currarg;
else
buf->len = 0;
}
}
template<class DumperFn, class Arg>
void catsep_dump_resume_(size_t currarg, DumperFn &&dumpfn, DumpResults *C4_RESTRICT results, substr *C4_RESTRICT buf, Arg const& C4_RESTRICT a)
{
if(C4_LIKELY(results->write_arg(currarg)))
{
size_t sz = dump(dumpfn, *buf, a);
results->bufsize = sz > results->bufsize ? sz : results->bufsize;
if(C4_LIKELY(sz <= buf->len))
results->lastok = currarg;
else
buf->len = 0;
}
}
template<DumperPfn dumpfn, class Sep, class Arg>
C4_ALWAYS_INLINE void catsep_dump_resume(size_t currarg, DumpResults *C4_RESTRICT results, substr *C4_RESTRICT buf, Sep const& C4_RESTRICT, Arg const& C4_RESTRICT a)
{
detail::catsep_dump_resume_<dumpfn>(currarg, results, buf, a);
}
template<class DumperFn, class Sep, class Arg>
C4_ALWAYS_INLINE void catsep_dump_resume(size_t currarg, DumperFn &&dumpfn, DumpResults *C4_RESTRICT results, substr *C4_RESTRICT buf, Sep const& C4_RESTRICT, Arg const& C4_RESTRICT a)
{
detail::catsep_dump_resume_(currarg, dumpfn, results, buf, a);
}
template<DumperPfn dumpfn, class Sep, class Arg, class... Args>
C4_ALWAYS_INLINE void catsep_dump_resume(size_t currarg, DumpResults *C4_RESTRICT results, substr *C4_RESTRICT buf, Sep const& C4_RESTRICT sep, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
detail::catsep_dump_resume_<dumpfn>(currarg , results, buf, a);
detail::catsep_dump_resume_<dumpfn>(currarg + 1u, results, buf, sep);
detail::catsep_dump_resume <dumpfn>(currarg + 2u, results, buf, sep, more...);
}
template<class DumperFn, class Sep, class Arg, class... Args>
C4_ALWAYS_INLINE void catsep_dump_resume(size_t currarg, DumperFn &&dumpfn, DumpResults *C4_RESTRICT results, substr *C4_RESTRICT buf, Sep const& C4_RESTRICT sep, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
detail::catsep_dump_resume_(currarg , dumpfn, results, buf, a);
detail::catsep_dump_resume_(currarg + 1u, dumpfn, results, buf, sep);
detail::catsep_dump_resume (currarg + 2u, dumpfn, results, buf, sep, more...);
}
} // namespace detail
/// @endcond
template<DumperPfn dumpfn, class Sep, class... Args>
C4_ALWAYS_INLINE DumpResults catsep_dump_resume(DumpResults results, substr buf, Sep const& C4_RESTRICT sep, Args const& C4_RESTRICT ...more)
{
detail::catsep_dump_resume<dumpfn>(0u, &results, &buf, sep, more...);
return results;
}
template<class DumperFn, class Sep, class... Args>
C4_ALWAYS_INLINE DumpResults catsep_dump_resume(DumperFn &&dumpfn, DumpResults results, substr buf, Sep const& C4_RESTRICT sep, Args const& C4_RESTRICT ...more)
{
detail::catsep_dump_resume(0u, dumpfn, &results, &buf, sep, more...);
return results;
}
template<DumperPfn dumpfn, class Sep, class... Args>
C4_ALWAYS_INLINE DumpResults catsep_dump_resume(substr buf, Sep const& C4_RESTRICT sep, Args const& C4_RESTRICT ...more)
{
DumpResults results;
detail::catsep_dump_resume<dumpfn>(0u, &results, &buf, sep, more...);
return results;
}
template<class DumperFn, class Sep, class... Args>
C4_ALWAYS_INLINE DumpResults catsep_dump_resume(DumperFn &&dumpfn, substr buf, Sep const& C4_RESTRICT sep, Args const& C4_RESTRICT ...more)
{
DumpResults results;
detail::catsep_dump_resume(0u, dumpfn, &results, &buf, sep, more...);
return results;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** take the function pointer as a function argument */
template<class DumperFn>
C4_ALWAYS_INLINE size_t format_dump(DumperFn &&dumpfn, substr buf, csubstr fmt)
{
// we can dump without using buf
// but we'll only dump if the buffer is ok
if(C4_LIKELY(buf.len > 0 && fmt.len))
dumpfn(fmt);
return 0u;
}
/** take the function pointer as a function argument */
template<DumperPfn dumpfn>
C4_ALWAYS_INLINE size_t format_dump(substr buf, csubstr fmt)
{
// we can dump without using buf
// but we'll only dump if the buffer is ok
if(C4_LIKELY(buf.len > 0 && fmt.len > 0))
dumpfn(fmt);
return 0u;
}
/** take the function pointer as a function argument */
template<class DumperFn, class Arg, class... Args>
size_t format_dump(DumperFn &&dumpfn, substr buf, csubstr fmt, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
// we can dump without using buf
// but we'll only dump if the buffer is ok
size_t pos = fmt.find("{}"); // @todo use _find_fmt()
if(C4_UNLIKELY(pos == csubstr::npos))
{
if(C4_LIKELY(buf.len > 0 && fmt.len > 0))
dumpfn(fmt);
return 0u;
}
if(C4_LIKELY(buf.len > 0 && pos > 0))
dumpfn(fmt.first(pos)); // we can dump without using buf
fmt = fmt.sub(pos + 2); // skip {} do this before assigning to pos again
pos = dump(dumpfn, buf, a);
if(C4_UNLIKELY(pos > buf.len))
buf.len = 0; // ensure no more calls to dump
size_t size_for_more = format_dump(dumpfn, buf, fmt, more...);
return size_for_more > pos ? size_for_more : pos;
}
/** take the function pointer as a template argument */
template<DumperPfn dumpfn, class Arg, class... Args>
size_t format_dump(substr buf, csubstr fmt, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
// we can dump without using buf
// but we'll only dump if the buffer is ok
size_t pos = fmt.find("{}"); // @todo use _find_fmt()
if(C4_UNLIKELY(pos == csubstr::npos))
{
if(C4_LIKELY(buf.len > 0 && fmt.len > 0))
dumpfn(fmt);
return 0u;
}
if(C4_LIKELY(buf.len > 0 && pos > 0))
dumpfn(fmt.first(pos)); // we can dump without using buf
fmt = fmt.sub(pos + 2); // skip {} do this before assigning to pos again
pos = dump<dumpfn>(buf, a);
if(C4_UNLIKELY(pos > buf.len))
buf.len = 0; // ensure no more calls to dump
size_t size_for_more = format_dump<dumpfn>(buf, fmt, more...);
return size_for_more > pos ? size_for_more : pos;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/// @cond dev
namespace detail {
template<DumperPfn dumpfn>
DumpResults format_dump_resume(size_t currarg, DumpResults results, substr buf, csubstr fmt)
{
// we can dump without using buf
// but we'll only dump if the buffer is ok
if(C4_LIKELY(buf.len > 0))
{
dumpfn(fmt);
results.lastok = currarg;
}
return results;
}
template<class DumperFn>
DumpResults format_dump_resume(size_t currarg, DumperFn &&dumpfn, DumpResults results, substr buf, csubstr fmt)
{
// we can dump without using buf
// but we'll only dump if the buffer is ok
if(C4_LIKELY(buf.len > 0))
{
dumpfn(fmt);
results.lastok = currarg;
}
return results;
}
template<DumperPfn dumpfn, class Arg, class... Args>
DumpResults format_dump_resume(size_t currarg, DumpResults results, substr buf, csubstr fmt, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
// we need to process the format even if we're not
// going to print the first arguments because we're resuming
size_t pos = fmt.find("{}"); // @todo use _find_fmt()
// we can dump without using buf
// but we'll only dump if the buffer is ok
if(C4_LIKELY(results.write_arg(currarg)))
{
if(C4_UNLIKELY(pos == csubstr::npos))
{
if(C4_LIKELY(buf.len > 0))
{
results.lastok = currarg;
dumpfn(fmt);
}
return results;
}
if(C4_LIKELY(buf.len > 0))
{
results.lastok = currarg;
dumpfn(fmt.first(pos));
}
}
fmt = fmt.sub(pos + 2);
if(C4_LIKELY(results.write_arg(currarg + 1)))
{
pos = dump<dumpfn>(buf, a);
results.bufsize = pos > results.bufsize ? pos : results.bufsize;
if(C4_LIKELY(pos <= buf.len))
results.lastok = currarg + 1;
else
buf.len = 0;
}
return detail::format_dump_resume<dumpfn>(currarg + 2u, results, buf, fmt, more...);
}
/// @endcond
template<class DumperFn, class Arg, class... Args>
DumpResults format_dump_resume(size_t currarg, DumperFn &&dumpfn, DumpResults results, substr buf, csubstr fmt, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
// we need to process the format even if we're not
// going to print the first arguments because we're resuming
size_t pos = fmt.find("{}"); // @todo use _find_fmt()
// we can dump without using buf
// but we'll only dump if the buffer is ok
if(C4_LIKELY(results.write_arg(currarg)))
{
if(C4_UNLIKELY(pos == csubstr::npos))
{
if(C4_LIKELY(buf.len > 0))
{
results.lastok = currarg;
dumpfn(fmt);
}
return results;
}
if(C4_LIKELY(buf.len > 0))
{
results.lastok = currarg;
dumpfn(fmt.first(pos));
}
}
fmt = fmt.sub(pos + 2);
if(C4_LIKELY(results.write_arg(currarg + 1)))
{
pos = dump(dumpfn, buf, a);
results.bufsize = pos > results.bufsize ? pos : results.bufsize;
if(C4_LIKELY(pos <= buf.len))
results.lastok = currarg + 1;
else
buf.len = 0;
}
return detail::format_dump_resume(currarg + 2u, dumpfn, results, buf, fmt, more...);
}
} // namespace detail
template<DumperPfn dumpfn, class... Args>
C4_ALWAYS_INLINE DumpResults format_dump_resume(DumpResults results, substr buf, csubstr fmt, Args const& C4_RESTRICT ...more)
{
return detail::format_dump_resume<dumpfn>(0u, results, buf, fmt, more...);
}
template<class DumperFn, class... Args>
C4_ALWAYS_INLINE DumpResults format_dump_resume(DumperFn &&dumpfn, DumpResults results, substr buf, csubstr fmt, Args const& C4_RESTRICT ...more)
{
return detail::format_dump_resume(0u, dumpfn, results, buf, fmt, more...);
}
template<DumperPfn dumpfn, class... Args>
C4_ALWAYS_INLINE DumpResults format_dump_resume(substr buf, csubstr fmt, Args const& C4_RESTRICT ...more)
{
return detail::format_dump_resume<dumpfn>(0u, DumpResults{}, buf, fmt, more...);
}
template<class DumperFn, class... Args>
C4_ALWAYS_INLINE DumpResults format_dump_resume(DumperFn &&dumpfn, substr buf, csubstr fmt, Args const& C4_RESTRICT ...more)
{
return detail::format_dump_resume(0u, dumpfn, DumpResults{}, buf, fmt, more...);
}
C4_SUPPRESS_WARNING_GCC_CLANG_POP
} // namespace c4
#endif /* C4_DUMP_HPP_ */

View File

@ -0,0 +1,435 @@
#ifndef _C4_ERROR_HPP_
#define _C4_ERROR_HPP_
/** @file error.hpp Facilities for error reporting and runtime assertions. */
/** @defgroup error_checking Error checking */
#include "c4/config.hpp"
#ifdef _DOXYGEN_
/** if this is defined and exceptions are enabled, then calls to C4_ERROR()
* will throw an exception
* @ingroup error_checking */
# define C4_EXCEPTIONS_ENABLED
/** if this is defined and exceptions are enabled, then calls to C4_ERROR()
* will throw an exception
* @see C4_EXCEPTIONS_ENABLED
* @ingroup error_checking */
# define C4_ERROR_THROWS_EXCEPTION
/** evaluates to noexcept when C4_ERROR might be called and
* exceptions are disabled. Otherwise, defaults to nothing.
* @ingroup error_checking */
# define C4_NOEXCEPT
#endif // _DOXYGEN_
#if defined(C4_EXCEPTIONS_ENABLED) && defined(C4_ERROR_THROWS_EXCEPTION)
# define C4_NOEXCEPT
#else
# define C4_NOEXCEPT noexcept
#endif
namespace c4 {
namespace detail {
struct fail_type__ {};
} // detail
} // c4
#define C4_STATIC_ERROR(dummy_type, errmsg) \
static_assert(std::is_same<dummy_type, c4::detail::fail_type__>::value, errmsg)
//-----------------------------------------------------------------------------
#define C4_ASSERT_SAME_TYPE(ty1, ty2) \
C4_STATIC_ASSERT(std::is_same<ty1 C4_COMMA_X ty2>::value)
#define C4_ASSERT_DIFF_TYPE(ty1, ty2) \
C4_STATIC_ASSERT( ! std::is_same<ty1 C4_COMMA_X ty2>::value)
//-----------------------------------------------------------------------------
#ifdef _DOXYGEN_
/** utility macro that triggers a breakpoint when
* the debugger is attached and NDEBUG is not defined.
* @ingroup error_checking */
# define C4_DEBUG_BREAK()
#endif // _DOXYGEN_
#if defined(NDEBUG) || defined(C4_NO_DEBUG_BREAK)
# define C4_DEBUG_BREAK()
#else
# ifdef __clang__
# pragma clang diagnostic push
# 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]
# endif
# else
# if __clang_major__ >= 13
# 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]
# endif
# endif
# elif defined(__GNUC__)
# 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__)
# endif
#endif
namespace c4 {
C4CORE_EXPORT bool is_debugger_attached();
} // namespace c4
//-----------------------------------------------------------------------------
#ifdef __clang__
/* NOTE: using , ## __VA_ARGS__ to deal with zero-args calls to
* variadic macros is not portable, but works in clang, gcc, msvc, icc.
* clang requires switching off compiler warnings for pedantic mode.
* @see http://stackoverflow.com/questions/32047685/variadic-macro-without-arguments */
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" // warning: token pasting of ',' and __VA_ARGS__ is a GNU extension
#elif defined(__GNUC__)
/* GCC also issues a warning for zero-args calls to variadic macros.
* This warning is switched on with -pedantic and apparently there is no
* easy way to turn it off as with clang. But marking this as a system
* header works.
* @see https://gcc.gnu.org/onlinedocs/cpp/System-Headers.html
* @see http://stackoverflow.com/questions/35587137/ */
# pragma GCC system_header
#endif
//-----------------------------------------------------------------------------
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,
/** when an error happens log a message. */
ON_ERROR_LOG = 0x01 << 1,
/** when an error happens invoke a callback if it was set with
* set_error_callback(). */
ON_ERROR_CALLBACK = 0x01 << 2,
/** when an error happens call std::terminate(). */
ON_ERROR_ABORT = 0x01 << 3,
/** when an error happens and exceptions are enabled throw an exception.
* Without effect otherwise. */
ON_ERROR_THROW = 0x01 << 4,
/** the default flags. */
ON_ERROR_DEFAULTS = ON_ERROR_DEBUGBREAK|ON_ERROR_LOG|ON_ERROR_CALLBACK|ON_ERROR_ABORT
} ErrorFlags_e;
using error_flags = uint32_t;
C4CORE_EXPORT void set_error_flags(error_flags f);
C4CORE_EXPORT error_flags get_error_flags();
using error_callback_type = void (*)(const char* msg, size_t msg_size);
C4CORE_EXPORT void set_error_callback(error_callback_type cb);
C4CORE_EXPORT error_callback_type get_error_callback();
//-----------------------------------------------------------------------------
/** RAII class controling the error settings inside a scope. */
struct ScopedErrorSettings
{
error_flags m_flags;
error_callback_type m_callback;
explicit ScopedErrorSettings(error_callback_type cb)
: m_flags(get_error_flags()),
m_callback(get_error_callback())
{
set_error_callback(cb);
}
explicit ScopedErrorSettings(error_flags flags)
: m_flags(get_error_flags()),
m_callback(get_error_callback())
{
set_error_flags(flags);
}
explicit ScopedErrorSettings(error_flags flags, error_callback_type cb)
: m_flags(get_error_flags()),
m_callback(get_error_callback())
{
set_error_flags(flags);
set_error_callback(cb);
}
~ScopedErrorSettings()
{
set_error_flags(m_flags);
set_error_callback(m_callback);
}
};
//-----------------------------------------------------------------------------
/** source location */
struct srcloc;
C4CORE_EXPORT void handle_error(srcloc s, const char *fmt, ...);
C4CORE_EXPORT void handle_warning(srcloc s, const char *fmt, ...);
# define C4_ERROR(msg, ...) \
do { \
if(c4::get_error_flags() & c4::ON_ERROR_DEBUGBREAK) \
{ \
C4_DEBUG_BREAK() \
} \
c4::handle_error(C4_SRCLOC(), msg, ## __VA_ARGS__); \
} while(0)
# define C4_WARNING(msg, ...) \
c4::handle_warning(C4_SRCLOC(), msg, ## __VA_ARGS__)
#if defined(C4_ERROR_SHOWS_FILELINE) && defined(C4_ERROR_SHOWS_FUNC)
struct srcloc
{
const char *file = "";
const char *func = "";
int line = 0;
};
#define C4_SRCLOC() c4::srcloc{__FILE__, C4_PRETTY_FUNC, __LINE__}
#elif defined(C4_ERROR_SHOWS_FILELINE)
struct srcloc
{
const char *file;
int line;
};
#define C4_SRCLOC() c4::srcloc{__FILE__, __LINE__}
#elif ! defined(C4_ERROR_SHOWS_FUNC)
struct srcloc
{
};
#define C4_SRCLOC() c4::srcloc()
#else
# error not implemented
#endif
//-----------------------------------------------------------------------------
// assertions
// Doxygen needs this so that only one definition counts
#ifdef _DOXYGEN_
/** Explicitly enables assertions, independently of NDEBUG status.
* This is meant to allow enabling assertions even when NDEBUG is defined.
* Defaults to undefined.
* @ingroup error_checking */
# define C4_USE_ASSERT
/** assert that a condition is true; this is turned off when NDEBUG
* is defined and C4_USE_ASSERT is not true.
* @ingroup error_checking */
# define C4_ASSERT
/** same as C4_ASSERT(), additionally prints a printf-formatted message
* @ingroup error_checking */
# define C4_ASSERT_MSG
/** evaluates to C4_NOEXCEPT when C4_XASSERT is disabled; otherwise, defaults
* to noexcept
* @ingroup error_checking */
# define C4_NOEXCEPT_A
#endif // _DOXYGEN_
#ifndef C4_USE_ASSERT
# ifdef NDEBUG
# define C4_USE_ASSERT 0
# else
# define C4_USE_ASSERT 1
# endif
#endif
#if C4_USE_ASSERT
# define C4_ASSERT(cond) C4_CHECK(cond)
# define C4_ASSERT_MSG(cond, /*fmt, */...) C4_CHECK_MSG(cond, ## __VA_ARGS__)
# define C4_ASSERT_IF(predicate, cond) if(predicate) { C4_ASSERT(cond); }
# define C4_NOEXCEPT_A C4_NOEXCEPT
#else
# define C4_ASSERT(cond)
# define C4_ASSERT_MSG(cond, /*fmt, */...)
# define C4_ASSERT_IF(predicate, cond)
# define C4_NOEXCEPT_A noexcept
#endif
//-----------------------------------------------------------------------------
// extreme assertions
// Doxygen needs this so that only one definition counts
#ifdef _DOXYGEN_
/** Explicitly enables extreme assertions; this is meant to allow enabling
* assertions even when NDEBUG is defined. Defaults to undefined.
* @ingroup error_checking */
# define C4_USE_XASSERT
/** extreme assertion: can be switched off independently of
* the regular assertion; use for example for bounds checking in hot code.
* Turned on only when C4_USE_XASSERT is defined
* @ingroup error_checking */
# define C4_XASSERT
/** same as C4_XASSERT(), and additionally prints a printf-formatted message
* @ingroup error_checking */
# define C4_XASSERT_MSG
/** evaluates to C4_NOEXCEPT when C4_XASSERT is disabled; otherwise, defaults to noexcept
* @ingroup error_checking */
# define C4_NOEXCEPT_X
#endif // _DOXYGEN_
#ifndef C4_USE_XASSERT
# define C4_USE_XASSERT C4_USE_ASSERT
#endif
#if C4_USE_XASSERT
# define C4_XASSERT(cond) C4_CHECK(cond)
# define C4_XASSERT_MSG(cond, /*fmt, */...) C4_CHECK_MSG(cond, ## __VA_ARGS__)
# define C4_XASSERT_IF(predicate, cond) if(predicate) { C4_XASSERT(cond); }
# define C4_NOEXCEPT_X C4_NOEXCEPT
#else
# define C4_XASSERT(cond)
# define C4_XASSERT_MSG(cond, /*fmt, */...)
# define C4_XASSERT_IF(predicate, cond)
# define C4_NOEXCEPT_X noexcept
#endif
//-----------------------------------------------------------------------------
// checks: never switched-off
/** Check that a condition is true, or raise an error when not
* true. Unlike C4_ASSERT(), this check is not disabled in non-debug
* builds.
* @see C4_ASSERT
* @ingroup error_checking
*
* @todo add constexpr-compatible compile-time assert:
* https://akrzemi1.wordpress.com/2017/05/18/asserts-in-constexpr-functions/
*/
#define C4_CHECK(cond) \
do { \
if(C4_UNLIKELY(!(cond))) \
{ \
C4_ERROR("check failed: %s", #cond); \
} \
} while(0)
/** like C4_CHECK(), and additionally log a printf-style message.
* @see C4_CHECK
* @ingroup error_checking */
#define C4_CHECK_MSG(cond, fmt, ...) \
do { \
if(C4_UNLIKELY(!(cond))) \
{ \
C4_ERROR("check failed: " #cond "\n" fmt, ## __VA_ARGS__); \
} \
} while(0)
//-----------------------------------------------------------------------------
// Common error conditions
#define C4_NOT_IMPLEMENTED() C4_ERROR("NOT IMPLEMENTED")
#define C4_NOT_IMPLEMENTED_MSG(/*msg, */...) C4_ERROR("NOT IMPLEMENTED: " __VA_ARGS__)
#define C4_NOT_IMPLEMENTED_IF(condition) do { if(C4_UNLIKELY(condition)) { C4_ERROR("NOT IMPLEMENTED"); } } while(0)
#define C4_NOT_IMPLEMENTED_IF_MSG(condition, /*msg, */...) do { if(C4_UNLIKELY(condition)) { C4_ERROR("NOT IMPLEMENTED: " __VA_ARGS__); } } while(0)
#define C4_NEVER_REACH() do { C4_ERROR("never reach this point"); C4_UNREACHABLE(); } while(0)
#define C4_NEVER_REACH_MSG(/*msg, */...) do { C4_ERROR("never reach this point: " __VA_ARGS__); C4_UNREACHABLE(); } while(0)
//-----------------------------------------------------------------------------
// helpers for warning suppression
// idea adapted from https://github.com/onqtam/doctest/
// TODO: add C4_MESSAGE() https://stackoverflow.com/questions/18252351/custom-preprocessor-macro-for-a-conditional-pragma-message-xxx?rq=1
#ifdef C4_MSVC
#define C4_SUPPRESS_WARNING_MSVC_PUSH __pragma(warning(push))
#define C4_SUPPRESS_WARNING_MSVC(w) __pragma(warning(disable : w))
#define C4_SUPPRESS_WARNING_MSVC_POP __pragma(warning(pop))
#else // C4_MSVC
#define C4_SUPPRESS_WARNING_MSVC_PUSH
#define C4_SUPPRESS_WARNING_MSVC(w)
#define C4_SUPPRESS_WARNING_MSVC_POP
#endif // C4_MSVC
#ifdef C4_CLANG
#define C4_PRAGMA_TO_STR(x) _Pragma(#x)
#define C4_SUPPRESS_WARNING_CLANG_PUSH _Pragma("clang diagnostic push")
#define C4_SUPPRESS_WARNING_CLANG(w) C4_PRAGMA_TO_STR(clang diagnostic ignored w)
#define C4_SUPPRESS_WARNING_CLANG_POP _Pragma("clang diagnostic pop")
#else // C4_CLANG
#define C4_SUPPRESS_WARNING_CLANG_PUSH
#define C4_SUPPRESS_WARNING_CLANG(w)
#define C4_SUPPRESS_WARNING_CLANG_POP
#endif // C4_CLANG
#ifdef C4_GCC
#define C4_PRAGMA_TO_STR(x) _Pragma(#x)
#define C4_SUPPRESS_WARNING_GCC_PUSH _Pragma("GCC diagnostic push")
#define C4_SUPPRESS_WARNING_GCC(w) C4_PRAGMA_TO_STR(GCC diagnostic ignored w)
#define C4_SUPPRESS_WARNING_GCC_POP _Pragma("GCC diagnostic pop")
#else // C4_GCC
#define C4_SUPPRESS_WARNING_GCC_PUSH
#define C4_SUPPRESS_WARNING_GCC(w)
#define C4_SUPPRESS_WARNING_GCC_POP
#endif // C4_GCC
#define C4_SUPPRESS_WARNING_MSVC_WITH_PUSH(w) \
C4_SUPPRESS_WARNING_MSVC_PUSH \
C4_SUPPRESS_WARNING_MSVC(w)
#define C4_SUPPRESS_WARNING_CLANG_WITH_PUSH(w) \
C4_SUPPRESS_WARNING_CLANG_PUSH \
C4_SUPPRESS_WARNING_CLANG(w)
#define C4_SUPPRESS_WARNING_GCC_WITH_PUSH(w) \
C4_SUPPRESS_WARNING_GCC_PUSH \
C4_SUPPRESS_WARNING_GCC(w)
#define C4_SUPPRESS_WARNING_GCC_CLANG_PUSH \
C4_SUPPRESS_WARNING_GCC_PUSH \
C4_SUPPRESS_WARNING_CLANG_PUSH
#define C4_SUPPRESS_WARNING_GCC_CLANG(w) \
C4_SUPPRESS_WARNING_GCC(w) \
C4_SUPPRESS_WARNING_CLANG(w)
#define C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH(w) \
C4_SUPPRESS_WARNING_GCC_WITH_PUSH(w) \
C4_SUPPRESS_WARNING_CLANG_WITH_PUSH(w)
#define C4_SUPPRESS_WARNING_GCC_CLANG_POP \
C4_SUPPRESS_WARNING_GCC_POP \
C4_SUPPRESS_WARNING_CLANG_POP
} // namespace c4
#ifdef __clang__
# pragma clang diagnostic pop
#endif
#endif /* _C4_ERROR_HPP_ */

View File

@ -0,0 +1,18 @@
#ifndef C4_EXPORT_HPP_
#define C4_EXPORT_HPP_
#ifdef _WIN32
#ifdef C4CORE_SHARED
#ifdef C4CORE_EXPORTS
#define C4CORE_EXPORT __declspec(dllexport)
#else
#define C4CORE_EXPORT __declspec(dllimport)
#endif
#else
#define C4CORE_EXPORT
#endif
#else
#define C4CORE_EXPORT
#endif
#endif /* C4CORE_EXPORT_HPP_ */

View File

@ -0,0 +1,879 @@
#ifndef _C4_FORMAT_HPP_
#define _C4_FORMAT_HPP_
/** @file format.hpp provides type-safe facilities for formatting arguments
* to string buffers */
#include "c4/charconv.hpp"
#include "c4/blob.hpp"
#ifdef _MSC_VER
# pragma warning(push)
# if C4_MSVC_VERSION != C4_MSVC_VERSION_2017
# pragma warning(disable: 4800) // forcing value to bool 'true' or 'false' (performance warning)
# endif
# pragma warning(disable: 4996) // snprintf/scanf: this function or variable may be unsafe
#elif defined(__clang__)
# pragma clang diagnostic push
#elif defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wuseless-cast"
#endif
namespace c4 {
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// formatting truthy types as booleans
namespace fmt {
/** write a variable as an alphabetic boolean, ie as either true or false
* @param strict_read */
template<class T>
struct boolalpha_
{
boolalpha_(T val_, bool strict_read_=false) : val(val_ ? true : false), strict_read(strict_read_) {}
bool val;
bool strict_read;
};
template<class T>
boolalpha_<T> boolalpha(T const& val, bool strict_read=false)
{
return boolalpha_<T>(val, strict_read);
}
} // namespace fmt
/** write a variable as an alphabetic boolean, ie as either true or false */
template<class T>
inline size_t to_chars(substr buf, fmt::boolalpha_<T> fmt)
{
return to_chars(buf, fmt.val ? "true" : "false");
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// formatting integral types
namespace fmt {
/** format an integral type with a custom radix */
template<typename T>
struct integral_
{
T val;
T radix;
C4_ALWAYS_INLINE integral_(T val_, T radix_) : val(val_), radix(radix_) {}
};
/** format an integral type with a custom radix, and pad with zeroes on the left */
template<typename T>
struct integral_padded_
{
T val;
T radix;
size_t num_digits;
C4_ALWAYS_INLINE integral_padded_(T val_, T radix_, size_t nd) : val(val_), radix(radix_), num_digits(nd) {}
};
/** format an integral type with a custom radix */
template<class T>
C4_ALWAYS_INLINE integral_<T> integral(T val, T radix=10)
{
return integral_<T>(val, radix);
}
/** format an integral type with a custom radix */
template<class T>
C4_ALWAYS_INLINE integral_<intptr_t> integral(T const* val, T radix=10)
{
return integral_<intptr_t>(reinterpret_cast<intptr_t>(val), static_cast<intptr_t>(radix));
}
/** format an integral type with a custom radix */
template<class T>
C4_ALWAYS_INLINE integral_<intptr_t> integral(std::nullptr_t, T radix=10)
{
return integral_<intptr_t>(intptr_t(0), static_cast<intptr_t>(radix));
}
/** pad the argument with zeroes on the left, with decimal radix */
template<class T>
C4_ALWAYS_INLINE integral_padded_<T> zpad(T val, size_t num_digits)
{
return integral_padded_<T>(val, T(10), num_digits);
}
/** pad the argument with zeroes on the left */
template<class T>
C4_ALWAYS_INLINE integral_padded_<T> zpad(integral_<T> val, size_t num_digits)
{
return integral_padded_<T>(val.val, val.radix, num_digits);
}
/** pad the argument with zeroes on the left */
C4_ALWAYS_INLINE integral_padded_<intptr_t> zpad(std::nullptr_t, size_t num_digits)
{
return integral_padded_<intptr_t>(0, 16, num_digits);
}
/** pad the argument with zeroes on the left */
template<class T>
C4_ALWAYS_INLINE integral_padded_<intptr_t> zpad(T const* val, size_t num_digits)
{
return integral_padded_<intptr_t>(reinterpret_cast<intptr_t>(val), 16, num_digits);
}
template<class T>
C4_ALWAYS_INLINE integral_padded_<intptr_t> zpad(T * val, size_t num_digits)
{
return integral_padded_<intptr_t>(reinterpret_cast<intptr_t>(val), 16, num_digits);
}
/** format the pointer as an hexadecimal value */
template<class T>
inline integral_<intptr_t> hex(T * v)
{
return integral_<intptr_t>(reinterpret_cast<intptr_t>(v), intptr_t(16));
}
/** format the pointer as an hexadecimal value */
template<class T>
inline integral_<intptr_t> hex(T const* v)
{
return integral_<intptr_t>(reinterpret_cast<intptr_t>(v), intptr_t(16));
}
/** format null as an hexadecimal value
* @overload hex */
inline integral_<intptr_t> hex(std::nullptr_t)
{
return integral_<intptr_t>(0, intptr_t(16));
}
/** format the integral_ argument as an hexadecimal value
* @overload hex */
template<class T>
inline integral_<T> hex(T v)
{
return integral_<T>(v, T(16));
}
/** format the pointer as an octal value */
template<class T>
inline integral_<intptr_t> oct(T const* v)
{
return integral_<intptr_t>(reinterpret_cast<intptr_t>(v), intptr_t(8));
}
/** format the pointer as an octal value */
template<class T>
inline integral_<intptr_t> oct(T * v)
{
return integral_<intptr_t>(reinterpret_cast<intptr_t>(v), intptr_t(8));
}
/** format null as an octal value */
inline integral_<intptr_t> oct(std::nullptr_t)
{
return integral_<intptr_t>(intptr_t(0), intptr_t(8));
}
/** format the integral_ argument as an octal value */
template<class T>
inline integral_<T> oct(T v)
{
return integral_<T>(v, T(8));
}
/** format the pointer as a binary 0-1 value
* @see c4::raw() if you want to use a binary memcpy instead of 0-1 formatting */
template<class T>
inline integral_<intptr_t> bin(T const* v)
{
return integral_<intptr_t>(reinterpret_cast<intptr_t>(v), intptr_t(2));
}
/** format the pointer as a binary 0-1 value
* @see c4::raw() if you want to use a binary memcpy instead of 0-1 formatting */
template<class T>
inline integral_<intptr_t> bin(T * v)
{
return integral_<intptr_t>(reinterpret_cast<intptr_t>(v), intptr_t(2));
}
/** format null as a binary 0-1 value
* @see c4::raw() if you want to use a binary memcpy instead of 0-1 formatting */
inline integral_<intptr_t> bin(std::nullptr_t)
{
return integral_<intptr_t>(intptr_t(0), intptr_t(2));
}
/** format the integral_ argument as a binary 0-1 value
* @see c4::raw() if you want to use a raw memcpy-based binary dump instead of 0-1 formatting */
template<class T>
inline integral_<T> bin(T v)
{
return integral_<T>(v, T(2));
}
template<class T>
struct overflow_checked_
{
static_assert(std::is_integral<T>::value, "range checking only for integral types");
C4_ALWAYS_INLINE overflow_checked_(T &val_) : val(&val_) {}
T *val;
};
template<class T>
C4_ALWAYS_INLINE overflow_checked_<T> overflow_checked(T &val)
{
return overflow_checked_<T>(val);
}
} // namespace fmt
/** format an integral_ signed type */
template<typename T>
C4_ALWAYS_INLINE
typename std::enable_if<std::is_signed<T>::value, size_t>::type
to_chars(substr buf, fmt::integral_<T> fmt)
{
return itoa(buf, fmt.val, fmt.radix);
}
/** format an integral_ signed type, pad with zeroes */
template<typename T>
C4_ALWAYS_INLINE
typename std::enable_if<std::is_signed<T>::value, size_t>::type
to_chars(substr buf, fmt::integral_padded_<T> fmt)
{
return itoa(buf, fmt.val, fmt.radix, fmt.num_digits);
}
/** format an integral_ unsigned type */
template<typename T>
C4_ALWAYS_INLINE
typename std::enable_if<std::is_unsigned<T>::value, size_t>::type
to_chars(substr buf, fmt::integral_<T> fmt)
{
return utoa(buf, fmt.val, fmt.radix);
}
/** format an integral_ unsigned type, pad with zeroes */
template<typename T>
C4_ALWAYS_INLINE
typename std::enable_if<std::is_unsigned<T>::value, size_t>::type
to_chars(substr buf, fmt::integral_padded_<T> fmt)
{
return utoa(buf, fmt.val, fmt.radix, fmt.num_digits);
}
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;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// formatting real types
namespace fmt {
template<class T>
struct real_
{
T val;
int precision;
RealFormat_e fmt;
real_(T v, int prec=-1, RealFormat_e f=FTOA_FLOAT) : val(v), precision(prec), fmt(f) {}
};
template<class T>
real_<T> real(T val, int precision, RealFormat_e fmt=FTOA_FLOAT)
{
return real_<T>(val, precision, fmt);
}
} // namespace fmt
inline size_t to_chars(substr buf, fmt::real_< float> fmt) { return ftoa(buf, fmt.val, fmt.precision, fmt.fmt); }
inline size_t to_chars(substr buf, fmt::real_<double> fmt) { return dtoa(buf, fmt.val, fmt.precision, fmt.fmt); }
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// writing raw binary data
namespace fmt {
/** @see blob_ */
template<class T>
struct raw_wrapper_ : public blob_<T>
{
size_t alignment;
C4_ALWAYS_INLINE raw_wrapper_(blob_<T> data, size_t alignment_) noexcept
:
blob_<T>(data),
alignment(alignment_)
{
C4_ASSERT_MSG(alignment > 0 && (alignment & (alignment - 1)) == 0, "alignment must be a power of two");
}
};
using const_raw_wrapper = raw_wrapper_<cbyte>;
using raw_wrapper = raw_wrapper_<byte>;
/** mark a variable to be written in raw binary format, using memcpy
* @see blob_ */
inline const_raw_wrapper craw(cblob data, size_t alignment=alignof(max_align_t))
{
return const_raw_wrapper(data, alignment);
}
/** mark a variable to be written in raw binary format, using memcpy
* @see blob_ */
inline const_raw_wrapper raw(cblob data, size_t alignment=alignof(max_align_t))
{
return const_raw_wrapper(data, alignment);
}
/** mark a variable to be written in raw binary format, using memcpy
* @see blob_ */
template<class T>
inline const_raw_wrapper craw(T const& C4_RESTRICT data, size_t alignment=alignof(T))
{
return const_raw_wrapper(cblob(data), alignment);
}
/** mark a variable to be written in raw binary format, using memcpy
* @see blob_ */
template<class T>
inline const_raw_wrapper raw(T const& C4_RESTRICT data, size_t alignment=alignof(T))
{
return const_raw_wrapper(cblob(data), alignment);
}
/** mark a variable to be read in raw binary format, using memcpy */
inline raw_wrapper raw(blob data, size_t alignment=alignof(max_align_t))
{
return raw_wrapper(data, alignment);
}
/** mark a variable to be read in raw binary format, using memcpy */
template<class T>
inline raw_wrapper raw(T & C4_RESTRICT data, size_t alignment=alignof(T))
{
return raw_wrapper(blob(data), alignment);
}
} // namespace fmt
/** write a variable in raw binary format, using memcpy */
C4CORE_EXPORT size_t to_chars(substr buf, fmt::const_raw_wrapper r);
/** read a variable in raw binary format, using memcpy */
C4CORE_EXPORT bool from_chars(csubstr buf, fmt::raw_wrapper *r);
/** read a variable in raw binary format, using memcpy */
inline bool from_chars(csubstr buf, fmt::raw_wrapper r)
{
return from_chars(buf, &r);
}
/** read a variable in raw binary format, using memcpy */
inline size_t from_chars_first(csubstr buf, fmt::raw_wrapper *r)
{
return from_chars(buf, r);
}
/** read a variable in raw binary format, using memcpy */
inline size_t from_chars_first(csubstr buf, fmt::raw_wrapper r)
{
return from_chars(buf, &r);
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// formatting aligned to left/right
namespace fmt {
template<class T>
struct left_
{
T val;
size_t width;
char pad;
left_(T v, size_t w, char p) : val(v), width(w), pad(p) {}
};
template<class T>
struct right_
{
T val;
size_t width;
char pad;
right_(T v, size_t w, char p) : val(v), width(w), pad(p) {}
};
/** mark an argument to be aligned left */
template<class T>
left_<T> left(T val, size_t width, char padchar=' ')
{
return left_<T>(val, width, padchar);
}
/** mark an argument to be aligned right */
template<class T>
right_<T> right(T val, size_t width, char padchar=' ')
{
return right_<T>(val, width, padchar);
}
} // namespace fmt
template<class T>
size_t to_chars(substr buf, fmt::left_<T> const& C4_RESTRICT align)
{
size_t ret = to_chars(buf, align.val);
if(ret >= buf.len || ret >= align.width)
return ret > align.width ? ret : align.width;
buf.first(align.width).sub(ret).fill(align.pad);
to_chars(buf, align.val);
return align.width;
}
template<class T>
size_t to_chars(substr buf, fmt::right_<T> const& C4_RESTRICT align)
{
size_t ret = to_chars(buf, align.val);
if(ret >= buf.len || ret >= align.width)
return ret > align.width ? ret : align.width;
size_t rem = static_cast<size_t>(align.width - ret);
buf.first(rem).fill(align.pad);
to_chars(buf.sub(rem), align.val);
return align.width;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/// @cond dev
// terminates the variadic recursion
inline size_t cat(substr /*buf*/)
{
return 0;
}
/// @endcond
/** serialize the arguments, concatenating them to the given fixed-size buffer.
* The buffer size is strictly respected: no writes will occur beyond its end.
* @return the number of characters needed to write all the arguments into the buffer.
* @see c4::catrs() if instead of a fixed-size buffer, a resizeable container is desired
* @see c4::uncat() for the inverse function
* @see c4::catsep() if a separator between each argument is to be used
* @see c4::format() if a format string is desired */
template<class Arg, class... Args>
size_t cat(substr buf, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
size_t num = to_chars(buf, a);
buf = buf.len >= num ? buf.sub(num) : substr{};
num += cat(buf, more...);
return num;
}
/** like c4::cat() but return a substr instead of a size */
template<class... Args>
substr cat_sub(substr buf, Args && ...args)
{
size_t sz = cat(buf, std::forward<Args>(args)...);
C4_CHECK(sz <= buf.len);
return {buf.str, sz <= buf.len ? sz : buf.len};
}
//-----------------------------------------------------------------------------
/// @cond dev
// terminates the variadic recursion
inline size_t uncat(csubstr /*buf*/)
{
return 0;
}
/// @endcond
/** deserialize the arguments from the given buffer.
*
* @return the number of characters read from the buffer, or csubstr::npos
* if a conversion was not successful.
* @see c4::cat(). c4::uncat() is the inverse of c4::cat(). */
template<class Arg, class... Args>
size_t uncat(csubstr buf, Arg & C4_RESTRICT a, Args & C4_RESTRICT ...more)
{
size_t out = from_chars_first(buf, &a);
if(C4_UNLIKELY(out == csubstr::npos))
return csubstr::npos;
buf = buf.len >= out ? buf.sub(out) : substr{};
size_t num = uncat(buf, more...);
if(C4_UNLIKELY(num == csubstr::npos))
return csubstr::npos;
return out + num;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
namespace detail {
template<class Sep>
C4_ALWAYS_INLINE size_t catsep_more(substr /*buf*/, Sep const& C4_RESTRICT /*sep*/)
{
return 0;
}
template<class Sep, class Arg, class... Args>
size_t catsep_more(substr buf, Sep const& C4_RESTRICT sep, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
size_t ret = to_chars(buf, sep);
size_t num = ret;
buf = buf.len >= ret ? buf.sub(ret) : substr{};
ret = to_chars(buf, a);
num += ret;
buf = buf.len >= ret ? buf.sub(ret) : substr{};
ret = catsep_more(buf, sep, more...);
num += ret;
return num;
}
template<class Sep>
inline size_t uncatsep_more(csubstr /*buf*/, Sep & /*sep*/)
{
return 0;
}
template<class Sep, class Arg, class... Args>
size_t uncatsep_more(csubstr buf, Sep & C4_RESTRICT sep, Arg & C4_RESTRICT a, Args & C4_RESTRICT ...more)
{
size_t ret = from_chars_first(buf, &sep);
size_t num = ret;
if(C4_UNLIKELY(ret == csubstr::npos))
return csubstr::npos;
buf = buf.len >= ret ? buf.sub(ret) : substr{};
ret = from_chars_first(buf, &a);
if(C4_UNLIKELY(ret == csubstr::npos))
return csubstr::npos;
num += ret;
buf = buf.len >= ret ? buf.sub(ret) : substr{};
ret = uncatsep_more(buf, sep, more...);
if(C4_UNLIKELY(ret == csubstr::npos))
return csubstr::npos;
num += ret;
return num;
}
} // namespace detail
/// @cond dev
template<class Sep>
size_t catsep(substr /*buf*/, Sep const& C4_RESTRICT /*sep*/)
{
return 0;
}
/// @endcond
/** serialize the arguments, concatenating them to the given fixed-size
* buffer, using a separator between each argument.
* The buffer size is strictly respected: no writes will occur beyond its end.
* @return the number of characters needed to write all the arguments into the buffer.
* @see c4::catseprs() if instead of a fixed-size buffer, a resizeable container is desired
* @see c4::uncatsep() for the inverse function (ie, reading instead of writing)
* @see c4::cat() if no separator is needed
* @see c4::format() if a format string is desired */
template<class Sep, class Arg, class... Args>
size_t catsep(substr buf, Sep const& C4_RESTRICT sep, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
size_t num = to_chars(buf, a);
buf = buf.len >= num ? buf.sub(num) : substr{};
num += detail::catsep_more(buf, sep, more...);
return num;
}
/** like c4::catsep() but return a substr instead of a size
* @see c4::catsep(). c4::uncatsep() is the inverse of c4::catsep(). */
template<class... Args>
substr catsep_sub(substr buf, Args && ...args)
{
size_t sz = catsep(buf, std::forward<Args>(args)...);
C4_CHECK(sz <= buf.len);
return {buf.str, sz <= buf.len ? sz : buf.len};
}
/** deserialize the arguments from the given buffer, using a separator.
*
* @return the number of characters read from the buffer, or csubstr::npos
* if a conversion was not successful
* @see c4::catsep(). c4::uncatsep() is the inverse of c4::catsep(). */
template<class Sep, class Arg, class... Args>
size_t uncatsep(csubstr buf, Sep & C4_RESTRICT sep, Arg & C4_RESTRICT a, Args & C4_RESTRICT ...more)
{
size_t ret = from_chars_first(buf, &a), num = ret;
if(C4_UNLIKELY(ret == csubstr::npos))
return csubstr::npos;
buf = buf.len >= ret ? buf.sub(ret) : substr{};
ret = detail::uncatsep_more(buf, sep, more...);
if(C4_UNLIKELY(ret == csubstr::npos))
return csubstr::npos;
num += ret;
return num;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/// @cond dev
// terminates the variadic recursion
inline size_t format(substr buf, csubstr fmt)
{
return to_chars(buf, fmt);
}
/// @endcond
/** using a format string, serialize the arguments into the given
* fixed-size buffer.
* The buffer size is strictly respected: no writes will occur beyond its end.
* In the format string, each argument is marked with a compact
* curly-bracket pair: {}. Arguments beyond the last curly bracket pair
* are silently ignored. For example:
* @code{.cpp}
* c4::format(buf, "the {} drank {} {}", "partier", 5, "beers"); // the partier drank 5 beers
* c4::format(buf, "the {} drank {} {}", "programmer", 6, "coffees"); // the programmer drank 6 coffees
* @endcode
* @return the number of characters needed to write into the buffer.
* @see c4::formatrs() if instead of a fixed-size buffer, a resizeable container is desired
* @see c4::unformat() for the inverse function
* @see c4::cat() if no format or separator is needed
* @see c4::catsep() if no format is needed, but a separator must be used */
template<class Arg, class... Args>
size_t format(substr buf, csubstr fmt, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)
{
size_t pos = fmt.find("{}"); // @todo use _find_fmt()
if(C4_UNLIKELY(pos == csubstr::npos))
return to_chars(buf, fmt);
size_t num = to_chars(buf, fmt.sub(0, pos));
size_t out = num;
buf = buf.len >= num ? buf.sub(num) : substr{};
num = to_chars(buf, a);
out += num;
buf = buf.len >= num ? buf.sub(num) : substr{};
num = format(buf, fmt.sub(pos + 2), more...);
out += num;
return out;
}
/** like c4::format() but return a substr instead of a size
* @see c4::format()
* @see c4::catsep(). uncatsep() is the inverse of catsep(). */
template<class... Args>
substr format_sub(substr buf, csubstr fmt, Args const& C4_RESTRICT ...args)
{
size_t sz = c4::format(buf, fmt, args...);
C4_CHECK(sz <= buf.len);
return {buf.str, sz <= buf.len ? sz : buf.len};
}
//-----------------------------------------------------------------------------
/// @cond dev
// terminates the variadic recursion
inline size_t unformat(csubstr /*buf*/, csubstr fmt)
{
return fmt.len;
}
/// @endcond
/** using a format string, deserialize the arguments from the given
* buffer.
* @return the number of characters read from the buffer, or npos if a conversion failed.
* @see c4::format(). c4::unformat() is the inverse function to format(). */
template<class Arg, class... Args>
size_t unformat(csubstr buf, csubstr fmt, Arg & C4_RESTRICT a, Args & C4_RESTRICT ...more)
{
const size_t pos = fmt.find("{}");
if(C4_UNLIKELY(pos == csubstr::npos))
return unformat(buf, fmt);
size_t num = pos;
size_t out = num;
buf = buf.len >= num ? buf.sub(num) : substr{};
num = from_chars_first(buf, &a);
if(C4_UNLIKELY(num == csubstr::npos))
return csubstr::npos;
out += num;
buf = buf.len >= num ? buf.sub(num) : substr{};
num = unformat(buf, fmt.sub(pos + 2), more...);
if(C4_UNLIKELY(num == csubstr::npos))
return csubstr::npos;
out += num;
return out;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** like c4::cat(), but receives a container, and resizes it as needed to contain
* the result. The container is overwritten. To append to it, use the append
* overload.
* @see c4::cat() */
template<class CharOwningContainer, class... Args>
inline void catrs(CharOwningContainer * C4_RESTRICT cont, Args const& C4_RESTRICT ...args)
{
retry:
substr buf = to_substr(*cont);
size_t ret = cat(buf, args...);
cont->resize(ret);
if(ret > buf.len)
goto retry;
}
/** like c4::cat(), but creates and returns a new container sized as needed to contain
* the result.
* @see c4::cat() */
template<class CharOwningContainer, class... Args>
inline CharOwningContainer catrs(Args const& C4_RESTRICT ...args)
{
CharOwningContainer cont;
catrs(&cont, args...);
return cont;
}
/** like c4::cat(), but receives a container, and appends to it instead of
* overwriting it. The container is resized as needed to contain the result.
* @return the region newly appended to the original container
* @see c4::cat()
* @see c4::catrs() */
template<class CharOwningContainer, class... Args>
inline csubstr catrs_append(CharOwningContainer * C4_RESTRICT cont, Args const& C4_RESTRICT ...args)
{
const size_t pos = cont->size();
retry:
substr buf = to_substr(*cont).sub(pos);
size_t ret = cat(buf, args...);
cont->resize(pos + ret);
if(ret > buf.len)
goto retry;
return to_csubstr(*cont).range(pos, cont->size());
}
//-----------------------------------------------------------------------------
/** like c4::catsep(), but receives a container, and resizes it as needed to contain the result.
* The container is overwritten. To append to the container use the append overload.
* @see c4::catsep() */
template<class CharOwningContainer, class Sep, class... Args>
inline void catseprs(CharOwningContainer * C4_RESTRICT cont, Sep const& C4_RESTRICT sep, Args const& C4_RESTRICT ...args)
{
retry:
substr buf = to_substr(*cont);
size_t ret = catsep(buf, sep, args...);
cont->resize(ret);
if(ret > buf.len)
goto retry;
}
/** like c4::catsep(), but create a new container with the result.
* @return the requested container */
template<class CharOwningContainer, class Sep, class... Args>
inline CharOwningContainer catseprs(Sep const& C4_RESTRICT sep, Args const& C4_RESTRICT ...args)
{
CharOwningContainer cont;
catseprs(&cont, sep, args...);
return cont;
}
/** like catsep(), but receives a container, and appends the arguments, resizing the
* container as needed to contain the result. The buffer is appended to.
* @return a csubstr of the appended part
* @ingroup formatting_functions */
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)
{
const size_t pos = cont->size();
retry:
substr buf = to_substr(*cont).sub(pos);
size_t ret = catsep(buf, sep, args...);
cont->resize(pos + ret);
if(ret > buf.len)
goto retry;
return to_csubstr(*cont).range(pos, cont->size());
}
//-----------------------------------------------------------------------------
/** like c4::format(), but receives a container, and resizes it as needed
* to contain the result. The container is overwritten. To append to
* the container use the append overload.
* @see c4::format() */
template<class CharOwningContainer, class... Args>
inline void formatrs(CharOwningContainer * C4_RESTRICT cont, csubstr fmt, Args const& C4_RESTRICT ...args)
{
retry:
substr buf = to_substr(*cont);
size_t ret = format(buf, fmt, args...);
cont->resize(ret);
if(ret > buf.len)
goto retry;
}
/** like c4::format(), but create a new container with the result.
* @return the requested container */
template<class CharOwningContainer, class... Args>
inline CharOwningContainer formatrs(csubstr fmt, Args const& C4_RESTRICT ...args)
{
CharOwningContainer cont;
formatrs(&cont, fmt, args...);
return cont;
}
/** like format(), but receives a container, and appends the
* 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 */
template<class CharOwningContainer, class... Args>
inline csubstr formatrs_append(CharOwningContainer * C4_RESTRICT cont, csubstr fmt, Args const& C4_RESTRICT ...args)
{
const size_t pos = cont->size();
retry:
substr buf = to_substr(*cont).sub(pos);
size_t ret = format(buf, fmt, args...);
cont->resize(pos + ret);
if(ret > buf.len)
goto retry;
return to_csubstr(*cont).range(pos, cont->size());
}
} // namespace c4
#ifdef _MSC_VER
# pragma warning(pop)
#elif defined(__clang__)
# pragma clang diagnostic pop
#elif defined(__GNUC__)
# pragma GCC diagnostic pop
#endif
#endif /* _C4_FORMAT_HPP_ */

View File

@ -0,0 +1,306 @@
#ifndef _C4_LANGUAGE_HPP_
#define _C4_LANGUAGE_HPP_
/** @file language.hpp Provides language standard information macros and
* compiler agnostic utility macros: namespace facilities, function attributes,
* variable attributes, etc.
* @ingroup basic_headers */
#include "c4/preprocessor.hpp"
#include "c4/compiler.hpp"
/* Detect C++ standard.
* @see http://stackoverflow.com/a/7132549/5875572 */
#ifndef C4_CPP
# if defined(_MSC_VER) && !defined(__clang__)
# if _MSC_VER >= 1910 // >VS2015: VS2017, VS2019
# if (!defined(_MSVC_LANG))
# error _MSVC not defined
# endif
# if _MSVC_LANG >= 201705L
# define C4_CPP 20
# define C4_CPP20
# elif _MSVC_LANG == 201703L
# define C4_CPP 17
# define C4_CPP17
# elif _MSVC_LANG >= 201402L
# define C4_CPP 14
# define C4_CPP14
# elif _MSVC_LANG >= 201103L
# define C4_CPP 11
# define C4_CPP11
# else
# error C++ lesser than C++11 not supported
# endif
# else
# if _MSC_VER == 1900
# define C4_CPP 14 // VS2015 is c++14 https://devblogs.microsoft.com/cppblog/c111417-features-in-vs-2015-rtm/
# define C4_CPP14
# elif _MSC_VER == 1800 // VS2013
# define C4_CPP 11
# define C4_CPP11
# else
# error C++ lesser than C++11 not supported
# endif
# endif
# elif defined(__INTEL_COMPILER) // https://software.intel.com/en-us/node/524490
# ifdef __INTEL_CXX20_MODE__ // not sure about this
# define C4_CPP 20
# define C4_CPP20
# elif defined __INTEL_CXX17_MODE__ // not sure about this
# define C4_CPP 17
# define C4_CPP17
# elif defined __INTEL_CXX14_MODE__ // not sure about this
# define C4_CPP 14
# define C4_CPP14
# elif defined __INTEL_CXX11_MODE__
# define C4_CPP 11
# define C4_CPP11
# else
# error C++ lesser than C++11 not supported
# endif
# else
# ifndef __cplusplus
# error __cplusplus is not defined?
# endif
# if __cplusplus == 1
# error cannot handle __cplusplus==1
# elif __cplusplus >= 201709L
# define C4_CPP 20
# define C4_CPP20
# elif __cplusplus >= 201703L
# define C4_CPP 17
# define C4_CPP17
# elif __cplusplus >= 201402L
# define C4_CPP 14
# define C4_CPP14
# elif __cplusplus >= 201103L
# define C4_CPP 11
# define C4_CPP11
# elif __cplusplus >= 199711L
# error C++ lesser than C++11 not supported
# endif
# endif
#else
# ifdef C4_CPP == 20
# define C4_CPP20
# elif C4_CPP == 17
# define C4_CPP17
# elif C4_CPP == 14
# define C4_CPP14
# elif C4_CPP == 11
# define C4_CPP11
# elif C4_CPP == 98
# define C4_CPP98
# error C++ lesser than C++11 not supported
# else
# error C4_CPP must be one of 20, 17, 14, 11, 98
# endif
#endif
#ifdef C4_CPP20
# define C4_CPP17
# define C4_CPP14
# define C4_CPP11
#elif defined(C4_CPP17)
# define C4_CPP14
# define C4_CPP11
#elif defined(C4_CPP14)
# define C4_CPP11
#endif
/** lifted from this answer: http://stackoverflow.com/a/20170989/5875572 */
#if defined(_MSC_VER) && !defined(__clang__)
# if _MSC_VER < 1900
# define C4_CONSTEXPR11
# define C4_CONSTEXPR14
# elif _MSC_VER < 2000
# define C4_CONSTEXPR11 constexpr
# define C4_CONSTEXPR14
# else
# define C4_CONSTEXPR11 constexpr
# define C4_CONSTEXPR14 constexpr
# endif
#else
# if __cplusplus < 201103
# define C4_CONSTEXPR11
# define C4_CONSTEXPR14
# elif __cplusplus == 201103
# define C4_CONSTEXPR11 constexpr
# define C4_CONSTEXPR14
# else
# define C4_CONSTEXPR11 constexpr
# define C4_CONSTEXPR14 constexpr
# endif
#endif // _MSC_VER
#if C4_CPP < 17
#define C4_IF_CONSTEXPR
#define C4_INLINE_CONSTEXPR constexpr
#else
#define C4_IF_CONSTEXPR constexpr
#define C4_INLINE_CONSTEXPR inline constexpr
#endif
#if defined(_MSC_VER) && !defined(__clang__)
# if (defined(_CPPUNWIND) && (_CPPUNWIND == 1))
# define C4_EXCEPTIONS
# endif
#else
# if defined(__EXCEPTIONS) || defined(__cpp_exceptions)
# define C4_EXCEPTIONS
# endif
#endif
#ifdef C4_EXCEPTIONS
# define C4_IF_EXCEPTIONS_(exc_code, setjmp_code) exc_code
# define C4_IF_EXCEPTIONS(exc_code, setjmp_code) do { exc_code } while(0)
#else
# define C4_IF_EXCEPTIONS_(exc_code, setjmp_code) setjmp_code
# define C4_IF_EXCEPTIONS(exc_code, setjmp_code) do { setjmp_code } while(0)
#endif
#if defined(_MSC_VER) && !defined(__clang__)
# if defined(_CPPRTTI)
# define C4_RTTI
# endif
#else
# if defined(__GXX_RTTI)
# define C4_RTTI
# endif
#endif
#ifdef C4_RTTI
# define C4_IF_RTTI_(code_rtti, code_no_rtti) code_rtti
# define C4_IF_RTTI(code_rtti, code_no_rtti) do { code_rtti } while(0)
#else
# define C4_IF_RTTI_(code_rtti, code_no_rtti) code_no_rtti
# define C4_IF_RTTI(code_rtti, code_no_rtti) do { code_no_rtti } while(0)
#endif
//------------------------------------------------------------
#define _C4_BEGIN_NAMESPACE(ns) namespace ns {
#define _C4_END_NAMESPACE(ns) }
// MSVC cant handle the C4_FOR_EACH macro... need to fix this
//#define C4_BEGIN_NAMESPACE(...) C4_FOR_EACH_SEP(_C4_BEGIN_NAMESPACE, , __VA_ARGS__)
//#define C4_END_NAMESPACE(...) C4_FOR_EACH_SEP(_C4_END_NAMESPACE, , __VA_ARGS__)
#define C4_BEGIN_NAMESPACE(ns) namespace ns {
#define C4_END_NAMESPACE(ns) }
#define C4_BEGIN_HIDDEN_NAMESPACE namespace /*hidden*/ {
#define C4_END_HIDDEN_NAMESPACE } /* namespace hidden */
//------------------------------------------------------------
#ifndef C4_API
# if defined(_MSC_VER) && !defined(__clang__)
# if defined(C4_EXPORT)
# define C4_API __declspec(dllexport)
# elif defined(C4_IMPORT)
# define C4_API __declspec(dllimport)
# else
# define C4_API
# endif
# else
# define C4_API
# endif
#endif
#if defined(_MSC_VER) && !defined(__clang__)
# define C4_RESTRICT __restrict
# define C4_RESTRICT_FN __declspec(restrict)
# define C4_NO_INLINE __declspec(noinline)
# define C4_ALWAYS_INLINE inline __forceinline
/** these are not available in VS AFAIK */
# define C4_CONST
# define C4_PURE
# define C4_FLATTEN
# define C4_HOT /** @todo */
# define C4_COLD /** @todo */
# define C4_EXPECT(x, y) x /** @todo */
# define C4_LIKELY(x) x /** @todo */
# define C4_UNLIKELY(x) x /** @todo */
# define C4_UNREACHABLE() /** @todo */
# define C4_ATTR_FORMAT(...) /** */
# define C4_NORETURN /** @todo */
#else
///< @todo assuming gcc-like compiler. check it is actually so.
/** for function attributes in GCC,
* @see https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#Common-Function-Attributes */
/** for __builtin functions in GCC,
* @see https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html */
# define C4_RESTRICT __restrict__
# define C4_RESTRICT_FN __attribute__((restrict))
# define C4_NO_INLINE __attribute__((noinline))
# define C4_ALWAYS_INLINE inline __attribute__((always_inline))
# define C4_CONST __attribute__((const))
# define C4_PURE __attribute__((pure))
/** force inlining of every callee function */
# define C4_FLATTEN __atribute__((flatten))
/** mark a function as hot, ie as having a visible impact in CPU time
* thus making it more likely to inline, etc
* @see http://stackoverflow.com/questions/15028990/semantics-of-gcc-hot-attribute */
# define C4_HOT __attribute__((hot))
/** mark a function as cold, ie as NOT having a visible impact in CPU time
* @see http://stackoverflow.com/questions/15028990/semantics-of-gcc-hot-attribute */
# define C4_COLD __attribute__((cold))
# define C4_EXPECT(x, y) __builtin_expect(x, y) ///< @see https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html
# define C4_LIKELY(x) __builtin_expect(x, 1)
# define C4_UNLIKELY(x) __builtin_expect(x, 0)
# define C4_UNREACHABLE() __builtin_unreachable()
# define C4_ATTR_FORMAT(...) //__attribute__((format (__VA_ARGS__))) ///< @see https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#Common-Function-Attributes
# define C4_NORETURN __attribute__((noreturn))
#endif
#ifdef _MSC_VER
# define C4_FUNC __FUNCTION__
# define C4_PRETTY_FUNC __FUNCSIG__
#else /// @todo assuming gcc-like compiler. check it is actually so.
# define C4_FUNC __FUNCTION__
# define C4_PRETTY_FUNC __PRETTY_FUNCTION__
#endif
/** prevent compiler warnings about a specific var being unused */
#define C4_UNUSED(var) (void)var
#if C4_CPP >= 17
#define C4_STATIC_ASSERT(cond) static_assert(cond)
#else
#define C4_STATIC_ASSERT(cond) static_assert((cond), #cond)
#endif
#define C4_STATIC_ASSERT_MSG(cond, msg) static_assert((cond), #cond ": " msg)
/** @def C4_DONT_OPTIMIZE idea lifted from GoogleBenchmark.
* @see https://github.com/google/benchmark/blob/master/include/benchmark/benchmark_api.h */
namespace c4 {
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"); }
#else
# define C4_DONT_OPTIMIZE(var) c4::detail::use_char_pointer(reinterpret_cast< const char* >(&var))
void use_char_pointer(char const volatile*);
#endif
} // namespace detail
} // namespace c4
/** @def C4_KEEP_EMPTY_LOOP prevent an empty loop from being optimized out.
* @see http://stackoverflow.com/a/7084193/5875572 */
#if defined(_MSC_VER) && !defined(__clang__)
# define C4_KEEP_EMPTY_LOOP { char c; C4_DONT_OPTIMIZE(c); }
#else
# define C4_KEEP_EMPTY_LOOP { asm(""); }
#endif
/** @def C4_VA_LIST_REUSE_MUST_COPY
* @todo <jpmag> I strongly suspect that this is actually only in UNIX platforms. revisit this. */
#ifdef __GNUC__
# define C4_VA_LIST_REUSE_MUST_COPY
#endif
#endif /* _C4_LANGUAGE_HPP_ */

View File

@ -0,0 +1,778 @@
#ifndef _C4_MEMORY_UTIL_HPP_
#define _C4_MEMORY_UTIL_HPP_
#include "c4/config.hpp"
#include "c4/error.hpp"
#include "c4/compiler.hpp"
#include "c4/cpu.hpp"
#ifdef C4_MSVC
#include <intrin.h>
#endif
#include <string.h>
#if (defined(__GNUC__) && __GNUC__ >= 10) || defined(__has_builtin)
#define _C4_USE_LSB_INTRINSIC(which) __has_builtin(which)
#define _C4_USE_MSB_INTRINSIC(which) __has_builtin(which)
#elif defined(C4_MSVC)
#define _C4_USE_LSB_INTRINSIC(which) true
#define _C4_USE_MSB_INTRINSIC(which) true
#else
// let's try our luck
#define _C4_USE_LSB_INTRINSIC(which) true
#define _C4_USE_MSB_INTRINSIC(which) true
#endif
/** @file memory_util.hpp Some memory utilities. */
namespace c4 {
C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wold-style-cast")
/** set the given memory to zero */
C4_ALWAYS_INLINE void mem_zero(void* mem, size_t num_bytes)
{
memset(mem, 0, num_bytes);
}
/** set the given memory to zero */
template<class T>
C4_ALWAYS_INLINE void mem_zero(T* mem, size_t num_elms)
{
memset(mem, 0, sizeof(T) * num_elms);
}
/** set the given memory to zero */
template<class T>
C4_ALWAYS_INLINE void mem_zero(T* mem)
{
memset(mem, 0, sizeof(T));
}
C4_ALWAYS_INLINE C4_CONST bool mem_overlaps(void const* a, void const* b, size_t sza, size_t szb)
{
// thanks @timwynants
return (((const char*)b + szb) > a && b < ((const char*)a+sza));
}
void mem_repeat(void* dest, void const* pattern, size_t pattern_size, size_t num_times);
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
template<class T>
C4_ALWAYS_INLINE C4_CONST bool is_aligned(T *ptr, uintptr_t alignment=alignof(T))
{
return (uintptr_t(ptr) & (alignment - uintptr_t(1))) == uintptr_t(0);
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// least significant bit
/** @name msb Compute the least significant bit
* @note the input value must be nonzero
* @note the input type must be unsigned
*/
/** @{ */
// https://graphics.stanford.edu/~seander/bithacks.html#ZerosOnRightLinear
#define _c4_lsb_fallback \
unsigned c = 0; \
v = (v ^ (v - 1)) >> 1; /* Set v's trailing 0s to 1s and zero rest */ \
for(; v; ++c) \
v >>= 1; \
return (unsigned) c
// u8
template<class I>
C4_CONSTEXPR14
auto lsb(I v) noexcept
-> typename std::enable_if<sizeof(I) == 1u, unsigned>::type
{
C4_STATIC_ASSERT(std::is_unsigned<I>::value);
C4_ASSERT(v != 0);
#if _C4_USE_LSB_INTRINSIC(__builtin_ctz)
// upcast to use the intrinsic, it's cheaper.
#ifdef C4_MSVC
#if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM)
unsigned long bit;
_BitScanForward(&bit, (unsigned long)v);
return bit;
#else
_c4_lsb_fallback;
#endif
#else
return (unsigned)__builtin_ctz((unsigned)v);
#endif
#else
_c4_lsb_fallback;
#endif
}
// u16
template<class I>
C4_CONSTEXPR14
auto lsb(I v) noexcept
-> typename std::enable_if<sizeof(I) == 2u, unsigned>::type
{
C4_STATIC_ASSERT(std::is_unsigned<I>::value);
C4_ASSERT(v != 0);
#if _C4_USE_LSB_INTRINSIC(__builtin_ctz)
// upcast to use the intrinsic, it's cheaper.
// Then remember that the upcast makes it to 31bits
#ifdef C4_MSVC
#if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM)
unsigned long bit;
_BitScanForward(&bit, (unsigned long)v);
return bit;
#else
_c4_lsb_fallback;
#endif
#else
return (unsigned)__builtin_ctz((unsigned)v);
#endif
#else
_c4_lsb_fallback;
#endif
}
// u32
template<class I>
C4_CONSTEXPR14
auto lsb(I v) noexcept
-> typename std::enable_if<sizeof(I) == 4u, unsigned>::type
{
C4_STATIC_ASSERT(std::is_unsigned<I>::value);
C4_ASSERT(v != 0);
#if _C4_USE_LSB_INTRINSIC(__builtin_ctz)
#ifdef C4_MSVC
#if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM)
unsigned long bit;
_BitScanForward(&bit, v);
return bit;
#else
_c4_lsb_fallback;
#endif
#else
return (unsigned)__builtin_ctz((unsigned)v);
#endif
#else
_c4_lsb_fallback;
#endif
}
// u64 in 64bits
template<class I>
C4_CONSTEXPR14
auto lsb(I v) noexcept
-> typename std::enable_if<sizeof(I) == 8u && sizeof(unsigned long) == 8u, unsigned>::type
{
C4_STATIC_ASSERT(std::is_unsigned<I>::value);
C4_ASSERT(v != 0);
#if _C4_USE_LSB_INTRINSIC(__builtin_ctzl)
#if defined(C4_MSVC)
#if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM)
unsigned long bit;
_BitScanForward64(&bit, v);
return bit;
#else
_c4_lsb_fallback;
#endif
#else
return (unsigned)__builtin_ctzl((unsigned long)v);
#endif
#else
_c4_lsb_fallback;
#endif
}
// u64 in 32bits
template<class I>
C4_CONSTEXPR14
auto lsb(I v) noexcept
-> typename std::enable_if<sizeof(I) == 8u && sizeof(unsigned long long) == 8u && sizeof(unsigned long) != sizeof(unsigned long long), unsigned>::type
{
C4_STATIC_ASSERT(std::is_unsigned<I>::value);
C4_ASSERT(v != 0);
#if _C4_USE_LSB_INTRINSIC(__builtin_ctzll)
#if defined(C4_MSVC)
#if !defined(C4_CPU_X86) && !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM)
unsigned long bit;
_BitScanForward64(&bit, v);
return bit;
#else
_c4_lsb_fallback;
#endif
#else
return (unsigned)__builtin_ctzll((unsigned long long)v);
#endif
#else
_c4_lsb_fallback;
#endif
}
#undef _c4_lsb_fallback
/** @} */
namespace detail {
template<class I, I val, unsigned num_bits, bool finished> struct _lsb11;
template<class I, I val, unsigned num_bits>
struct _lsb11<I, val, num_bits, false>
{
enum : unsigned { num = _lsb11<I, (val>>1), num_bits+I(1), (((val>>1)&I(1))!=I(0))>::num };
};
template<class I, I val, unsigned num_bits>
struct _lsb11<I, val, num_bits, true>
{
enum : unsigned { num = num_bits };
};
} // namespace detail
/** TMP version of lsb(); this needs to be implemented with template
* meta-programming because C++11 cannot use a constexpr function with
* local variables
* @see lsb */
template<class I, I number>
struct lsb11
{
static_assert(number != 0, "lsb: number must be nonzero");
enum : unsigned { value = detail::_lsb11<I, number, 0, ((number&I(1))!=I(0))>::num};
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// most significant bit
/** @name msb Compute the most significant bit
* @note the input value must be nonzero
* @note the input type must be unsigned
*/
/** @{ */
#define _c4_msb8_fallback \
unsigned n = 0; \
if(v & I(0xf0)) v >>= 4, n |= I(4); \
if(v & I(0x0c)) v >>= 2, n |= I(2); \
if(v & I(0x02)) v >>= 1, n |= I(1); \
return n
#define _c4_msb16_fallback \
unsigned n = 0; \
if(v & I(0xff00)) v >>= 8, n |= I(8); \
if(v & I(0x00f0)) v >>= 4, n |= I(4); \
if(v & I(0x000c)) v >>= 2, n |= I(2); \
if(v & I(0x0002)) v >>= 1, n |= I(1); \
return n
#define _c4_msb32_fallback \
unsigned n = 0; \
if(v & I(0xffff0000)) v >>= 16, n |= 16; \
if(v & I(0x0000ff00)) v >>= 8, n |= 8; \
if(v & I(0x000000f0)) v >>= 4, n |= 4; \
if(v & I(0x0000000c)) v >>= 2, n |= 2; \
if(v & I(0x00000002)) v >>= 1, n |= 1; \
return n
#define _c4_msb64_fallback \
unsigned n = 0; \
if(v & I(0xffffffff00000000)) v >>= 32, n |= I(32); \
if(v & I(0x00000000ffff0000)) v >>= 16, n |= I(16); \
if(v & I(0x000000000000ff00)) v >>= 8, n |= I(8); \
if(v & I(0x00000000000000f0)) v >>= 4, n |= I(4); \
if(v & I(0x000000000000000c)) v >>= 2, n |= I(2); \
if(v & I(0x0000000000000002)) v >>= 1, n |= I(1); \
return n
// u8
template<class I>
C4_CONSTEXPR14
auto msb(I v) noexcept
-> typename std::enable_if<sizeof(I) == 1u, unsigned>::type
{
C4_STATIC_ASSERT(std::is_unsigned<I>::value);
C4_ASSERT(v != 0);
#if _C4_USE_MSB_INTRINSIC(__builtin_clz)
// upcast to use the intrinsic, it's cheaper.
// Then remember that the upcast makes it to 31bits
#ifdef C4_MSVC
#if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM)
unsigned long bit;
_BitScanReverse(&bit, (unsigned long)v);
return bit;
#else
_c4_msb8_fallback;
#endif
#else
return 31u - (unsigned)__builtin_clz((unsigned)v);
#endif
#else
_c4_msb8_fallback;
#endif
}
// u16
template<class I>
C4_CONSTEXPR14
auto msb(I v) noexcept
-> typename std::enable_if<sizeof(I) == 2u, unsigned>::type
{
C4_STATIC_ASSERT(std::is_unsigned<I>::value);
C4_ASSERT(v != 0);
#if _C4_USE_MSB_INTRINSIC(__builtin_clz)
// upcast to use the intrinsic, it's cheaper.
// Then remember that the upcast makes it to 31bits
#ifdef C4_MSVC
#if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM)
unsigned long bit;
_BitScanReverse(&bit, (unsigned long)v);
return bit;
#else
_c4_msb16_fallback;
#endif
#else
return 31u - (unsigned)__builtin_clz((unsigned)v);
#endif
#else
_c4_msb16_fallback;
#endif
}
// u32
template<class I>
C4_CONSTEXPR14
auto msb(I v) noexcept
-> typename std::enable_if<sizeof(I) == 4u, unsigned>::type
{
C4_STATIC_ASSERT(std::is_unsigned<I>::value);
C4_ASSERT(v != 0);
#if _C4_USE_MSB_INTRINSIC(__builtin_clz)
#ifdef C4_MSVC
#if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM)
unsigned long bit;
_BitScanReverse(&bit, v);
return bit;
#else
_c4_msb32_fallback;
#endif
#else
return 31u - (unsigned)__builtin_clz((unsigned)v);
#endif
#else
_c4_msb32_fallback;
#endif
}
// u64 in 64bits
template<class I>
C4_CONSTEXPR14
auto msb(I v) noexcept
-> typename std::enable_if<sizeof(I) == 8u && sizeof(unsigned long) == 8u, unsigned>::type
{
C4_STATIC_ASSERT(std::is_unsigned<I>::value);
C4_ASSERT(v != 0);
#if _C4_USE_MSB_INTRINSIC(__builtin_clzl)
#ifdef C4_MSVC
#if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM)
unsigned long bit;
_BitScanReverse64(&bit, v);
return bit;
#else
_c4_msb64_fallback;
#endif
#else
return 63u - (unsigned)__builtin_clzl((unsigned long)v);
#endif
#else
_c4_msb64_fallback;
#endif
}
// u64 in 32bits
template<class I>
C4_CONSTEXPR14
auto msb(I v) noexcept
-> typename std::enable_if<sizeof(I) == 8u && sizeof(unsigned long long) == 8u && sizeof(unsigned long) != sizeof(unsigned long long), unsigned>::type
{
C4_STATIC_ASSERT(std::is_unsigned<I>::value);
C4_ASSERT(v != 0);
#if _C4_USE_MSB_INTRINSIC(__builtin_clzll)
#ifdef C4_MSVC
#if !defined(C4_CPU_X86) && !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM)
unsigned long bit;
_BitScanReverse64(&bit, v);
return bit;
#else
_c4_msb64_fallback;
#endif
#else
return 63u - (unsigned)__builtin_clzll((unsigned long long)v);
#endif
#else
_c4_msb64_fallback;
#endif
}
#undef _c4_msb8_fallback
#undef _c4_msb16_fallback
#undef _c4_msb32_fallback
#undef _c4_msb64_fallback
/** @} */
namespace detail {
template<class I, I val, I num_bits, bool finished> struct _msb11;
template<class I, I val, I num_bits>
struct _msb11< I, val, num_bits, false>
{
enum : unsigned { num = _msb11<I, (val>>1), num_bits+I(1), ((val>>1)==I(0))>::num };
};
template<class I, I val, I num_bits>
struct _msb11<I, val, num_bits, true>
{
static_assert(val == 0, "bad implementation");
enum : unsigned { num = (unsigned)(num_bits-1) };
};
} // namespace detail
/** TMP version of msb(); this needs to be implemented with template
* meta-programming because C++11 cannot use a constexpr function with
* local variables
* @see msb */
template<class I, I number>
struct msb11
{
enum : unsigned { value = detail::_msb11<I, number, 0, (number==I(0))>::num };
};
#undef _C4_USE_LSB_INTRINSIC
#undef _C4_USE_MSB_INTRINSIC
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// there is an implicit conversion below; it happens when E or B are
// narrower than int, and thus any operation will upcast the result to
// int, and then downcast to assign
C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wconversion")
/** integer power; this function is constexpr-14 because of the local
* variables */
template<class B, class E>
C4_CONSTEXPR14 C4_CONST auto ipow(B base, E exponent) noexcept -> typename std::enable_if<std::is_signed<E>::value, B>::type
{
C4_STATIC_ASSERT(std::is_integral<E>::value);
B r = B(1);
if(exponent >= 0)
{
for(E e = 0; e < exponent; ++e)
r *= base;
}
else
{
exponent *= E(-1);
for(E e = 0; e < exponent; ++e)
r /= base;
}
return r;
}
/** integer power; this function is constexpr-14 because of the local
* variables */
template<class B, B base, class E>
C4_CONSTEXPR14 C4_CONST auto ipow(E exponent) noexcept -> typename std::enable_if<std::is_signed<E>::value, B>::type
{
C4_STATIC_ASSERT(std::is_integral<E>::value);
B r = B(1);
if(exponent >= 0)
{
for(E e = 0; e < exponent; ++e)
r *= base;
}
else
{
exponent *= E(-1);
for(E e = 0; e < exponent; ++e)
r /= base;
}
return r;
}
/** integer power; this function is constexpr-14 because of the local
* variables */
template<class B, class Base, Base base, class E>
C4_CONSTEXPR14 C4_CONST auto ipow(E exponent) noexcept -> typename std::enable_if<std::is_signed<E>::value, B>::type
{
C4_STATIC_ASSERT(std::is_integral<E>::value);
B r = B(1);
B bbase = B(base);
if(exponent >= 0)
{
for(E e = 0; e < exponent; ++e)
r *= bbase;
}
else
{
exponent *= E(-1);
for(E e = 0; e < exponent; ++e)
r /= bbase;
}
return r;
}
/** integer power; this function is constexpr-14 because of the local
* variables */
template<class B, class E>
C4_CONSTEXPR14 C4_CONST auto ipow(B base, E exponent) noexcept -> typename std::enable_if<!std::is_signed<E>::value, B>::type
{
C4_STATIC_ASSERT(std::is_integral<E>::value);
B r = B(1);
for(E e = 0; e < exponent; ++e)
r *= base;
return r;
}
/** integer power; this function is constexpr-14 because of the local
* variables */
template<class B, B base, class E>
C4_CONSTEXPR14 C4_CONST auto ipow(E exponent) noexcept -> typename std::enable_if<!std::is_signed<E>::value, B>::type
{
C4_STATIC_ASSERT(std::is_integral<E>::value);
B r = B(1);
for(E e = 0; e < exponent; ++e)
r *= base;
return r;
}
/** integer power; this function is constexpr-14 because of the local
* variables */
template<class B, class Base, Base base, class E>
C4_CONSTEXPR14 C4_CONST auto ipow(E exponent) noexcept -> typename std::enable_if<!std::is_signed<E>::value, B>::type
{
C4_STATIC_ASSERT(std::is_integral<E>::value);
B r = B(1);
B bbase = B(base);
for(E e = 0; e < exponent; ++e)
r *= bbase;
return r;
}
C4_SUPPRESS_WARNING_GCC_CLANG_POP
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** return a mask with all bits set [first_bit,last_bit[; this function
* is constexpr-14 because of the local variables */
template<class I>
C4_CONSTEXPR14 I contiguous_mask(I first_bit, I last_bit)
{
I r = 0;
for(I i = first_bit; i < last_bit; ++i)
{
r |= (I(1) << i);
}
return r;
}
namespace detail {
template<class I, I val, I first, I last, bool finished>
struct _ctgmsk11;
template<class I, I val, I first, I last>
struct _ctgmsk11< I, val, first, last, true>
{
enum : I { value = _ctgmsk11<I, val|(I(1)<<first), first+I(1), last, (first+1!=last)>::value };
};
template<class I, I val, I first, I last>
struct _ctgmsk11< I, val, first, last, false>
{
enum : I { value = val };
};
} // namespace detail
/** TMP version of contiguous_mask(); this needs to be implemented with template
* meta-programming because C++11 cannot use a constexpr function with
* local variables
* @see contiguous_mask */
template<class I, I first_bit, I last_bit>
struct contiguous_mask11
{
enum : I { value = detail::_ctgmsk11<I, I(0), first_bit, last_bit, (first_bit!=last_bit)>::value };
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** use Empty Base Class Optimization to reduce the size of a pair of
* potentially empty types*/
namespace detail {
typedef enum {
tpc_same,
tpc_same_empty,
tpc_both_empty,
tpc_first_empty,
tpc_second_empty,
tpc_general
} TightPairCase_e;
template<class First, class Second>
constexpr TightPairCase_e tpc_which_case()
{
return std::is_same<First, Second>::value ?
std::is_empty<First>::value ?
tpc_same_empty
:
tpc_same
:
std::is_empty<First>::value && std::is_empty<Second>::value ?
tpc_both_empty
:
std::is_empty<First>::value ?
tpc_first_empty
:
std::is_empty<Second>::value ?
tpc_second_empty
:
tpc_general
;
}
template<class First, class Second, TightPairCase_e Case>
struct tight_pair
{
private:
First m_first;
Second m_second;
public:
using first_type = First;
using second_type = Second;
tight_pair() : m_first(), m_second() {}
tight_pair(First const& f, Second const& s) : m_first(f), m_second(s) {}
C4_ALWAYS_INLINE C4_CONSTEXPR14 First & first () { return m_first; }
C4_ALWAYS_INLINE C4_CONSTEXPR14 First const& first () const { return m_first; }
C4_ALWAYS_INLINE C4_CONSTEXPR14 Second & second() { return m_second; }
C4_ALWAYS_INLINE C4_CONSTEXPR14 Second const& second() const { return m_second; }
};
template<class First, class Second>
struct tight_pair<First, Second, tpc_same_empty> : public First
{
static_assert(std::is_same<First, Second>::value, "bad implementation");
using first_type = First;
using second_type = Second;
tight_pair() : First() {}
tight_pair(First const& f, Second const& /*s*/) : First(f) {}
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); }
};
template<class First, class Second>
struct tight_pair<First, Second, tpc_both_empty> : public First, public Second
{
using first_type = First;
using second_type = Second;
tight_pair() : First(), Second() {}
tight_pair(First const& f, Second const& s) : First(f), Second(s) {}
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 static_cast<Second &>(*this); }
C4_ALWAYS_INLINE C4_CONSTEXPR14 Second const& second() const { return static_cast<Second const&>(*this); }
};
template<class First, class Second>
struct tight_pair<First, Second, tpc_same> : public First
{
Second m_second;
using first_type = First;
using second_type = Second;
tight_pair() : First() {}
tight_pair(First const& f, Second const& s) : First(f), m_second(s) {}
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 m_second; }
C4_ALWAYS_INLINE C4_CONSTEXPR14 Second const& second() const { return m_second; }
};
template<class First, class Second>
struct tight_pair<First, Second, tpc_first_empty> : public First
{
Second m_second;
using first_type = First;
using second_type = Second;
tight_pair() : First(), m_second() {}
tight_pair(First const& f, Second const& s) : First(f), m_second(s) {}
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 m_second; }
C4_ALWAYS_INLINE C4_CONSTEXPR14 Second const& second() const { return m_second; }
};
template<class First, class Second>
struct tight_pair<First, Second, tpc_second_empty> : public Second
{
First m_first;
using first_type = First;
using second_type = Second;
tight_pair() : Second(), m_first() {}
tight_pair(First const& f, Second const& s) : Second(s), m_first(f) {}
C4_ALWAYS_INLINE C4_CONSTEXPR14 First & first () { return m_first; }
C4_ALWAYS_INLINE C4_CONSTEXPR14 First const& first () const { return m_first; }
C4_ALWAYS_INLINE C4_CONSTEXPR14 Second & second() { return static_cast<Second &>(*this); }
C4_ALWAYS_INLINE C4_CONSTEXPR14 Second const& second() const { return static_cast<Second const&>(*this); }
};
} // namespace detail
template<class First, class Second>
using tight_pair = detail::tight_pair<First, Second, detail::tpc_which_case<First,Second>()>;
C4_SUPPRESS_WARNING_GCC_CLANG_POP
} // namespace c4
#endif /* _C4_MEMORY_UTIL_HPP_ */

View File

@ -0,0 +1,46 @@
#ifndef _C4_PLATFORM_HPP_
#define _C4_PLATFORM_HPP_
/** @file platform.hpp Provides platform information macros
* @ingroup basic_headers */
// see also https://sourceforge.net/p/predef/wiki/OperatingSystems/
#if defined(_WIN64)
# define C4_WIN
# define C4_WIN64
#elif defined(_WIN32)
# define C4_WIN
# define C4_WIN32
#elif defined(__ANDROID__)
# define C4_ANDROID
#elif defined(__APPLE__)
# include "TargetConditionals.h"
# if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
# define C4_IOS
# elif TARGET_OS_MAC || TARGET_OS_OSX
# define C4_MACOS
# else
# error "Unknown Apple platform"
# endif
#elif defined(__linux__) || defined(__linux)
# define C4_UNIX
# define C4_LINUX
#elif defined(__unix__) || defined(__unix)
# define C4_UNIX
#elif defined(__arm__) || defined(__aarch64__)
# define C4_ARM
#elif defined(__xtensa__) || defined(__XTENSA__)
# define C4_XTENSA
#elif defined(SWIG)
# define C4_SWIG
#else
# error "unknown platform"
#endif
#if defined(__posix) || defined(C4_UNIX) || defined(C4_LINUX)
# define C4_POSIX
#endif
#endif /* _C4_PLATFORM_HPP_ */

View File

@ -0,0 +1,123 @@
#ifndef _C4_PREPROCESSOR_HPP_
#define _C4_PREPROCESSOR_HPP_
/** @file preprocessor.hpp Contains basic macros and preprocessor utilities.
* @ingroup basic_headers */
#ifdef __clang__
/* NOTE: using , ## __VA_ARGS__ to deal with zero-args calls to
* variadic macros is not portable, but works in clang, gcc, msvc, icc.
* clang requires switching off compiler warnings for pedantic mode.
* @see http://stackoverflow.com/questions/32047685/variadic-macro-without-arguments */
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" // warning: token pasting of ',' and __VA_ARGS__ is a GNU extension
#elif defined(__GNUC__)
/* GCC also issues a warning for zero-args calls to variadic macros.
* This warning is switched on with -pedantic and apparently there is no
* easy way to turn it off as with clang. But marking this as a system
* header works.
* @see https://gcc.gnu.org/onlinedocs/cpp/System-Headers.html
* @see http://stackoverflow.com/questions/35587137/ */
# pragma GCC system_header
#endif
#define C4_WIDEN(str) L"" str
#define C4_COUNTOF(arr) (sizeof(arr)/sizeof((arr)[0]))
#define C4_EXPAND(arg) arg
/** useful in some macro calls with template arguments */
#define C4_COMMA ,
/** useful in some macro calls with template arguments
* @see C4_COMMA */
#define C4_COMMA_X C4_COMMA
/** expand and quote */
#define C4_XQUOTE(arg) _C4_XQUOTE(arg)
#define _C4_XQUOTE(arg) C4_QUOTE(arg)
#define C4_QUOTE(arg) #arg
/** expand and concatenate */
#define C4_XCAT(arg1, arg2) _C4_XCAT(arg1, arg2)
#define _C4_XCAT(arg1, arg2) C4_CAT(arg1, arg2)
#define C4_CAT(arg1, arg2) arg1##arg2
#define C4_VERSION_CAT(major, minor, patch) ((major)*10000 + (minor)*100 + (patch))
/** A preprocessor foreach. Spectacular trick taken from:
* http://stackoverflow.com/a/1872506/5875572
* The first argument is for a macro receiving a single argument,
* which will be called with every subsequent argument. There is
* currently a limit of 32 arguments, and at least 1 must be provided.
*
Example:
@code{.cpp}
struct Example {
int a;
int b;
int c;
};
// define a one-arg macro to be called
#define PRN_STRUCT_OFFSETS(field) PRN_STRUCT_OFFSETS_(Example, field)
#define PRN_STRUCT_OFFSETS_(structure, field) printf(C4_XQUOTE(structure) ":" C4_XQUOTE(field)" - offset=%zu\n", offsetof(structure, field));
// now call the macro for a, b and c
C4_FOR_EACH(PRN_STRUCT_OFFSETS, a, b, c);
@endcode */
#define C4_FOR_EACH(what, ...) C4_FOR_EACH_SEP(what, ;, __VA_ARGS__)
/** same as C4_FOR_EACH(), but use a custom separator between statements.
* If a comma is needed as the separator, use the C4_COMMA macro.
* @see C4_FOR_EACH
* @see C4_COMMA
*/
#define C4_FOR_EACH_SEP(what, sep, ...) _C4_FOR_EACH_(_C4_FOR_EACH_NARG(__VA_ARGS__), what, sep, __VA_ARGS__)
/// @cond dev
#define _C4_FOR_EACH_01(what, sep, x) what(x) sep
#define _C4_FOR_EACH_02(what, sep, x, ...) what(x) sep _C4_FOR_EACH_01(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_03(what, sep, x, ...) what(x) sep _C4_FOR_EACH_02(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_04(what, sep, x, ...) what(x) sep _C4_FOR_EACH_03(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_05(what, sep, x, ...) what(x) sep _C4_FOR_EACH_04(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_06(what, sep, x, ...) what(x) sep _C4_FOR_EACH_05(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_07(what, sep, x, ...) what(x) sep _C4_FOR_EACH_06(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_08(what, sep, x, ...) what(x) sep _C4_FOR_EACH_07(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_09(what, sep, x, ...) what(x) sep _C4_FOR_EACH_08(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_10(what, sep, x, ...) what(x) sep _C4_FOR_EACH_09(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_11(what, sep, x, ...) what(x) sep _C4_FOR_EACH_10(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_12(what, sep, x, ...) what(x) sep _C4_FOR_EACH_11(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_13(what, sep, x, ...) what(x) sep _C4_FOR_EACH_12(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_14(what, sep, x, ...) what(x) sep _C4_FOR_EACH_13(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_15(what, sep, x, ...) what(x) sep _C4_FOR_EACH_14(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_16(what, sep, x, ...) what(x) sep _C4_FOR_EACH_15(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_17(what, sep, x, ...) what(x) sep _C4_FOR_EACH_16(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_18(what, sep, x, ...) what(x) sep _C4_FOR_EACH_17(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_19(what, sep, x, ...) what(x) sep _C4_FOR_EACH_18(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_20(what, sep, x, ...) what(x) sep _C4_FOR_EACH_19(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_21(what, sep, x, ...) what(x) sep _C4_FOR_EACH_20(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_22(what, sep, x, ...) what(x) sep _C4_FOR_EACH_21(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_23(what, sep, x, ...) what(x) sep _C4_FOR_EACH_22(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_24(what, sep, x, ...) what(x) sep _C4_FOR_EACH_23(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_25(what, sep, x, ...) what(x) sep _C4_FOR_EACH_24(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_26(what, sep, x, ...) what(x) sep _C4_FOR_EACH_25(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_27(what, sep, x, ...) what(x) sep _C4_FOR_EACH_26(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_28(what, sep, x, ...) what(x) sep _C4_FOR_EACH_27(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_29(what, sep, x, ...) what(x) sep _C4_FOR_EACH_28(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_30(what, sep, x, ...) what(x) sep _C4_FOR_EACH_29(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_31(what, sep, x, ...) what(x) sep _C4_FOR_EACH_30(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_32(what, sep, x, ...) what(x) sep _C4_FOR_EACH_31(what, sep, __VA_ARGS__)
#define _C4_FOR_EACH_NARG(...) _C4_FOR_EACH_NARG_(__VA_ARGS__, _C4_FOR_EACH_RSEQ_N())
#define _C4_FOR_EACH_NARG_(...) _C4_FOR_EACH_ARG_N(__VA_ARGS__)
#define _C4_FOR_EACH_ARG_N(_01, _02, _03, _04, _05, _06, _07, _08, _09, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, N, ...) N
#define _C4_FOR_EACH_RSEQ_N() 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 09, 08, 07, 06, 05, 04, 03, 02, 01
#define _C4_FOR_EACH_(N, what, sep, ...) C4_XCAT(_C4_FOR_EACH_, N)(what, sep, __VA_ARGS__)
/// @endcond
#ifdef __clang__
# pragma clang diagnostic pop
#endif
#endif /* _C4_PREPROCESSOR_HPP_ */

View File

@ -0,0 +1,11 @@
#ifndef _C4_STD_STD_HPP_
#define _C4_STD_STD_HPP_
/** @file std.hpp includes all c4-std interop files */
#include "c4/std/vector.hpp"
#include "c4/std/string.hpp"
#include "c4/std/string_view.hpp"
#include "c4/std/tuple.hpp"
#endif // _C4_STD_STD_HPP_

View File

@ -0,0 +1,10 @@
#ifndef _C4_STD_STD_FWD_HPP_
#define _C4_STD_STD_FWD_HPP_
/** @file std_fwd.hpp includes all c4-std interop fwd files */
#include "c4/std/vector_fwd.hpp"
#include "c4/std/string_fwd.hpp"
//#include "c4/std/tuple_fwd.hpp"
#endif // _C4_STD_STD_FWD_HPP_

View File

@ -0,0 +1,97 @@
#ifndef _C4_STD_STRING_HPP_
#define _C4_STD_STRING_HPP_
/** @file string.hpp */
#ifndef C4CORE_SINGLE_HEADER
#include "c4/substr.hpp"
#endif
#include <string>
namespace c4 {
//-----------------------------------------------------------------------------
/** get a writeable view to an existing std::string.
* When the string is empty, the returned view will be pointing
* at the character with value '\0', but the size will be zero.
* @see https://en.cppreference.com/w/cpp/string/basic_string/operator_at
*/
C4_ALWAYS_INLINE c4::substr to_substr(std::string &s) noexcept
{
#if C4_CPP < 11
#error this function will have undefined behavior
#endif
// since c++11 it is legal to call s[s.size()].
return c4::substr(&s[0], s.size());
}
/** get a readonly view to an existing std::string.
* When the string is empty, the returned view will be pointing
* at the character with value '\0', but the size will be zero.
* @see https://en.cppreference.com/w/cpp/string/basic_string/operator_at
*/
C4_ALWAYS_INLINE c4::csubstr to_csubstr(std::string const& s) noexcept
{
#if C4_CPP < 11
#error this function will have undefined behavior
#endif
// since c++11 it is legal to call s[s.size()].
return c4::csubstr(&s[0], s.size());
}
//-----------------------------------------------------------------------------
C4_ALWAYS_INLINE bool operator== (c4::csubstr ss, std::string const& s) { return ss.compare(to_csubstr(s)) == 0; }
C4_ALWAYS_INLINE bool operator!= (c4::csubstr ss, std::string const& s) { return ss.compare(to_csubstr(s)) != 0; }
C4_ALWAYS_INLINE bool operator>= (c4::csubstr ss, std::string const& s) { return ss.compare(to_csubstr(s)) >= 0; }
C4_ALWAYS_INLINE bool operator> (c4::csubstr ss, std::string const& s) { return ss.compare(to_csubstr(s)) > 0; }
C4_ALWAYS_INLINE bool operator<= (c4::csubstr ss, std::string const& s) { return ss.compare(to_csubstr(s)) <= 0; }
C4_ALWAYS_INLINE bool operator< (c4::csubstr ss, std::string const& s) { return ss.compare(to_csubstr(s)) < 0; }
C4_ALWAYS_INLINE bool operator== (std::string const& s, c4::csubstr ss) { return ss.compare(to_csubstr(s)) == 0; }
C4_ALWAYS_INLINE bool operator!= (std::string const& s, c4::csubstr ss) { return ss.compare(to_csubstr(s)) != 0; }
C4_ALWAYS_INLINE bool operator>= (std::string const& s, c4::csubstr ss) { return ss.compare(to_csubstr(s)) <= 0; }
C4_ALWAYS_INLINE bool operator> (std::string const& s, c4::csubstr ss) { return ss.compare(to_csubstr(s)) < 0; }
C4_ALWAYS_INLINE bool operator<= (std::string const& s, c4::csubstr ss) { return ss.compare(to_csubstr(s)) >= 0; }
C4_ALWAYS_INLINE bool operator< (std::string const& s, c4::csubstr ss) { return ss.compare(to_csubstr(s)) > 0; }
//-----------------------------------------------------------------------------
/** copy an std::string to a writeable string view */
inline size_t to_chars(c4::substr buf, std::string const& s)
{
C4_ASSERT(!buf.overlaps(to_csubstr(s)));
size_t len = buf.len < s.size() ? buf.len : s.size();
// calling memcpy with null strings is undefined behavior
// and will wreak havoc in calling code's branches.
// see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637
if(len)
{
C4_ASSERT(s.data() != nullptr);
C4_ASSERT(buf.str != nullptr);
memcpy(buf.str, s.data(), len);
}
return s.size(); // return the number of needed chars
}
/** copy a string view to an existing std::string */
inline bool from_chars(c4::csubstr buf, std::string * s)
{
s->resize(buf.len);
C4_ASSERT(!buf.overlaps(to_csubstr(*s)));
// calling memcpy with null strings is undefined behavior
// and will wreak havoc in calling code's branches.
// see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637
if(buf.len)
{
C4_ASSERT(buf.str != nullptr);
memcpy(&(*s)[0], buf.str, buf.len);
}
return true;
}
} // namespace c4
#endif // _C4_STD_STRING_HPP_

View File

@ -0,0 +1,59 @@
#ifndef _C4_STD_STRING_FWD_HPP_
#define _C4_STD_STRING_FWD_HPP_
/** @file string_fwd.hpp */
#ifndef DOXYGEN
#ifndef C4CORE_SINGLE_HEADER
#include "c4/substr_fwd.hpp"
#endif
#include <cstddef>
// forward declarations for std::string
#if defined(__GLIBCXX__) || defined(__GLIBCPP__)
#include <bits/stringfwd.h> // use the fwd header in glibcxx
#elif defined(_LIBCPP_VERSION) || defined(__APPLE_CC__)
#include <iosfwd> // use the fwd header in stdlibc++
#elif defined(_MSC_VER)
#include "c4/error.hpp"
//! @todo is there a fwd header in msvc?
namespace std {
C4_SUPPRESS_WARNING_MSVC_WITH_PUSH(4643) // Forward declaring 'char_traits' in namespace std is not permitted by the C++ Standard.
template<typename> struct char_traits;
template<typename> class allocator;
template<typename _CharT, typename _Traits, typename _Alloc> class basic_string;
using string = basic_string<char, char_traits<char>, allocator<char>>;
C4_SUPPRESS_WARNING_MSVC_POP
} /* namespace std */
#else
#error "unknown standard library"
#endif
namespace c4 {
C4_ALWAYS_INLINE c4::substr to_substr(std::string &s) noexcept;
C4_ALWAYS_INLINE c4::csubstr to_csubstr(std::string const& s) noexcept;
bool operator== (c4::csubstr ss, std::string const& s);
bool operator!= (c4::csubstr ss, std::string const& s);
bool operator>= (c4::csubstr ss, std::string const& s);
bool operator> (c4::csubstr ss, std::string const& s);
bool operator<= (c4::csubstr ss, std::string const& s);
bool operator< (c4::csubstr ss, std::string const& s);
bool operator== (std::string const& s, c4::csubstr ss);
bool operator!= (std::string const& s, c4::csubstr ss);
bool operator>= (std::string const& s, c4::csubstr ss);
bool operator> (std::string const& s, c4::csubstr ss);
bool operator<= (std::string const& s, c4::csubstr ss);
bool operator< (std::string const& s, c4::csubstr ss);
size_t to_chars(c4::substr buf, std::string const& s);
bool from_chars(c4::csubstr buf, std::string * s);
} // namespace c4
#endif // DOXYGEN
#endif // _C4_STD_STRING_FWD_HPP_

View File

@ -0,0 +1,71 @@
#ifndef _C4_STD_STRING_VIEW_HPP_
#define _C4_STD_STRING_VIEW_HPP_
/** @file string_view.hpp */
#ifndef C4CORE_SINGLE_HEADER
#include "c4/language.hpp"
#endif
#if (C4_CPP >= 17 && defined(__cpp_lib_string_view))
#ifndef C4CORE_SINGLE_HEADER
#include "c4/substr.hpp"
#endif
#include <string_view>
namespace c4 {
//-----------------------------------------------------------------------------
/** create a csubstr from an existing std::string_view. */
C4_ALWAYS_INLINE c4::csubstr to_csubstr(std::string_view s) noexcept
{
return c4::csubstr(s.data(), s.size());
}
//-----------------------------------------------------------------------------
C4_ALWAYS_INLINE bool operator== (c4::csubstr ss, std::string_view s) { return ss.compare(s.data(), s.size()) == 0; }
C4_ALWAYS_INLINE bool operator!= (c4::csubstr ss, std::string_view s) { return ss.compare(s.data(), s.size()) != 0; }
C4_ALWAYS_INLINE bool operator>= (c4::csubstr ss, std::string_view s) { return ss.compare(s.data(), s.size()) >= 0; }
C4_ALWAYS_INLINE bool operator> (c4::csubstr ss, std::string_view s) { return ss.compare(s.data(), s.size()) > 0; }
C4_ALWAYS_INLINE bool operator<= (c4::csubstr ss, std::string_view s) { return ss.compare(s.data(), s.size()) <= 0; }
C4_ALWAYS_INLINE bool operator< (c4::csubstr ss, std::string_view s) { return ss.compare(s.data(), s.size()) < 0; }
C4_ALWAYS_INLINE bool operator== (std::string_view s, c4::csubstr ss) { return ss.compare(s.data(), s.size()) == 0; }
C4_ALWAYS_INLINE bool operator!= (std::string_view s, c4::csubstr ss) { return ss.compare(s.data(), s.size()) != 0; }
C4_ALWAYS_INLINE bool operator<= (std::string_view s, c4::csubstr ss) { return ss.compare(s.data(), s.size()) >= 0; }
C4_ALWAYS_INLINE bool operator< (std::string_view s, c4::csubstr ss) { return ss.compare(s.data(), s.size()) > 0; }
C4_ALWAYS_INLINE bool operator>= (std::string_view s, c4::csubstr ss) { return ss.compare(s.data(), s.size()) <= 0; }
C4_ALWAYS_INLINE bool operator> (std::string_view s, c4::csubstr ss) { return ss.compare(s.data(), s.size()) < 0; }
//-----------------------------------------------------------------------------
/** copy an std::string_view to a writeable substr */
inline size_t to_chars(c4::substr buf, std::string_view s)
{
C4_ASSERT(!buf.overlaps(to_csubstr(s)));
size_t sz = s.size();
size_t len = buf.len < sz ? buf.len : sz;
// calling memcpy with null strings is undefined behavior
// and will wreak havoc in calling code's branches.
// see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637
if(len)
{
C4_ASSERT(s.data() != nullptr);
C4_ASSERT(buf.str != nullptr);
memcpy(buf.str, s.data(), len);
}
return sz; // return the number of needed chars
}
} // namespace c4
#endif // C4_STRING_VIEW_AVAILABLE
#endif // _C4_STD_STRING_VIEW_HPP_

View File

@ -0,0 +1,184 @@
#ifndef _C4_STD_TUPLE_HPP_
#define _C4_STD_TUPLE_HPP_
/** @file tuple.hpp */
#ifndef C4CORE_SINGLE_HEADER
#include "c4/format.hpp"
#endif
#include <tuple>
/** this is a work in progress */
#undef C4_TUPLE_TO_CHARS
namespace c4 {
#ifdef C4_TUPLE_TO_CHARS
namespace detail {
template< size_t Curr, class... Types >
struct tuple_helper
{
static size_t do_cat(substr buf, std::tuple< Types... > const& tp)
{
size_t num = to_chars(buf, std::get<Curr>(tp));
buf = buf.len >= num ? buf.sub(num) : substr{};
num += tuple_helper< Curr+1, Types... >::do_cat(buf, tp);
return num;
}
static size_t do_uncat(csubstr buf, std::tuple< Types... > & tp)
{
size_t num = from_str_trim(buf, &std::get<Curr>(tp));
if(num == csubstr::npos) return csubstr::npos;
buf = buf.len >= num ? buf.sub(num) : substr{};
num += tuple_helper< Curr+1, Types... >::do_uncat(buf, tp);
return num;
}
template< class Sep >
static size_t do_catsep_more(substr buf, Sep const& sep, std::tuple< Types... > const& tp)
{
size_t ret = to_chars(buf, sep), num = ret;
buf = buf.len >= ret ? buf.sub(ret) : substr{};
ret = to_chars(buf, std::get<Curr>(tp));
num += ret;
buf = buf.len >= ret ? buf.sub(ret) : substr{};
ret = tuple_helper< Curr+1, Types... >::do_catsep_more(buf, sep, tp);
num += ret;
return num;
}
template< class Sep >
static size_t do_uncatsep_more(csubstr buf, Sep & sep, std::tuple< Types... > & tp)
{
size_t ret = from_str_trim(buf, &sep), num = ret;
if(ret == csubstr::npos) return csubstr::npos;
buf = buf.len >= ret ? buf.sub(ret) : substr{};
ret = from_str_trim(buf, &std::get<Curr>(tp));
if(ret == csubstr::npos) return csubstr::npos;
num += ret;
buf = buf.len >= ret ? buf.sub(ret) : substr{};
ret = tuple_helper< Curr+1, Types... >::do_uncatsep_more(buf, sep, tp);
if(ret == csubstr::npos) return csubstr::npos;
num += ret;
return num;
}
static size_t do_format(substr buf, csubstr fmt, std::tuple< Types... > const& tp)
{
auto pos = fmt.find("{}");
if(pos != csubstr::npos)
{
size_t num = to_chars(buf, fmt.sub(0, pos));
size_t out = num;
buf = buf.len >= num ? buf.sub(num) : substr{};
num = to_chars(buf, std::get<Curr>(tp));
out += num;
buf = buf.len >= num ? buf.sub(num) : substr{};
num = tuple_helper< Curr+1, Types... >::do_format(buf, fmt.sub(pos + 2), tp);
out += num;
return out;
}
else
{
return format(buf, fmt);
}
}
static size_t do_unformat(csubstr buf, csubstr fmt, std::tuple< Types... > & tp)
{
auto pos = fmt.find("{}");
if(pos != csubstr::npos)
{
size_t num = pos;
size_t out = num;
buf = buf.len >= num ? buf.sub(num) : substr{};
num = from_str_trim(buf, &std::get<Curr>(tp));
out += num;
buf = buf.len >= num ? buf.sub(num) : substr{};
num = tuple_helper< Curr+1, Types... >::do_unformat(buf, fmt.sub(pos + 2), tp);
out += num;
return out;
}
else
{
return tuple_helper< sizeof...(Types), Types... >::do_unformat(buf, fmt, tp);
}
}
};
/** @todo VS compilation fails for this class */
template< class... Types >
struct tuple_helper< sizeof...(Types), Types... >
{
static size_t do_cat(substr /*buf*/, std::tuple<Types...> const& /*tp*/) { return 0; }
static size_t do_uncat(csubstr /*buf*/, std::tuple<Types...> & /*tp*/) { return 0; }
template< class Sep > static size_t do_catsep_more(substr /*buf*/, Sep const& /*sep*/, std::tuple<Types...> const& /*tp*/) { return 0; }
template< class Sep > static size_t do_uncatsep_more(csubstr /*buf*/, Sep & /*sep*/, std::tuple<Types...> & /*tp*/) { return 0; }
static size_t do_format(substr buf, csubstr fmt, std::tuple<Types...> const& /*tp*/)
{
return to_chars(buf, fmt);
}
static size_t do_unformat(csubstr buf, csubstr fmt, std::tuple<Types...> const& /*tp*/)
{
return 0;
}
};
} // namespace detail
template< class... Types >
inline size_t cat(substr buf, std::tuple< Types... > const& tp)
{
return detail::tuple_helper< 0, Types... >::do_cat(buf, tp);
}
template< class... Types >
inline size_t uncat(csubstr buf, std::tuple< Types... > & tp)
{
return detail::tuple_helper< 0, Types... >::do_uncat(buf, tp);
}
template< class Sep, class... Types >
inline size_t catsep(substr buf, Sep const& sep, std::tuple< Types... > const& tp)
{
size_t num = to_chars(buf, std::cref(std::get<0>(tp)));
buf = buf.len >= num ? buf.sub(num) : substr{};
num += detail::tuple_helper< 1, Types... >::do_catsep_more(buf, sep, tp);
return num;
}
template< class Sep, class... Types >
inline size_t uncatsep(csubstr buf, Sep & sep, std::tuple< Types... > & tp)
{
size_t ret = from_str_trim(buf, &std::get<0>(tp)), num = ret;
if(ret == csubstr::npos) return csubstr::npos;
buf = buf.len >= ret ? buf.sub(ret) : substr{};
ret = detail::tuple_helper< 1, Types... >::do_uncatsep_more(buf, sep, tp);
if(ret == csubstr::npos) return csubstr::npos;
num += ret;
return num;
}
template< class... Types >
inline size_t format(substr buf, csubstr fmt, std::tuple< Types... > const& tp)
{
return detail::tuple_helper< 0, Types... >::do_format(buf, fmt, tp);
}
template< class... Types >
inline size_t unformat(csubstr buf, csubstr fmt, std::tuple< Types... > & tp)
{
return detail::tuple_helper< 0, Types... >::do_unformat(buf, fmt, tp);
}
#endif // C4_TUPLE_TO_CHARS
} // namespace c4
#endif /* _C4_STD_TUPLE_HPP_ */

View File

@ -0,0 +1,88 @@
#ifndef _C4_STD_VECTOR_HPP_
#define _C4_STD_VECTOR_HPP_
/** @file vector.hpp provides conversion and comparison facilities
* from/between std::vector<char> to c4::substr and c4::csubstr.
* @todo add to_span() and friends
*/
#ifndef C4CORE_SINGLE_HEADER
#include "c4/substr.hpp"
#endif
#include <vector>
namespace c4 {
//-----------------------------------------------------------------------------
/** get a substr (writeable string view) of an existing std::vector<char> */
template<class Alloc>
c4::substr to_substr(std::vector<char, Alloc> &vec)
{
char *data = vec.empty() ? nullptr : vec.data(); // data() may or may not return a null pointer.
return c4::substr(data, vec.size());
}
/** get a csubstr (read-only string) view of an existing std::vector<char> */
template<class Alloc>
c4::csubstr to_csubstr(std::vector<char, Alloc> const& vec)
{
const char *data = vec.empty() ? nullptr : vec.data(); // data() may or may not return a null pointer.
return c4::csubstr(data, vec.size());
}
//-----------------------------------------------------------------------------
// comparisons between substrings and std::vector<char>
template<class Alloc> C4_ALWAYS_INLINE bool operator!= (c4::csubstr ss, std::vector<char, Alloc> const& s) { return ss != to_csubstr(s); }
template<class Alloc> C4_ALWAYS_INLINE bool operator== (c4::csubstr ss, std::vector<char, Alloc> const& s) { return ss == to_csubstr(s); }
template<class Alloc> C4_ALWAYS_INLINE bool operator>= (c4::csubstr ss, std::vector<char, Alloc> const& s) { return ss >= to_csubstr(s); }
template<class Alloc> C4_ALWAYS_INLINE bool operator> (c4::csubstr ss, std::vector<char, Alloc> const& s) { return ss > to_csubstr(s); }
template<class Alloc> C4_ALWAYS_INLINE bool operator<= (c4::csubstr ss, std::vector<char, Alloc> const& s) { return ss <= to_csubstr(s); }
template<class Alloc> C4_ALWAYS_INLINE bool operator< (c4::csubstr ss, std::vector<char, Alloc> const& s) { return ss < to_csubstr(s); }
template<class Alloc> C4_ALWAYS_INLINE bool operator!= (std::vector<char, Alloc> const& s, c4::csubstr ss) { return ss != to_csubstr(s); }
template<class Alloc> C4_ALWAYS_INLINE bool operator== (std::vector<char, Alloc> const& s, c4::csubstr ss) { return ss == to_csubstr(s); }
template<class Alloc> C4_ALWAYS_INLINE bool operator>= (std::vector<char, Alloc> const& s, c4::csubstr ss) { return ss <= to_csubstr(s); }
template<class Alloc> C4_ALWAYS_INLINE bool operator> (std::vector<char, Alloc> const& s, c4::csubstr ss) { return ss < to_csubstr(s); }
template<class Alloc> C4_ALWAYS_INLINE bool operator<= (std::vector<char, Alloc> const& s, c4::csubstr ss) { return ss >= to_csubstr(s); }
template<class Alloc> C4_ALWAYS_INLINE bool operator< (std::vector<char, Alloc> const& s, c4::csubstr ss) { return ss > to_csubstr(s); }
//-----------------------------------------------------------------------------
/** copy a std::vector<char> to a writeable string view */
template<class Alloc>
inline size_t to_chars(c4::substr buf, std::vector<char, Alloc> const& s)
{
C4_ASSERT(!buf.overlaps(to_csubstr(s)));
size_t len = buf.len < s.size() ? buf.len : s.size();
// calling memcpy with null strings is undefined behavior
// and will wreak havoc in calling code's branches.
// see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637
if(len > 0)
{
memcpy(buf.str, s.data(), len);
}
return s.size(); // return the number of needed chars
}
/** copy a string view to an existing std::vector<char> */
template<class Alloc>
inline bool from_chars(c4::csubstr buf, std::vector<char, Alloc> * s)
{
s->resize(buf.len);
C4_ASSERT(!buf.overlaps(to_csubstr(*s)));
// calling memcpy with null strings is undefined behavior
// and will wreak havoc in calling code's branches.
// see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637
if(buf.len > 0)
{
memcpy(&(*s)[0], buf.str, buf.len);
}
return true;
}
} // namespace c4
#endif // _C4_STD_VECTOR_HPP_

View File

@ -0,0 +1,66 @@
#ifndef _C4_STD_VECTOR_FWD_HPP_
#define _C4_STD_VECTOR_FWD_HPP_
/** @file vector_fwd.hpp */
#include <cstddef>
// forward declarations for std::vector
#if defined(__GLIBCXX__) || defined(__GLIBCPP__) || defined(_MSC_VER)
#if defined(_MSC_VER)
__pragma(warning(push))
__pragma(warning(disable : 4643))
#endif
namespace std {
template<typename> class allocator;
#ifdef _GLIBCXX_DEBUG
inline namespace __debug {
template<typename T, typename Alloc> class vector;
}
#else
template<typename T, typename Alloc> class vector;
#endif
} // namespace std
#if defined(_MSC_VER)
__pragma(warning(pop))
#endif
#elif defined(_LIBCPP_ABI_NAMESPACE)
namespace std {
inline namespace _LIBCPP_ABI_NAMESPACE {
template<typename> class allocator;
template<typename T, typename Alloc> class vector;
} // namespace _LIBCPP_ABI_NAMESPACE
} // namespace std
#else
#error "unknown standard library"
#endif
#ifndef C4CORE_SINGLE_HEADER
#include "c4/substr_fwd.hpp"
#endif
namespace c4 {
template<class Alloc> c4::substr to_substr(std::vector<char, Alloc> &vec);
template<class Alloc> c4::csubstr to_csubstr(std::vector<char, Alloc> const& vec);
template<class Alloc> bool operator!= (c4::csubstr ss, std::vector<char, Alloc> const& s);
template<class Alloc> bool operator== (c4::csubstr ss, std::vector<char, Alloc> const& s);
template<class Alloc> bool operator>= (c4::csubstr ss, std::vector<char, Alloc> const& s);
template<class Alloc> bool operator> (c4::csubstr ss, std::vector<char, Alloc> const& s);
template<class Alloc> bool operator<= (c4::csubstr ss, std::vector<char, Alloc> const& s);
template<class Alloc> bool operator< (c4::csubstr ss, std::vector<char, Alloc> const& s);
template<class Alloc> bool operator!= (std::vector<char, Alloc> const& s, c4::csubstr ss);
template<class Alloc> bool operator== (std::vector<char, Alloc> const& s, c4::csubstr ss);
template<class Alloc> bool operator>= (std::vector<char, Alloc> const& s, c4::csubstr ss);
template<class Alloc> bool operator> (std::vector<char, Alloc> const& s, c4::csubstr ss);
template<class Alloc> bool operator<= (std::vector<char, Alloc> const& s, c4::csubstr ss);
template<class Alloc> bool operator< (std::vector<char, Alloc> const& s, c4::csubstr ss);
template<class Alloc> size_t to_chars(c4::substr buf, std::vector<char, Alloc> const& s);
template<class Alloc> bool from_chars(c4::csubstr buf, std::vector<char, Alloc> * s);
} // namespace c4
#endif // _C4_STD_VECTOR_FWD_HPP_

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,16 @@
#ifndef _C4_SUBSTR_FWD_HPP_
#define _C4_SUBSTR_FWD_HPP_
#include "c4/export.hpp"
namespace c4 {
#ifndef DOXYGEN
template<class C> struct basic_substring;
using csubstr = C4CORE_EXPORT basic_substring<const char>;
using substr = C4CORE_EXPORT basic_substring<char>;
#endif // !DOXYGEN
} // namespace c4
#endif /* _C4_SUBSTR_FWD_HPP_ */

View File

@ -0,0 +1,68 @@
#ifndef _C4_SZCONV_HPP_
#define _C4_SZCONV_HPP_
/** @file szconv.hpp utilities to deal safely with narrowing conversions */
#include "c4/config.hpp"
#include "c4/error.hpp"
#include <limits>
namespace c4 {
C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wold-style-cast")
/** @todo this would be so much easier with calls to numeric_limits::max()... */
template<class SizeOut, class SizeIn>
struct is_narrower_size : std::conditional
<
(std::is_signed<SizeOut>::value == std::is_signed<SizeIn>::value)
?
(sizeof(SizeOut) < sizeof(SizeIn))
:
(
(sizeof(SizeOut) < sizeof(SizeIn))
||
(
(sizeof(SizeOut) == sizeof(SizeIn))
&&
(std::is_signed<SizeOut>::value && std::is_unsigned<SizeIn>::value)
)
),
std::true_type,
std::false_type
>::type
{
static_assert(std::is_integral<SizeIn >::value, "must be integral type");
static_assert(std::is_integral<SizeOut>::value, "must be integral type");
};
/** when SizeOut is wider than SizeIn, assignment can occur without reservations */
template<class SizeOut, class SizeIn>
C4_ALWAYS_INLINE
typename std::enable_if< ! is_narrower_size<SizeOut, SizeIn>::value, SizeOut>::type
szconv(SizeIn sz) noexcept
{
return static_cast<SizeOut>(sz);
}
/** when SizeOut is narrower than SizeIn, narrowing will occur, so we check
* for overflow. Note that this check is done only if C4_XASSERT is enabled.
* @see C4_XASSERT */
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
{
C4_XASSERT(sz >= 0);
C4_XASSERT_MSG((SizeIn)sz <= (SizeIn)std::numeric_limits<SizeOut>::max(), "size conversion overflow: in=%zu", (size_t)sz);
SizeOut szo = static_cast<SizeOut>(sz);
return szo;
}
C4_SUPPRESS_WARNING_GCC_CLANG_POP
} // namespace c4
#endif /* _C4_SZCONV_HPP_ */

View File

@ -0,0 +1,503 @@
#ifndef _C4_TYPES_HPP_
#define _C4_TYPES_HPP_
#include <stdint.h>
#include <stddef.h>
#include <type_traits>
#if __cplusplus >= 201103L
#include <utility> // for integer_sequence and friends
#endif
#include "c4/preprocessor.hpp"
#include "c4/language.hpp"
/** @file types.hpp basic types, and utility macros and traits for types.
* @ingroup basic_headers */
/** @defgroup types Type utilities */
namespace c4 {
/** @defgroup intrinsic_types Intrinsic types
* @ingroup types
* @{ */
using cbyte = const char; /**< a constant byte */
using byte = char; /**< a mutable byte */
using i8 = int8_t;
using i16 = int16_t;
using i32 = int32_t;
using i64 = int64_t;
using u8 = uint8_t;
using u16 = uint16_t;
using u32 = uint32_t;
using u64 = uint64_t;
using f32 = float;
using f64 = double;
using ssize_t = typename std::make_signed<size_t>::type;
/** @} */
//--------------------------------------------------
/** @defgroup utility_types Utility types
* @ingroup types
* @{ */
// some tag types
#if !defined(__clang__) && defined(__GNUC__)
#pragma GCC diagnostic push
#if __GNUC__ >= 6
#pragma GCC diagnostic ignored "-Wunused-const-variable"
#endif
#endif
/** a tag type for initializing the containers with variadic arguments a la
* initializer_list, minus the initializer_list overload problems.
*/
struct aggregate_t {};
/** @see aggregate_t */
constexpr const aggregate_t aggregate{};
/** a tag type for specifying the initial capacity of allocatable contiguous storage */
struct with_capacity_t {};
/** @see with_capacity_t */
constexpr const with_capacity_t with_capacity{};
/** a tag type for disambiguating template parameter packs in variadic template overloads */
struct varargs_t {};
/** @see with_capacity_t */
constexpr const varargs_t varargs{};
#if !defined(__clang__) && defined(__GNUC__)
#pragma GCC diagnostic pop
#endif
//--------------------------------------------------
/** whether a value should be used in place of a const-reference in argument passing. */
template<class T>
struct cref_uses_val
{
enum { value = (
std::is_scalar<T>::value
||
(
#if C4_CPP >= 20
(std::is_trivially_copyable<T>::value && std::is_standard_layout<T>::value)
#else
std::is_pod<T>::value
#endif
&&
sizeof(T) <= sizeof(size_t))) };
};
/** utility macro to override the default behaviour for c4::fastcref<T>
@see fastcref */
#define C4_CREF_USES_VAL(T) \
template<> \
struct cref_uses_val<T> \
{ \
enum { value = true }; \
};
/** Whether to use pass-by-value or pass-by-const-reference in a function argument
* or return type. */
template<class T>
using fastcref = typename std::conditional<c4::cref_uses_val<T>::value, T, T const&>::type;
//--------------------------------------------------
/** Just what its name says. Useful sometimes as a default empty policy class. */
struct EmptyStruct
{
template<class... T> EmptyStruct(T && ...){}
};
/** Just what its name says. Useful sometimes as a default policy class to
* be inherited from. */
struct EmptyStructVirtual
{
virtual ~EmptyStructVirtual() = default;
template<class... T> EmptyStructVirtual(T && ...){}
};
/** */
template<class T>
struct inheritfrom : public T {};
//--------------------------------------------------
// Utilities to make a class obey size restrictions (eg, min size or size multiple of).
// DirectX usually makes this restriction with uniform buffers.
// This is also useful for padding to prevent false-sharing.
/** how many bytes must be added to size such that the result is at least minsize? */
C4_ALWAYS_INLINE constexpr size_t min_remainder(size_t size, size_t minsize) noexcept
{
return size < minsize ? minsize-size : 0;
}
/** how many bytes must be added to size such that the result is a multiple of multipleof? */
C4_ALWAYS_INLINE constexpr size_t mult_remainder(size_t size, size_t multipleof) noexcept
{
return (((size % multipleof) != 0) ? (multipleof-(size % multipleof)) : 0);
}
/* force the following class to be tightly packed. */
#pragma pack(push, 1)
/** pad a class with more bytes at the end.
* @see http://stackoverflow.com/questions/21092415/force-c-structure-to-pack-tightly */
template<class T, size_t BytesToPadAtEnd>
struct Padded : public T
{
using T::T;
using T::operator=;
Padded(T const& val) : T(val) {}
Padded(T && val) : T(val) {}
char ___c4padspace___[BytesToPadAtEnd];
};
#pragma pack(pop)
/** When the padding argument is 0, we cannot declare the char[] array. */
template<class T>
struct Padded<T, 0> : public T
{
using T::T;
using T::operator=;
Padded(T const& val) : T(val) {}
Padded(T && val) : T(val) {}
};
/** make T have a size which is at least Min bytes */
template<class T, size_t Min>
using MinSized = Padded<T, min_remainder(sizeof(T), Min)>;
/** make T have a size which is a multiple of Mult bytes */
template<class T, size_t Mult>
using MultSized = Padded<T, mult_remainder(sizeof(T), Mult)>;
/** make T have a size which is simultaneously:
* -bigger or equal than Min
* -a multiple of Mult */
template<class T, size_t Min, size_t Mult>
using MinMultSized = MultSized<MinSized<T, Min>, Mult>;
/** make T be suitable for use as a uniform buffer. (at least with DirectX). */
template<class T>
using UbufSized = MinMultSized<T, 64, 16>;
//-----------------------------------------------------------------------------
#define C4_NO_COPY_CTOR(ty) ty(ty const&) = delete
#define C4_NO_MOVE_CTOR(ty) ty(ty &&) = delete
#define C4_NO_COPY_ASSIGN(ty) ty& operator=(ty const&) = delete
#define C4_NO_MOVE_ASSIGN(ty) ty& operator=(ty &&) = delete
#define C4_DEFAULT_COPY_CTOR(ty) ty(ty const&) noexcept = default
#define C4_DEFAULT_MOVE_CTOR(ty) ty(ty &&) noexcept = default
#define C4_DEFAULT_COPY_ASSIGN(ty) ty& operator=(ty const&) noexcept = default
#define C4_DEFAULT_MOVE_ASSIGN(ty) ty& operator=(ty &&) noexcept = default
#define C4_NO_COPY_OR_MOVE_CTOR(ty) \
C4_NO_COPY_CTOR(ty); \
C4_NO_MOVE_CTOR(ty)
#define C4_NO_COPY_OR_MOVE_ASSIGN(ty) \
C4_NO_COPY_ASSIGN(ty); \
C4_NO_MOVE_ASSIGN(ty)
#define C4_NO_COPY_OR_MOVE(ty) \
C4_NO_COPY_OR_MOVE_CTOR(ty); \
C4_NO_COPY_OR_MOVE_ASSIGN(ty)
#define C4_DEFAULT_COPY_AND_MOVE_CTOR(ty) \
C4_DEFAULT_COPY_CTOR(ty); \
C4_DEFAULT_MOVE_CTOR(ty)
#define C4_DEFAULT_COPY_AND_MOVE_ASSIGN(ty) \
C4_DEFAULT_COPY_ASSIGN(ty); \
C4_DEFAULT_MOVE_ASSIGN(ty)
#define C4_DEFAULT_COPY_AND_MOVE(ty) \
C4_DEFAULT_COPY_AND_MOVE_CTOR(ty); \
C4_DEFAULT_COPY_AND_MOVE_ASSIGN(ty)
/** @see https://en.cppreference.com/w/cpp/named_req/TriviallyCopyable */
#define C4_MUST_BE_TRIVIAL_COPY(ty) \
static_assert(std::is_trivially_copyable<ty>::value, #ty " must be trivially copyable")
/** @} */
//-----------------------------------------------------------------------------
/** @defgroup traits_types Type traits utilities
* @ingroup types
* @{ */
// http://stackoverflow.com/questions/10821380/is-t-an-instance-of-a-template-in-c
template<template<typename...> class X, typename T> struct is_instance_of_tpl : std::false_type {};
template<template<typename...> class X, typename... Y> struct is_instance_of_tpl<X, X<Y...>> : std::true_type {};
//-----------------------------------------------------------------------------
/** SFINAE. use this macro to enable a template function overload
based on a compile-time condition.
@code
// define an overload for a non-pod type
template<class T, C4_REQUIRE_T(std::is_pod<T>::value)>
void foo() { std::cout << "pod type\n"; }
// define an overload for a non-pod type
template<class T, C4_REQUIRE_T(!std::is_pod<T>::value)>
void foo() { std::cout << "nonpod type\n"; }
struct non_pod
{
non_pod() : name("asdfkjhasdkjh") {}
const char *name;
};
int main()
{
foo<float>(); // prints "pod type"
foo<non_pod>(); // prints "nonpod type"
}
@endcode */
#define C4_REQUIRE_T(cond) typename std::enable_if<cond, bool>::type* = nullptr
/** enable_if for a return type
* @see C4_REQUIRE_T */
#define C4_REQUIRE_R(cond, type_) typename std::enable_if<cond, type_>::type
//-----------------------------------------------------------------------------
/** define a traits class reporting whether a type provides a member typedef */
#define C4_DEFINE_HAS_TYPEDEF(member_typedef) \
template<typename T> \
struct has_##stype \
{ \
private: \
\
typedef char yes; \
typedef struct { char array[2]; } no; \
\
template<typename C> \
static yes _test(typename C::member_typedef*); \
\
template<typename C> \
static no _test(...); \
\
public: \
\
enum { value = (sizeof(_test<T>(0)) == sizeof(yes)) }; \
\
}
/** @} */
//-----------------------------------------------------------------------------
/** @defgroup type_declarations Type declaration utilities
* @ingroup types
* @{ */
#define _c4_DEFINE_ARRAY_TYPES_WITHOUT_ITERATOR(T, I) \
\
using size_type = I; \
using ssize_type = typename std::make_signed<I>::type; \
using difference_type = typename std::make_signed<I>::type; \
\
using value_type = T; \
using pointer = T*; \
using const_pointer = T const*; \
using reference = T&; \
using const_reference = T const&
#define _c4_DEFINE_TUPLE_ARRAY_TYPES_WITHOUT_ITERATOR(interior_types, I) \
\
using size_type = I; \
using ssize_type = typename std::make_signed<I>::type; \
using difference_type = typename std::make_signed<I>::type; \
\
template<I n> using value_type = typename std::tuple_element< n, std::tuple<interior_types...>>::type; \
template<I n> using pointer = value_type<n>*; \
template<I n> using const_pointer = value_type<n> const*; \
template<I n> using reference = value_type<n>&; \
template<I n> using const_reference = value_type<n> const&
#define _c4_DEFINE_ARRAY_TYPES(T, I) \
\
_c4_DEFINE_ARRAY_TYPES_WITHOUT_ITERATOR(T, I); \
\
using iterator = T*; \
using const_iterator = T const*; \
using reverse_iterator = std::reverse_iterator<T*>; \
using const_reverse_iterator = std::reverse_iterator<T const*>
#define _c4_DEFINE_TUPLE_ARRAY_TYPES(interior_types, I) \
\
_c4_DEFINE_TUPLE_ARRAY_TYPES_WITHOUT_ITERATOR(interior_types, I); \
\
template<I n> using iterator = value_type<n>*; \
template<I n> using const_iterator = value_type<n> const*; \
template<I n> using reverse_iterator = std::reverse_iterator< value_type<n>*>; \
template<I n> using const_reverse_iterator = std::reverse_iterator< value_type<n> const*>
/** @} */
//-----------------------------------------------------------------------------
/** @defgroup compatility_utilities Backport implementation of some Modern C++ utilities
* @ingroup types
* @{ */
//-----------------------------------------------------------------------------
// index_sequence and friends are available only for C++14 and later.
// A C++11 implementation is provided here.
// This implementation was copied over from clang.
// see http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/utility?revision=211563&view=markup#l687
#if __cplusplus > 201103L
using std::integer_sequence;
using std::index_sequence;
using std::make_integer_sequence;
using std::make_index_sequence;
using std::index_sequence_for;
#else
/** C++11 implementation of integer sequence
* @see https://en.cppreference.com/w/cpp/utility/integer_sequence
* @see taken from clang: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/utility?revision=211563&view=markup#l687 */
template<class _Tp, _Tp... _Ip>
struct integer_sequence
{
static_assert(std::is_integral<_Tp>::value,
"std::integer_sequence can only be instantiated with an integral type" );
using value_type = _Tp;
static constexpr size_t size() noexcept { return sizeof...(_Ip); }
};
/** C++11 implementation of index sequence
* @see https://en.cppreference.com/w/cpp/utility/integer_sequence
* @see taken from clang: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/utility?revision=211563&view=markup#l687 */
template<size_t... _Ip>
using index_sequence = integer_sequence<size_t, _Ip...>;
/** @cond DONT_DOCUMENT_THIS */
namespace __detail {
template<typename _Tp, size_t ..._Extra>
struct __repeat;
template<typename _Tp, _Tp ..._Np, size_t ..._Extra>
struct __repeat<integer_sequence<_Tp, _Np...>, _Extra...>
{
using type = integer_sequence<_Tp,
_Np...,
sizeof...(_Np) + _Np...,
2 * sizeof...(_Np) + _Np...,
3 * sizeof...(_Np) + _Np...,
4 * sizeof...(_Np) + _Np...,
5 * sizeof...(_Np) + _Np...,
6 * sizeof...(_Np) + _Np...,
7 * sizeof...(_Np) + _Np...,
_Extra...>;
};
template<size_t _Np> struct __parity;
template<size_t _Np> struct __make : __parity<_Np % 8>::template __pmake<_Np> {};
template<> struct __make<0> { using type = integer_sequence<size_t>; };
template<> struct __make<1> { using type = integer_sequence<size_t, 0>; };
template<> struct __make<2> { using type = integer_sequence<size_t, 0, 1>; };
template<> struct __make<3> { using type = integer_sequence<size_t, 0, 1, 2>; };
template<> struct __make<4> { using type = integer_sequence<size_t, 0, 1, 2, 3>; };
template<> struct __make<5> { using type = integer_sequence<size_t, 0, 1, 2, 3, 4>; };
template<> struct __make<6> { using type = integer_sequence<size_t, 0, 1, 2, 3, 4, 5>; };
template<> struct __make<7> { using type = integer_sequence<size_t, 0, 1, 2, 3, 4, 5, 6>; };
template<> struct __parity<0> { template<size_t _Np> struct __pmake : __repeat<typename __make<_Np / 8>::type> {}; };
template<> struct __parity<1> { template<size_t _Np> struct __pmake : __repeat<typename __make<_Np / 8>::type, _Np - 1> {}; };
template<> struct __parity<2> { template<size_t _Np> struct __pmake : __repeat<typename __make<_Np / 8>::type, _Np - 2, _Np - 1> {}; };
template<> struct __parity<3> { template<size_t _Np> struct __pmake : __repeat<typename __make<_Np / 8>::type, _Np - 3, _Np - 2, _Np - 1> {}; };
template<> struct __parity<4> { template<size_t _Np> struct __pmake : __repeat<typename __make<_Np / 8>::type, _Np - 4, _Np - 3, _Np - 2, _Np - 1> {}; };
template<> struct __parity<5> { template<size_t _Np> struct __pmake : __repeat<typename __make<_Np / 8>::type, _Np - 5, _Np - 4, _Np - 3, _Np - 2, _Np - 1> {}; };
template<> struct __parity<6> { template<size_t _Np> struct __pmake : __repeat<typename __make<_Np / 8>::type, _Np - 6, _Np - 5, _Np - 4, _Np - 3, _Np - 2, _Np - 1> {}; };
template<> struct __parity<7> { template<size_t _Np> struct __pmake : __repeat<typename __make<_Np / 8>::type, _Np - 7, _Np - 6, _Np - 5, _Np - 4, _Np - 3, _Np - 2, _Np - 1> {}; };
template<typename _Tp, typename _Up>
struct __convert
{
template<typename> struct __result;
template<_Tp ..._Np> struct __result<integer_sequence<_Tp, _Np...>>
{
using type = integer_sequence<_Up, _Np...>;
};
};
template<typename _Tp>
struct __convert<_Tp, _Tp>
{
template<typename _Up> struct __result
{
using type = _Up;
};
};
template<typename _Tp, _Tp _Np>
using __make_integer_sequence_unchecked = typename __detail::__convert<size_t, _Tp>::template __result<typename __detail::__make<_Np>::type>::type;
template<class _Tp, _Tp _Ep>
struct __make_integer_sequence
{
static_assert(std::is_integral<_Tp>::value,
"std::make_integer_sequence can only be instantiated with an integral type" );
static_assert(0 <= _Ep, "std::make_integer_sequence input shall not be negative");
typedef __make_integer_sequence_unchecked<_Tp, _Ep> type;
};
} // namespace __detail
/** @endcond */
/** C++11 implementation of index sequence
* @see https://en.cppreference.com/w/cpp/utility/integer_sequence
* @see taken from clang: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/utility?revision=211563&view=markup#l687 */
template<class _Tp, _Tp _Np>
using make_integer_sequence = typename __detail::__make_integer_sequence<_Tp, _Np>::type;
/** C++11 implementation of index sequence
* @see https://en.cppreference.com/w/cpp/utility/integer_sequence
* @see taken from clang: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/utility?revision=211563&view=markup#l687 */
template<size_t _Np>
using make_index_sequence = make_integer_sequence<size_t, _Np>;
/** C++11 implementation of index sequence
* @see https://en.cppreference.com/w/cpp/utility/integer_sequence
* @see taken from clang: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/utility?revision=211563&view=markup#l687 */
template<class... _Tp>
using index_sequence_for = make_index_sequence<sizeof...(_Tp)>;
#endif
/** @} */
} // namespace c4
#endif /* _C4_TYPES_HPP_ */

View File

@ -0,0 +1,16 @@
#ifndef C4_UTF_HPP_
#define C4_UTF_HPP_
#include "c4/language.hpp"
#include "c4/substr_fwd.hpp"
#include <stddef.h>
#include <stdint.h>
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);
} // namespace c4
#endif // C4_UTF_HPP_

View File

@ -0,0 +1,10 @@
#ifndef _C4_WINDOWS_HPP_
#define _C4_WINDOWS_HPP_
#if defined(_WIN64) || defined(_WIN32)
#include "c4/windows_push.hpp"
#include <windows.h>
#include "c4/windows_pop.hpp"
#endif
#endif /* _C4_WINDOWS_HPP_ */

View File

@ -0,0 +1,41 @@
#ifndef _C4_WINDOWS_POP_HPP_
#define _C4_WINDOWS_POP_HPP_
#if defined(_WIN64) || defined(_WIN32)
#ifdef _c4_AMD64_
# undef _c4_AMD64_
# undef _AMD64_
#endif
#ifdef _c4_X86_
# undef _c4_X86_
# undef _X86_
#endif
#ifdef _c4_ARM_
# undef _c4_ARM_
# undef _ARM_
#endif
#ifdef _c4_NOMINMAX
# undef _c4_NOMINMAX
# undef NOMINMAX
#endif
#ifdef NOGDI
# undef _c4_NOGDI
# undef NOGDI
#endif
#ifdef VC_EXTRALEAN
# undef _c4_VC_EXTRALEAN
# undef VC_EXTRALEAN
#endif
#ifdef WIN32_LEAN_AND_MEAN
# undef _c4_WIN32_LEAN_AND_MEAN
# undef WIN32_LEAN_AND_MEAN
#endif
#endif /* defined(_WIN64) || defined(_WIN32) */
#endif /* _C4_WINDOWS_POP_HPP_ */

View File

@ -0,0 +1,102 @@
#ifndef _C4_WINDOWS_PUSH_HPP_
#define _C4_WINDOWS_PUSH_HPP_
/** @file windows_push.hpp sets up macros to include windows header files
* without pulling in all of <windows.h>
*
* @see #include windows_pop.hpp to undefine these macros
*
* @see https://aras-p.info/blog/2018/01/12/Minimizing-windows.h/ */
#if defined(_WIN64) || defined(_WIN32)
#if defined(_M_AMD64)
# ifndef _AMD64_
# define _c4_AMD64_
# define _AMD64_
# endif
#elif defined(_M_IX86)
# ifndef _X86_
# define _c4_X86_
# define _X86_
# endif
#elif defined(_M_ARM64)
# ifndef _ARM64_
# define _c4_ARM64_
# define _ARM64_
# endif
#elif defined(_M_ARM)
# ifndef _ARM_
# define _c4_ARM_
# define _ARM_
# endif
#endif
#ifndef NOMINMAX
# define _c4_NOMINMAX
# define NOMINMAX
#endif
#ifndef NOGDI
# define _c4_NOGDI
# define NOGDI
#endif
#ifndef VC_EXTRALEAN
# define _c4_VC_EXTRALEAN
# define VC_EXTRALEAN
#endif
#ifndef WIN32_LEAN_AND_MEAN
# define _c4_WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
#endif
/* If defined, the following flags inhibit definition
* of the indicated items.
*
* NOGDICAPMASKS - CC_*, LC_*, PC_*, CP_*, TC_*, RC_
* NOVIRTUALKEYCODES - VK_*
* NOWINMESSAGES - WM_*, EM_*, LB_*, CB_*
* NOWINSTYLES - WS_*, CS_*, ES_*, LBS_*, SBS_*, CBS_*
* NOSYSMETRICS - SM_*
* NOMENUS - MF_*
* NOICONS - IDI_*
* NOKEYSTATES - MK_*
* NOSYSCOMMANDS - SC_*
* NORASTEROPS - Binary and Tertiary raster ops
* NOSHOWWINDOW - SW_*
* OEMRESOURCE - OEM Resource values
* NOATOM - Atom Manager routines
* NOCLIPBOARD - Clipboard routines
* NOCOLOR - Screen colors
* NOCTLMGR - Control and Dialog routines
* NODRAWTEXT - DrawText() and DT_*
* NOGDI - All GDI defines and routines
* NOKERNEL - All KERNEL defines and routines
* NOUSER - All USER defines and routines
* NONLS - All NLS defines and routines
* NOMB - MB_* and MessageBox()
* NOMEMMGR - GMEM_*, LMEM_*, GHND, LHND, associated routines
* NOMETAFILE - typedef METAFILEPICT
* NOMINMAX - Macros min(a,b) and max(a,b)
* NOMSG - typedef MSG and associated routines
* NOOPENFILE - OpenFile(), OemToAnsi, AnsiToOem, and OF_*
* NOSCROLL - SB_* and scrolling routines
* NOSERVICE - All Service Controller routines, SERVICE_ equates, etc.
* NOSOUND - Sound driver routines
* NOTEXTMETRIC - typedef TEXTMETRIC and associated routines
* NOWH - SetWindowsHook and WH_*
* NOWINOFFSETS - GWL_*, GCL_*, associated routines
* NOCOMM - COMM driver routines
* NOKANJI - Kanji support stuff.
* NOHELP - Help engine interface.
* NOPROFILER - Profiler interface.
* NODEFERWINDOWPOS - DeferWindowPos routines
* NOMCX - Modem Configuration Extensions
*/
#endif /* defined(_WIN64) || defined(_WIN32) */
#endif /* _C4_WINDOWS_PUSH_HPP_ */

View File

@ -0,0 +1,282 @@
#ifndef _C4_YML_COMMON_HPP_
#define _C4_YML_COMMON_HPP_
#include <cstddef>
#include <c4/substr.hpp>
#include <c4/yml/export.hpp>
#ifndef RYML_USE_ASSERT
# define RYML_USE_ASSERT C4_USE_ASSERT
#endif
#if RYML_USE_ASSERT
# define RYML_ASSERT(cond) RYML_CHECK(cond)
# define RYML_ASSERT_MSG(cond, msg) RYML_CHECK_MSG(cond, msg)
#else
# define RYML_ASSERT(cond)
# define RYML_ASSERT_MSG(cond, msg)
#endif
#if defined(NDEBUG) || defined(C4_NO_DEBUG_BREAK)
# define RYML_DEBUG_BREAK()
#else
# define RYML_DEBUG_BREAK() \
{ \
if(c4::get_error_flags() & c4::ON_ERROR_DEBUGBREAK) \
{ \
C4_DEBUG_BREAK(); \
} \
}
#endif
#define RYML_CHECK(cond) \
do { \
if(!(cond)) \
{ \
RYML_DEBUG_BREAK() \
c4::yml::error("check failed: " #cond, c4::yml::Location(__FILE__, __LINE__, 0)); \
} \
} while(0)
#define RYML_CHECK_MSG(cond, msg) \
do \
{ \
if(!(cond)) \
{ \
RYML_DEBUG_BREAK() \
c4::yml::error(msg ": check failed: " #cond, c4::yml::Location(__FILE__, __LINE__, 0)); \
} \
} while(0)
#if C4_CPP >= 14
# define RYML_DEPRECATED(msg) [[deprecated(msg)]]
#else
# if defined(_MSC_VER)
# define RYML_DEPRECATED(msg) __declspec(deprecated(msg))
# else // defined(__GNUC__) || defined(__clang__)
# define RYML_DEPRECATED(msg) __attribute__((deprecated(msg)))
# endif
#endif
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
namespace c4 {
namespace yml {
C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wold-style-cast")
enum : size_t {
/** a null position */
npos = size_t(-1),
/** an index to none */
NONE = size_t(-1)
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//! holds a position into a source buffer
struct RYML_EXPORT LineCol
{
//! number of bytes from the beginning of the source buffer
size_t offset;
//! line
size_t line;
//! column
size_t col;
LineCol() : offset(), line(), col() {}
//! 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) {}
};
//! a source file position
struct RYML_EXPORT Location : public LineCol
{
csubstr name;
operator bool () const { return !name.empty() || line != 0 || offset != 0; }
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)) {}
};
//-----------------------------------------------------------------------------
/** the type of the function used to report errors. This function must
* interrupt execution, either by raising an exception or calling
* std::abort().
*
* @warning the error callback must never return: it must either abort
* or throw an exception. Otherwise, the parser will enter into an
* infinite loop, or the program may crash. */
using pfn_error = void (*)(const char* msg, size_t msg_len, Location location, void *user_data);
/** the type of the function used to allocate memory */
using pfn_allocate = void* (*)(size_t len, void* hint, void *user_data);
/** the type of the function used to free memory */
using pfn_free = void (*)(void* mem, size_t size, void *user_data);
/** trigger an error: call the current error callback. */
RYML_EXPORT void error(const char *msg, size_t msg_len, Location loc);
/** @overload error */
inline void error(const char *msg, size_t msg_len)
{
error(msg, msg_len, Location{});
}
/** @overload error */
template<size_t N>
inline void error(const char (&msg)[N], Location loc)
{
error(msg, N-1, loc);
}
/** @overload error */
template<size_t N>
inline void error(const char (&msg)[N])
{
error(msg, N-1, Location{});
}
//-----------------------------------------------------------------------------
/** a c-style callbacks class
*
* @warning the error callback must never return: it must either abort
* or throw an exception. Otherwise, the parser will enter into an
* infinite loop, or the program may crash. */
struct RYML_EXPORT Callbacks
{
void * m_user_data;
pfn_allocate m_allocate;
pfn_free m_free;
pfn_error m_error;
Callbacks();
Callbacks(void *user_data, pfn_allocate alloc, pfn_free free, pfn_error error_);
bool operator!= (Callbacks const& that) const { return !operator==(that); }
bool operator== (Callbacks const& that) const
{
return (m_user_data == that.m_user_data &&
m_allocate == that.m_allocate &&
m_free == that.m_free &&
m_error == that.m_error);
}
};
/** set the global callbacks.
*
* @warning the error callback must never return: it must either abort
* or throw an exception. Otherwise, the parser will enter into an
* infinite loop, or the program may crash. */
RYML_EXPORT void set_callbacks(Callbacks const& c);
/// get the global callbacks
RYML_EXPORT Callbacks const& get_callbacks();
/// set the global callbacks back to their defaults
RYML_EXPORT void reset_callbacks();
/// @cond dev
#define _RYML_CB_ERR(cb, msg_literal) \
do \
{ \
const char msg[] = msg_literal; \
RYML_DEBUG_BREAK() \
(cb).m_error(msg, sizeof(msg), c4::yml::Location(__FILE__, 0, __LINE__, 0), (cb).m_user_data); \
} while(0)
#define _RYML_CB_CHECK(cb, cond) \
do \
{ \
if(!(cond)) \
{ \
const char msg[] = "check failed: " #cond; \
RYML_DEBUG_BREAK() \
(cb).m_error(msg, sizeof(msg), c4::yml::Location(__FILE__, 0, __LINE__, 0), (cb).m_user_data); \
} \
} while(0)
#ifdef RYML_USE_ASSERT
#define _RYML_CB_ASSERT(cb, cond) _RYML_CB_CHECK((cb), (cond))
#else
#define _RYML_CB_ASSERT(cb, cond) do {} while(0)
#endif
#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_FREE(cb, buf, T, num) \
do { \
(cb).m_free((buf), (num) * sizeof(T), (cb).m_user_data); \
(buf) = nullptr; \
} while(0)
namespace detail {
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<uint8_t, unsignedval>>::type
{};
#define _RYML_CHCONST(signedval, unsignedval) ::c4::yml::detail::_charconstant_t<INT8_C(signedval), UINT8_C(unsignedval)>::value
} // namespace detail
namespace detail {
struct _SubstrWriter
{
substr buf;
size_t pos;
_SubstrWriter(substr buf_, size_t pos_=0) : buf(buf_), pos(pos_) {}
void append(csubstr s)
{
C4_ASSERT(!s.overlaps(buf));
if(pos + s.len <= buf.len)
memcpy(buf.str + pos, s.str, s.len);
pos += s.len;
}
void append(char c)
{
if(pos < buf.len)
buf.str[pos] = c;
++pos;
}
void append_n(char c, size_t numtimes)
{
if(pos + numtimes < buf.len)
memset(buf.str + pos, c, numtimes);
pos += numtimes;
}
size_t slack() const { return pos <= buf.len ? buf.len - pos : 0; }
size_t excess() const { return pos > buf.len ? pos - buf.len : 0; }
//! 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); }
size_t advance(size_t more) { pos += more; return pos; }
};
} // namespace detail
/// @endcond
C4_SUPPRESS_WARNING_GCC_CLANG_POP
} // namespace yml
} // namespace c4
#endif /* _C4_YML_COMMON_HPP_ */

View File

@ -0,0 +1,137 @@
#ifndef _C4_YML_DETAIL_PARSER_DBG_HPP_
#define _C4_YML_DETAIL_PARSER_DBG_HPP_
#ifndef _C4_YML_COMMON_HPP_
#include "../common.hpp"
#endif
#include <cstdio>
//-----------------------------------------------------------------------------
// some debugging scaffolds
#if defined(_MSC_VER)
# pragma warning(push)
# pragma warning(disable: 4068/*unknown pragma*/)
#endif
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunknown-pragmas"
//#pragma GCC diagnostic ignored "-Wpragma-system-header-outside-header"
#pragma GCC system_header
#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")
# define _c4err(fmt, ...) \
do { if(c4::is_debugger_attached()) { C4_DEBUG_BREAK(); } \
this->_err("ERROR:\n" "{}:{}: " fmt, __FILE__, __LINE__, ## __VA_ARGS__); } while(0)
#else
# define _c4dbgt(fmt, ...)
# define _c4dbgpf(fmt, ...)
# define _c4dbgp(msg)
# define _c4dbgq(msg)
# define _c4err(fmt, ...) \
do { if(c4::is_debugger_attached()) { C4_DEBUG_BREAK(); } \
this->_err("ERROR: " fmt, ## __VA_ARGS__); } while(0)
#endif
#define _c4prsp(sp) sp
#define _c4presc(s) __c4presc(s.str, s.len)
inline c4::csubstr _c4prc(const char &C4_RESTRICT c)
{
switch(c)
{
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);
}
}
inline void __c4presc(const char *s, size_t len)
{
size_t prev = 0;
for(size_t i = 0; i < len; ++i)
{
switch(s[i])
{
case '\n' : fwrite(s+prev, 1, i-prev, stdout); putchar('\\'); putchar('n'); putchar('\n'); prev = i+1; break;
case '\t' : fwrite(s+prev, 1, i-prev, stdout); putchar('\\'); putchar('t'); prev = i+1; break;
case '\0' : fwrite(s+prev, 1, i-prev, stdout); putchar('\\'); putchar('0'); prev = i+1; break;
case '\r' : fwrite(s+prev, 1, i-prev, stdout); putchar('\\'); putchar('r'); prev = i+1; break;
case '\f' : fwrite(s+prev, 1, i-prev, stdout); putchar('\\'); putchar('f'); prev = i+1; break;
case '\b' : fwrite(s+prev, 1, i-prev, stdout); putchar('\\'); putchar('b'); prev = i+1; break;
case '\v' : fwrite(s+prev, 1, i-prev, stdout); putchar('\\'); putchar('v'); prev = i+1; break;
case '\a' : fwrite(s+prev, 1, i-prev, stdout); putchar('\\'); putchar('a'); prev = i+1; break;
case '\x1b': fwrite(s+prev, 1, i-prev, stdout); putchar('\\'); putchar('e'); prev = i+1; break;
case -0x3e/*0xc2u*/:
if(i+1 < len)
{
if(s[i+1] == -0x60/*0xa0u*/)
{
fwrite(s+prev, 1, i-prev, stdout); putchar('\\'); putchar('_'); prev = i+2; ++i;
}
else if(s[i+1] == -0x7b/*0x85u*/)
{
fwrite(s+prev, 1, i-prev, stdout); putchar('\\'); putchar('N'); prev = i+2; ++i;
}
break;
}
case -0x1e/*0xe2u*/:
if(i+2 < len && s[i+1] == -0x80/*0x80u*/)
{
if(s[i+2] == -0x58/*0xa8u*/)
{
fwrite(s+prev, 1, i-prev, stdout); putchar('\\'); putchar('L'); prev = i+3; i += 2;
}
else if(s[i+2] == -0x57/*0xa9u*/)
{
fwrite(s+prev, 1, i-prev, stdout); putchar('\\'); putchar('P'); prev = i+3; i += 2;
}
break;
}
}
}
fwrite(s + prev, 1, len - prev, stdout);
}
#pragma clang diagnostic pop
#pragma GCC diagnostic pop
#if defined(_MSC_VER)
# pragma warning(pop)
#endif
#endif /* _C4_YML_DETAIL_PARSER_DBG_HPP_ */

View File

@ -0,0 +1,274 @@
#ifndef _C4_YML_DETAIL_STACK_HPP_
#define _C4_YML_DETAIL_STACK_HPP_
#ifndef _C4_YML_COMMON_HPP_
#include "../common.hpp"
#endif
#ifdef RYML_DBG
# include <type_traits>
#endif
#include <string.h>
namespace c4 {
namespace yml {
namespace detail {
/** A lightweight contiguous stack with SSO. This avoids a dependency on std. */
template<class T, size_t 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:
T m_buf[N];
T * m_stack;
size_t m_size;
size_t m_capacity;
Callbacks m_callbacks;
public:
constexpr static bool is_contiguous() { return true; }
stack(Callbacks const& cb)
: m_buf()
, m_stack(m_buf)
, m_size(0)
, m_capacity(N)
, m_callbacks(cb) {}
stack() : stack(get_callbacks()) {}
~stack()
{
_free();
}
stack(stack const& that) noexcept : stack(that.m_callbacks)
{
resize(that.m_size);
_cp(&that);
}
stack(stack &&that) noexcept : stack(that.m_callbacks)
{
_mv(&that);
}
stack& operator= (stack const& that) noexcept
{
_cb(that.m_callbacks);
resize(that.m_size);
_cp(&that);
return *this;
}
stack& operator= (stack &&that) noexcept
{
_cb(that.m_callbacks);
_mv(&that);
return *this;
}
public:
size_t size() const { return m_size; }
size_t empty() const { return m_size == 0; }
size_t capacity() const { return m_capacity; }
void clear()
{
m_size = 0;
}
void resize(size_t sz)
{
reserve(sz);
m_size = sz;
}
void reserve(size_t sz);
void push(T const& C4_RESTRICT n)
{
RYML_ASSERT((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;
reserve(cap);
}
m_stack[m_size] = n;
++m_size;
}
void push_top()
{
RYML_ASSERT(m_size > 0);
if(m_size == m_capacity)
{
size_t cap = m_capacity == 0 ? N : 2 * m_capacity;
reserve(cap);
}
m_stack[m_size] = m_stack[m_size - 1];
++m_size;
}
T const& C4_RESTRICT pop()
{
RYML_ASSERT(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 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 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 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 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]; }
public:
using iterator = T *;
using const_iterator = T const *;
iterator begin() { return m_stack; }
iterator end () { return m_stack + m_size; }
const_iterator begin() const { return (const_iterator)m_stack; }
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);
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
template<class T, size_t N>
void stack<T, N>::reserve(size_t sz)
{
if(sz <= m_size)
return;
if(sz <= N)
{
m_stack = m_buf;
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));
if(m_stack != m_buf)
{
m_callbacks.m_free(m_stack, m_capacity * sizeof(T), m_callbacks.m_user_data);
}
m_stack = buf;
m_capacity = sz;
}
//-----------------------------------------------------------------------------
template<class T, size_t N>
void stack<T, N>::_free()
{
RYML_ASSERT(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_stack = m_buf;
m_size = N;
m_capacity = N;
}
else
{
RYML_ASSERT(m_capacity == N);
}
}
//-----------------------------------------------------------------------------
template<class T, size_t 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);
}
else
{
RYML_ASSERT(that->m_capacity <= N);
RYML_ASSERT(that->m_size <= that->m_capacity);
}
memcpy(m_stack, that->m_stack, that->m_size * sizeof(T));
m_size = that->m_size;
m_capacity = that->m_size < N ? N : that->m_size;
m_callbacks = that->m_callbacks;
}
//-----------------------------------------------------------------------------
template<class T, size_t 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);
m_stack = that->m_stack;
}
else
{
RYML_ASSERT(that->m_capacity <= N);
RYML_ASSERT(that->m_size <= that->m_capacity);
memcpy(m_buf, that->m_buf, that->m_size * sizeof(T));
m_stack = m_buf;
}
m_size = that->m_size;
m_capacity = that->m_capacity;
m_callbacks = that->m_callbacks;
// make sure no deallocation happens on destruction
RYML_ASSERT(that->m_stack != m_buf);
that->m_stack = that->m_buf;
that->m_capacity = N;
that->m_size = 0;
}
//-----------------------------------------------------------------------------
template<class T, size_t N>
void stack<T, N>::_cb(Callbacks const& cb)
{
if(cb != m_callbacks)
{
_free();
m_callbacks = cb;
}
}
} // namespace detail
} // namespace yml
} // namespace c4
#endif /* _C4_YML_DETAIL_STACK_HPP_ */

View File

@ -0,0 +1,960 @@
#ifndef _C4_YML_EMIT_DEF_HPP_
#define _C4_YML_EMIT_DEF_HPP_
#ifndef _C4_YML_EMIT_HPP_
#include "c4/yml/emit.hpp"
#endif
namespace c4 {
namespace yml {
template<class Writer>
substr Emitter<Writer>::emit_as(EmitType_e type, Tree const& t, size_t id, bool error_on_excess)
{
if(t.empty())
{
_RYML_CB_ASSERT(t.callbacks(), id == NONE);
return {};
}
_RYML_CB_CHECK(t.callbacks(), id < t.capacity());
m_tree = &t;
if(type == EMIT_YAML)
_emit_yaml(id);
else if(type == EMIT_JSON)
_do_visit_json(id);
else
_RYML_CB_ERR(m_tree->callbacks(), "unknown emit type");
return this->Writer::_get(error_on_excess);
}
template<class Writer>
substr Emitter<Writer>::emit_as(EmitType_e type, Tree const& t, bool error_on_excess)
{
if(t.empty())
return {};
return this->emit_as(type, t, t.root_id(), error_on_excess);
}
template<class Writer>
substr Emitter<Writer>::emit_as(EmitType_e type, ConstNodeRef const& n, bool error_on_excess)
{
_RYML_CB_CHECK(n.tree()->callbacks(), n.valid());
return this->emit_as(type, *n.tree(), n.id(), error_on_excess);
}
//-----------------------------------------------------------------------------
template<class Writer>
void Emitter<Writer>::_emit_yaml(size_t id)
{
// save branches in the visitor by doing the initial stream/doc
// logic here, sparing the need to check stream/val/keyval inside
// the visitor functions
auto dispatch = [this](size_t node){
NodeType ty = m_tree->type(node);
if(ty.marked_flow_sl())
_do_visit_flow_sl(node, 0);
else if(ty.marked_flow_ml())
_do_visit_flow_ml(node, 0);
else
{
_do_visit_block(node, 0);
}
};
if(!m_tree->is_root(id))
{
if(m_tree->is_container(id) && !m_tree->type(id).marked_flow())
{
size_t ilevel = 0;
if(m_tree->has_key(id))
{
this->Writer::_do_write(m_tree->key(id));
this->Writer::_do_write(":\n");
++ilevel;
}
_do_visit_block_container(id, ilevel, ilevel);
return;
}
}
auto *btd = m_tree->tag_directives().b;
auto *etd = m_tree->tag_directives().e;
auto write_tag_directives = [&btd, etd, this](size_t next_node){
auto end = btd;
while(end < etd)
{
if(end->next_node_id > next_node)
break;
++end;
}
for( ; btd != end; ++btd)
{
if(next_node != m_tree->first_child(m_tree->parent(next_node)))
this->Writer::_do_write("...\n");
this->Writer::_do_write("%TAG ");
this->Writer::_do_write(btd->handle);
this->Writer::_do_write(' ');
this->Writer::_do_write(btd->prefix);
this->Writer::_do_write('\n');
}
};
if(m_tree->is_stream(id))
{
if(m_tree->first_child(id) != NONE)
write_tag_directives(m_tree->first_child(id));
for(size_t child = m_tree->first_child(id); child != NONE; child = m_tree->next_sibling(child))
{
dispatch(child);
if(m_tree->next_sibling(child) != NONE)
write_tag_directives(m_tree->next_sibling(child));
}
}
else if(m_tree->is_container(id))
{
dispatch(id);
}
else if(m_tree->is_doc(id))
{
_RYML_CB_ASSERT(m_tree->callbacks(), !m_tree->is_container(id)); // checked above
_RYML_CB_ASSERT(m_tree->callbacks(), m_tree->is_val(id)); // so it must be a val
_write_doc(id);
}
else if(m_tree->is_keyval(id))
{
_writek(id, 0);
this->Writer::_do_write(": ");
_writev(id, 0);
if(!m_tree->type(id).marked_flow())
this->Writer::_do_write('\n');
}
else if(m_tree->is_val(id))
{
//this->Writer::_do_write("- ");
_writev(id, 0);
if(!m_tree->type(id).marked_flow())
this->Writer::_do_write('\n');
}
else if(m_tree->type(id) == NOTYPE)
{
;
}
else
{
_RYML_CB_ERR(m_tree->callbacks(), "unknown type");
}
}
template<class Writer>
void Emitter<Writer>::_write_doc(size_t id)
{
RYML_ASSERT(m_tree->is_doc(id));
if(!m_tree->is_root(id))
{
RYML_ASSERT(m_tree->is_stream(m_tree->parent(id)));
this->Writer::_do_write("---");
}
if(!m_tree->has_val(id)) // this is more frequent
{
if(m_tree->has_val_tag(id))
{
if(!m_tree->is_root(id))
this->Writer::_do_write(' ');
_write_tag(m_tree->val_tag(id));
}
if(m_tree->has_val_anchor(id))
{
if(!m_tree->is_root(id))
this->Writer::_do_write(' ');
this->Writer::_do_write('&');
this->Writer::_do_write(m_tree->val_anchor(id));
}
}
else // docval
{
RYML_ASSERT(m_tree->has_val(id));
RYML_ASSERT(!m_tree->has_key(id));
if(!m_tree->is_root(id))
this->Writer::_do_write(' ');
_writev(id, 0);
}
this->Writer::_do_write('\n');
}
template<class Writer>
void Emitter<Writer>::_do_visit_flow_sl(size_t node, size_t ilevel)
{
RYML_ASSERT(!m_tree->is_stream(node));
RYML_ASSERT(m_tree->is_container(node) || m_tree->is_doc(node));
RYML_ASSERT(m_tree->is_root(node) || (m_tree->parent_is_map(node) || m_tree->parent_is_seq(node)));
if(m_tree->is_doc(node))
{
_write_doc(node);
if(!m_tree->has_children(node))
return;
}
else if(m_tree->is_container(node))
{
RYML_ASSERT(m_tree->is_map(node) || m_tree->is_seq(node));
bool spc = false; // write a space
if(m_tree->has_key(node))
{
_writek(node, ilevel);
this->Writer::_do_write(':');
spc = true;
}
if(m_tree->has_val_tag(node))
{
if(spc)
this->Writer::_do_write(' ');
_write_tag(m_tree->val_tag(node));
spc = true;
}
if(m_tree->has_val_anchor(node))
{
if(spc)
this->Writer::_do_write(' ');
this->Writer::_do_write('&');
this->Writer::_do_write(m_tree->val_anchor(node));
spc = true;
}
if(spc)
this->Writer::_do_write(' ');
if(m_tree->is_map(node))
{
this->Writer::_do_write('{');
}
else
{
_RYML_CB_ASSERT(m_tree->callbacks(), m_tree->is_seq(node));
this->Writer::_do_write('[');
}
} // container
for(size_t child = m_tree->first_child(node), count = 0; child != NONE; child = m_tree->next_sibling(child))
{
if(count++)
this->Writer::_do_write(',');
if(m_tree->is_keyval(child))
{
_writek(child, ilevel);
this->Writer::_do_write(": ");
_writev(child, ilevel);
}
else if(m_tree->is_val(child))
{
_writev(child, ilevel);
}
else
{
// with single-line flow, we can never go back to block
_do_visit_flow_sl(child, ilevel + 1);
}
}
if(m_tree->is_map(node))
{
this->Writer::_do_write('}');
}
else if(m_tree->is_seq(node))
{
this->Writer::_do_write(']');
}
}
template<class Writer>
void Emitter<Writer>::_do_visit_flow_ml(size_t id, size_t ilevel, size_t do_indent)
{
C4_UNUSED(id);
C4_UNUSED(ilevel);
C4_UNUSED(do_indent);
RYML_CHECK(false/*not implemented*/);
}
template<class Writer>
void Emitter<Writer>::_do_visit_block_container(size_t node, size_t next_level, size_t do_indent)
{
RepC ind = indent_to(do_indent * next_level);
if(m_tree->is_seq(node))
{
for(size_t child = m_tree->first_child(node); child != NONE; child = m_tree->next_sibling(child))
{
_RYML_CB_ASSERT(m_tree->callbacks(), !m_tree->has_key(child));
if(m_tree->is_val(child))
{
this->Writer::_do_write(ind);
this->Writer::_do_write("- ");
_writev(child, next_level);
this->Writer::_do_write('\n');
}
else
{
_RYML_CB_ASSERT(m_tree->callbacks(), m_tree->is_container(child));
NodeType ty = m_tree->type(child);
if(ty.marked_flow_sl())
{
this->Writer::_do_write(ind);
this->Writer::_do_write("- ");
_do_visit_flow_sl(child, 0u);
this->Writer::_do_write('\n');
}
else if(ty.marked_flow_ml())
{
this->Writer::_do_write(ind);
this->Writer::_do_write("- ");
_do_visit_flow_ml(child, next_level, do_indent);
this->Writer::_do_write('\n');
}
else
{
_do_visit_block(child, next_level, do_indent);
}
}
do_indent = true;
ind = indent_to(do_indent * next_level);
}
}
else // map
{
_RYML_CB_ASSERT(m_tree->callbacks(), m_tree->is_map(node));
for(size_t ich = m_tree->first_child(node); ich != NONE; ich = m_tree->next_sibling(ich))
{
_RYML_CB_ASSERT(m_tree->callbacks(), m_tree->has_key(ich));
if(m_tree->is_keyval(ich))
{
this->Writer::_do_write(ind);
_writek(ich, next_level);
this->Writer::_do_write(": ");
_writev(ich, next_level);
this->Writer::_do_write('\n');
}
else
{
_RYML_CB_ASSERT(m_tree->callbacks(), m_tree->is_container(ich));
NodeType ty = m_tree->type(ich);
if(ty.marked_flow_sl())
{
this->Writer::_do_write(ind);
_do_visit_flow_sl(ich, 0u);
this->Writer::_do_write('\n');
}
else if(ty.marked_flow_ml())
{
this->Writer::_do_write(ind);
_do_visit_flow_ml(ich, 0u);
this->Writer::_do_write('\n');
}
else
{
_do_visit_block(ich, next_level, do_indent);
}
}
do_indent = true;
ind = indent_to(do_indent * next_level);
}
}
}
template<class Writer>
void Emitter<Writer>::_do_visit_block(size_t node, size_t ilevel, size_t do_indent)
{
RYML_ASSERT(!m_tree->is_stream(node));
RYML_ASSERT(m_tree->is_container(node) || m_tree->is_doc(node));
RYML_ASSERT(m_tree->is_root(node) || (m_tree->parent_is_map(node) || m_tree->parent_is_seq(node)));
RepC ind = indent_to(do_indent * ilevel);
if(m_tree->is_doc(node))
{
_write_doc(node);
if(!m_tree->has_children(node))
return;
}
else if(m_tree->is_container(node))
{
RYML_ASSERT(m_tree->is_map(node) || m_tree->is_seq(node));
bool spc = false; // write a space
bool nl = false; // write a newline
if(m_tree->has_key(node))
{
this->Writer::_do_write(ind);
_writek(node, ilevel);
this->Writer::_do_write(':');
spc = true;
}
else if(!m_tree->is_root(node))
{
this->Writer::_do_write(ind);
this->Writer::_do_write('-');
spc = true;
}
if(m_tree->has_val_tag(node))
{
if(spc)
this->Writer::_do_write(' ');
_write_tag(m_tree->val_tag(node));
spc = true;
nl = true;
}
if(m_tree->has_val_anchor(node))
{
if(spc)
this->Writer::_do_write(' ');
this->Writer::_do_write('&');
this->Writer::_do_write(m_tree->val_anchor(node));
spc = true;
nl = true;
}
if(m_tree->has_children(node))
{
if(m_tree->has_key(node))
nl = true;
else
if(!m_tree->is_root(node) && !nl)
spc = true;
}
else
{
if(m_tree->is_seq(node))
this->Writer::_do_write(" []\n");
else if(m_tree->is_map(node))
this->Writer::_do_write(" {}\n");
return;
}
if(spc && !nl)
this->Writer::_do_write(' ');
do_indent = 0;
if(nl)
{
this->Writer::_do_write('\n');
do_indent = 1;
}
} // container
size_t next_level = ilevel + 1;
if(m_tree->is_root(node) || m_tree->is_doc(node))
next_level = ilevel; // do not indent at top level
_do_visit_block_container(node, next_level, do_indent);
}
template<class Writer>
void Emitter<Writer>::_do_visit_json(size_t id)
{
_RYML_CB_CHECK(m_tree->callbacks(), !m_tree->is_stream(id)); // JSON does not have streams
if(m_tree->is_keyval(id))
{
_writek_json(id);
this->Writer::_do_write(": ");
_writev_json(id);
}
else if(m_tree->is_val(id))
{
_writev_json(id);
}
else if(m_tree->is_container(id))
{
if(m_tree->has_key(id))
{
_writek_json(id);
this->Writer::_do_write(": ");
}
if(m_tree->is_seq(id))
this->Writer::_do_write('[');
else if(m_tree->is_map(id))
this->Writer::_do_write('{');
} // container
for(size_t ich = m_tree->first_child(id); ich != NONE; ich = m_tree->next_sibling(ich))
{
if(ich != m_tree->first_child(id))
this->Writer::_do_write(',');
_do_visit_json(ich);
}
if(m_tree->is_seq(id))
this->Writer::_do_write(']');
else if(m_tree->is_map(id))
this->Writer::_do_write('}');
}
template<class Writer>
void Emitter<Writer>::_write(NodeScalar const& C4_RESTRICT sc, NodeType flags, size_t ilevel)
{
if( ! sc.tag.empty())
{
_write_tag(sc.tag);
this->Writer::_do_write(' ');
}
if(flags.has_anchor())
{
RYML_ASSERT(flags.is_ref() != flags.has_anchor());
RYML_ASSERT( ! sc.anchor.empty());
this->Writer::_do_write('&');
this->Writer::_do_write(sc.anchor);
this->Writer::_do_write(' ');
}
else if(flags.is_ref())
{
if(sc.anchor != "<<")
this->Writer::_do_write('*');
this->Writer::_do_write(sc.anchor);
return;
}
// ensure the style flags only have one of KEY or VAL
_RYML_CB_ASSERT(m_tree->callbacks(), ((flags & (_WIP_KEY_STYLE|_WIP_VAL_STYLE)) == 0) || (((flags&_WIP_KEY_STYLE) == 0) != ((flags&_WIP_VAL_STYLE) == 0)));
auto style_marks = flags & (_WIP_KEY_STYLE|_WIP_VAL_STYLE);
if(style_marks & (_WIP_KEY_LITERAL|_WIP_VAL_LITERAL))
{
_write_scalar_literal(sc.scalar, ilevel, flags.has_key());
}
else if(style_marks & (_WIP_KEY_FOLDED|_WIP_VAL_FOLDED))
{
_write_scalar_folded(sc.scalar, ilevel, flags.has_key());
}
else if(style_marks & (_WIP_KEY_SQUO|_WIP_VAL_SQUO))
{
_write_scalar_squo(sc.scalar, ilevel);
}
else if(style_marks & (_WIP_KEY_DQUO|_WIP_VAL_DQUO))
{
_write_scalar_dquo(sc.scalar, ilevel);
}
else if(style_marks & (_WIP_KEY_PLAIN|_WIP_VAL_PLAIN))
{
_write_scalar_plain(sc.scalar, ilevel);
}
else if(!style_marks)
{
size_t first_non_nl = sc.scalar.first_not_of('\n');
bool all_newlines = first_non_nl == npos;
bool has_leading_ws = (!all_newlines) && sc.scalar.sub(first_non_nl).begins_with_any(" \t");
bool do_literal = ((!sc.scalar.empty() && all_newlines) || (has_leading_ws && !sc.scalar.trim(' ').empty()));
if(do_literal)
{
_write_scalar_literal(sc.scalar, ilevel, flags.has_key(), /*explicit_indentation*/has_leading_ws);
}
else
{
for(size_t i = 0; i < sc.scalar.len; ++i)
{
if(sc.scalar.str[i] == '\n')
{
_write_scalar_literal(sc.scalar, ilevel, flags.has_key(), /*explicit_indentation*/has_leading_ws);
goto wrote_special;
}
// todo: check for escaped characters requiring double quotes
}
_write_scalar(sc.scalar, flags.is_quoted());
wrote_special:
;
}
}
else
{
_RYML_CB_ERR(m_tree->callbacks(), "not implemented");
}
}
template<class Writer>
void Emitter<Writer>::_write_json(NodeScalar const& C4_RESTRICT sc, NodeType flags)
{
if(C4_UNLIKELY( ! sc.tag.empty()))
_RYML_CB_ERR(m_tree->callbacks(), "JSON does not have tags");
if(C4_UNLIKELY(flags.has_anchor()))
_RYML_CB_ERR(m_tree->callbacks(), "JSON does not have anchors");
_write_scalar_json(sc.scalar, flags.has_key(), flags.is_quoted());
}
#define _rymlindent_nextline() for(size_t lv = 0; lv < ilevel+1; ++lv) { this->Writer::_do_write(' '); this->Writer::_do_write(' '); }
template<class Writer>
void Emitter<Writer>::_write_scalar_literal(csubstr s, size_t ilevel, bool explicit_key, bool explicit_indentation)
{
if(explicit_key)
this->Writer::_do_write("? ");
csubstr trimmed = s.trimr("\n\r");
size_t numnewlines_at_end = s.len - trimmed.len - s.sub(trimmed.len).count('\r');
//
if(!explicit_indentation)
this->Writer::_do_write('|');
else
this->Writer::_do_write("|2");
//
if(numnewlines_at_end > 1 || (trimmed.len == 0 && s.len > 0)/*only newlines*/)
this->Writer::_do_write("+\n");
else if(numnewlines_at_end == 1)
this->Writer::_do_write('\n');
else
this->Writer::_do_write("-\n");
//
if(trimmed.len)
{
size_t pos = 0; // tracks the last character that was already written
for(size_t i = 0; i < trimmed.len; ++i)
{
if(trimmed[i] != '\n')
continue;
// write everything up to this point
csubstr since_pos = trimmed.range(pos, i+1); // include the newline
_rymlindent_nextline()
this->Writer::_do_write(since_pos);
pos = i+1; // already written
}
if(pos < trimmed.len)
{
_rymlindent_nextline()
this->Writer::_do_write(trimmed.sub(pos));
}
if(numnewlines_at_end)
{
this->Writer::_do_write('\n');
--numnewlines_at_end;
}
}
for(size_t i = 0; i < numnewlines_at_end; ++i)
{
_rymlindent_nextline()
if(i+1 < numnewlines_at_end || explicit_key)
this->Writer::_do_write('\n');
}
if(explicit_key && !numnewlines_at_end)
this->Writer::_do_write('\n');
}
template<class Writer>
void Emitter<Writer>::_write_scalar_folded(csubstr s, size_t ilevel, bool explicit_key)
{
if(explicit_key)
{
this->Writer::_do_write("? ");
}
RYML_ASSERT(s.find("\r") == csubstr::npos);
csubstr trimmed = s.trimr('\n');
size_t numnewlines_at_end = s.len - trimmed.len;
if(numnewlines_at_end == 0)
{
this->Writer::_do_write(">-\n");
}
else if(numnewlines_at_end == 1)
{
this->Writer::_do_write(">\n");
}
else if(numnewlines_at_end > 1)
{
this->Writer::_do_write(">+\n");
}
if(trimmed.len)
{
size_t pos = 0; // tracks the last character that was already written
for(size_t i = 0; i < trimmed.len; ++i)
{
if(trimmed[i] != '\n')
continue;
// write everything up to this point
csubstr since_pos = trimmed.range(pos, i+1); // include the newline
pos = i+1; // because of the newline
_rymlindent_nextline()
this->Writer::_do_write(since_pos);
this->Writer::_do_write('\n'); // write the newline twice
}
if(pos < trimmed.len)
{
_rymlindent_nextline()
this->Writer::_do_write(trimmed.sub(pos));
}
if(numnewlines_at_end)
{
this->Writer::_do_write('\n');
--numnewlines_at_end;
}
}
for(size_t i = 0; i < numnewlines_at_end; ++i)
{
_rymlindent_nextline()
if(i+1 < numnewlines_at_end || explicit_key)
this->Writer::_do_write('\n');
}
if(explicit_key && !numnewlines_at_end)
this->Writer::_do_write('\n');
}
template<class Writer>
void Emitter<Writer>::_write_scalar_squo(csubstr s, size_t ilevel)
{
size_t pos = 0; // tracks the last character that was already written
this->Writer::_do_write('\'');
for(size_t i = 0; i < s.len; ++i)
{
if(s[i] == '\n')
{
csubstr sub = s.range(pos, i+1);
this->Writer::_do_write(sub); // write everything up to (including) this char
this->Writer::_do_write('\n'); // write the character again
if(i + 1 < s.len)
_rymlindent_nextline() // indent the next line
pos = i+1;
}
else if(s[i] == '\'')
{
csubstr sub = s.range(pos, i+1);
this->Writer::_do_write(sub); // write everything up to (including) this char
this->Writer::_do_write('\''); // write the character again
pos = i+1;
}
}
// write missing characters at the end of the string
if(pos < s.len)
this->Writer::_do_write(s.sub(pos));
this->Writer::_do_write('\'');
}
template<class Writer>
void Emitter<Writer>::_write_scalar_dquo(csubstr s, size_t ilevel)
{
size_t pos = 0; // tracks the last character that was already written
this->Writer::_do_write('"');
for(size_t i = 0; i < s.len; ++i)
{
const char curr = s.str[i];
if(curr == '"' || curr == '\\')
{
csubstr sub = s.range(pos, i);
this->Writer::_do_write(sub); // write everything up to (excluding) this char
this->Writer::_do_write('\\'); // write the escape
this->Writer::_do_write(curr); // write the char
pos = i+1;
}
else if(s[i] == '\n')
{
csubstr sub = s.range(pos, i+1);
this->Writer::_do_write(sub); // write everything up to (including) this newline
this->Writer::_do_write('\n'); // write the newline again
if(i + 1 < s.len)
_rymlindent_nextline() // indent the next line
pos = i+1;
if(i+1 < s.len) // escape leading whitespace after the newline
{
const char next = s.str[i+1];
if(next == ' ' || next == '\t')
this->Writer::_do_write('\\');
}
}
else if(curr == ' ' || curr == '\t')
{
// escape trailing whitespace before a newline
size_t next = s.first_not_of(" \t\r", i);
if(next != npos && s[next] == '\n')
{
csubstr sub = s.range(pos, i);
this->Writer::_do_write(sub); // write everything up to (excluding) this char
this->Writer::_do_write('\\'); // escape the whitespace
pos = i;
}
}
else if(C4_UNLIKELY(curr == '\r'))
{
csubstr sub = s.range(pos, i);
this->Writer::_do_write(sub); // write everything up to (excluding) this char
this->Writer::_do_write("\\r"); // write the escaped char
pos = i+1;
}
}
// write missing characters at the end of the string
if(pos < s.len)
{
csubstr sub = s.sub(pos);
this->Writer::_do_write(sub);
}
this->Writer::_do_write('"');
}
template<class Writer>
void Emitter<Writer>::_write_scalar_plain(csubstr s, size_t ilevel)
{
size_t pos = 0; // tracks the last character that was already written
for(size_t i = 0; i < s.len; ++i)
{
const char curr = s.str[i];
if(curr == '\n')
{
csubstr sub = s.range(pos, i+1);
this->Writer::_do_write(sub); // write everything up to (including) this newline
this->Writer::_do_write('\n'); // write the newline again
if(i + 1 < s.len)
_rymlindent_nextline() // indent the next line
pos = i+1;
}
}
// write missing characters at the end of the string
if(pos < s.len)
{
csubstr sub = s.sub(pos);
this->Writer::_do_write(sub);
}
}
#undef _rymlindent_nextline
template<class Writer>
void Emitter<Writer>::_write_scalar(csubstr s, bool was_quoted)
{
// this block of code needed to be moved to before the needs_quotes
// assignment to work around a g++ optimizer bug where (s.str != nullptr)
// was evaluated as true even if s.str was actually a nullptr (!!!)
if(s.len == size_t(0))
{
if(was_quoted || s.str != nullptr)
this->Writer::_do_write("''");
return;
}
const bool needs_quotes = (
was_quoted
||
(
( ! s.is_number())
&&
(
// has leading whitespace
// looks like reference or anchor
// would be treated as a directive
// see https://www.yaml.info/learn/quote.html#noplain
s.begins_with_any(" \n\t\r*&%@`")
||
s.begins_with("<<")
||
// has trailing whitespace
s.ends_with_any(" \n\t\r")
||
// has special chars
(s.first_of("#:-?,\n{}[]'\"") != npos)
)
)
);
if( ! needs_quotes)
{
this->Writer::_do_write(s);
}
else
{
const bool has_dquotes = s.first_of( '"') != npos;
const bool has_squotes = s.first_of('\'') != npos;
if(!has_squotes && has_dquotes)
{
this->Writer::_do_write('\'');
this->Writer::_do_write(s);
this->Writer::_do_write('\'');
}
else if(has_squotes && !has_dquotes)
{
RYML_ASSERT(s.count('\n') == 0);
this->Writer::_do_write('"');
this->Writer::_do_write(s);
this->Writer::_do_write('"');
}
else
{
_write_scalar_squo(s, /*FIXME FIXME FIXME*/0);
}
}
}
template<class Writer>
void Emitter<Writer>::_write_scalar_json(csubstr s, bool as_key, bool use_quotes)
{
if((!use_quotes)
// json keys require quotes
&& (!as_key)
&& (
// do not quote special cases
(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) ))
)
)
)
{
this->Writer::_do_write(s);
}
else
{
size_t pos = 0;
this->Writer::_do_write('"');
for(size_t i = 0; i < s.len; ++i)
{
switch(s.str[i])
{
case '"':
this->Writer ::_do_write(s.range(pos, i));
this->Writer ::_do_write("\\\"");
pos = i + 1;
break;
case '\n':
this->Writer ::_do_write(s.range(pos, i));
this->Writer ::_do_write("\\n");
pos = i + 1;
break;
case '\t':
this->Writer ::_do_write(s.range(pos, i));
this->Writer ::_do_write("\\t");
pos = i + 1;
break;
case '\\':
this->Writer ::_do_write(s.range(pos, i));
this->Writer ::_do_write("\\\\");
pos = i + 1;
break;
case '\r':
this->Writer ::_do_write(s.range(pos, i));
this->Writer ::_do_write("\\r");
pos = i + 1;
break;
case '\b':
this->Writer ::_do_write(s.range(pos, i));
this->Writer ::_do_write("\\b");
pos = i + 1;
break;
case '\f':
this->Writer ::_do_write(s.range(pos, i));
this->Writer ::_do_write("\\f");
pos = i + 1;
break;
}
}
if(pos < s.len)
{
csubstr sub = s.sub(pos);
this->Writer::_do_write(sub);
}
this->Writer::_do_write('"');
}
}
} // namespace yml
} // namespace c4
#endif /* _C4_YML_EMIT_DEF_HPP_ */

View File

@ -0,0 +1,490 @@
#ifndef _C4_YML_EMIT_HPP_
#define _C4_YML_EMIT_HPP_
#ifndef _C4_YML_WRITER_HPP_
#include "./writer.hpp"
#endif
#ifndef _C4_YML_TREE_HPP_
#include "./tree.hpp"
#endif
#ifndef _C4_YML_NODE_HPP_
#include "./node.hpp"
#endif
#define RYML_DEPRECATE_EMIT \
RYML_DEPRECATED("use emit_yaml() instead. See https://github.com/biojppm/rapidyaml/issues/120")
#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_EMITRS \
RYML_DEPRECATED("use emitrs_yaml() instead. See https://github.com/biojppm/rapidyaml/issues/120")
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
namespace c4 {
namespace yml {
template<class Writer> class Emitter;
template<class OStream>
using EmitterOStream = Emitter<WriterOStream<OStream>>;
using EmitterFile = Emitter<WriterFile>;
using EmitterBuf = Emitter<WriterBuf>;
typedef enum {
EMIT_YAML = 0,
EMIT_JSON = 1
} EmitType_e;
/** mark a tree or node to be emitted as json */
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()) {}
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
template<class Writer>
class Emitter : public Writer
{
public:
using Writer::Writer;
/** emit!
*
* When writing to a buffer, returns a substr of the emitted YAML.
* If the given buffer has insufficient space, the returned span will
* be null and its size will be the needed space. No writes are done
* after the end of the buffer.
*
* When writing to a file, the returned substr will be null, but its
* length will be set to the number of bytes written. */
substr emit_as(EmitType_e type, Tree const& t, size_t 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);
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);
private:
void _write(NodeScalar const& C4_RESTRICT sc, NodeType flags, size_t 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_tag(csubstr tag)
{
if(!tag.begins_with('!'))
this->Writer::_do_write('!');
this->Writer::_do_write(tag);
}
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_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_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)); }
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** 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)
{
EmitterFile em(f);
return em.emit_as(EMIT_YAML, t, id, /*error_on_excess*/true).len;
}
RYML_DEPRECATE_EMIT inline size_t emit(Tree const& t, size_t id, FILE *f)
{
return emit_yaml(t, id, f);
}
/** 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)
{
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 */
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;
}
RYML_DEPRECATE_EMIT inline size_t emit(Tree const& t, FILE *f=nullptr)
{
return emit_yaml(t, f);
}
/** emit JSON to the given file. A null file defaults to stdout.
* Return the number of bytes written.
* @overload */
inline size_t emit_json(Tree const& t, FILE *f=nullptr)
{
EmitterFile em(f);
return em.emit_as(EMIT_JSON, t, /*error_on_excess*/true).len;
}
/** emit YAML to the given file. A null file defaults to stdout.
* Return the number of bytes written.
* @overload */
inline size_t emit_yaml(ConstNodeRef const& r, FILE *f=nullptr)
{
EmitterFile em(f);
return em.emit_as(EMIT_YAML, r, /*error_on_excess*/true).len;
}
RYML_DEPRECATE_EMIT inline size_t emit(ConstNodeRef const& r, FILE *f=nullptr)
{
return emit_yaml(r, f);
}
/** emit JSON to the given file. A null file defaults to stdout.
* Return the number of bytes written.
* @overload */
inline size_t emit_json(ConstNodeRef const& r, FILE *f=nullptr)
{
EmitterFile em(f);
return em.emit_as(EMIT_JSON, r, /*error_on_excess*/true).len;
}
//-----------------------------------------------------------------------------
/** emit YAML to an STL-like ostream */
template<class OStream>
inline OStream& operator<< (OStream& s, Tree const& t)
{
EmitterOStream<OStream> em(s);
em.emit_as(EMIT_YAML, t);
return s;
}
/** emit YAML to an STL-like ostream
* @overload */
template<class OStream>
inline OStream& operator<< (OStream& s, ConstNodeRef const& n)
{
EmitterOStream<OStream> em(s);
em.emit_as(EMIT_YAML, n);
return s;
}
/** emit json to an STL-like stream */
template<class OStream>
inline OStream& operator<< (OStream& s, as_json const& j)
{
EmitterOStream<OStream> em(s);
em.emit_as(EMIT_JSON, *j.tree, j.node, true);
return s;
}
//-----------------------------------------------------------------------------
/** emit YAML to the given buffer. Return a substr trimmed to the emitted YAML.
* @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)
{
EmitterBuf em(buf);
return em.emit_as(EMIT_YAML, t, id, error_on_excess);
}
RYML_DEPRECATE_EMIT inline substr emit(Tree const& t, size_t id, substr buf, bool error_on_excess=true)
{
return emit_yaml(t, id, buf, error_on_excess);
}
/** emit JSON to the given buffer. Return a substr trimmed to the emitted JSON.
* @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)
{
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.
* @param error_on_excess Raise an error if the space in the buffer is insufficient.
* @overload */
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);
}
RYML_DEPRECATE_EMIT inline substr emit(Tree const& t, substr buf, bool error_on_excess=true)
{
return emit_yaml(t, buf, error_on_excess);
}
/** emit JSON to the given buffer. Return a substr trimmed to the emitted JSON.
* @param error_on_excess Raise an error if the space in the buffer is insufficient.
* @overload */
inline substr emit_json(Tree const& t, substr buf, bool error_on_excess=true)
{
EmitterBuf em(buf);
return em.emit_as(EMIT_JSON, t, error_on_excess);
}
/** emit YAML to the given buffer. Return a substr trimmed to the emitted YAML.
* @param error_on_excess Raise an error if the space in the buffer is insufficient.
* @overload
*/
inline substr emit_yaml(ConstNodeRef const& r, substr buf, bool error_on_excess=true)
{
EmitterBuf em(buf);
return em.emit_as(EMIT_YAML, r, error_on_excess);
}
RYML_DEPRECATE_EMIT inline substr emit(ConstNodeRef const& r, substr buf, bool error_on_excess=true)
{
return emit_yaml(r, buf, error_on_excess);
}
/** emit JSON to the given buffer. Return a substr trimmed to the emitted JSON.
* @param error_on_excess Raise an error if the space in the buffer is insufficient.
* @overload
*/
inline substr emit_json(ConstNodeRef const& r, substr buf, bool error_on_excess=true)
{
EmitterBuf em(buf);
return em.emit_as(EMIT_JSON, r, 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. */
template<class CharOwningContainer>
substr emitrs_yaml(Tree const& t, size_t id, CharOwningContainer * cont)
{
substr buf = to_substr(*cont);
substr ret = emit_yaml(t, id, 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);
}
return ret;
}
template<class CharOwningContainer>
RYML_DEPRECATE_EMITRS substr emitrs(Tree const& t, size_t id, CharOwningContainer * cont)
{
return emitrs_yaml(t, id, cont);
}
/** emit+resize: emit JSON to the given std::string/std::vector-like
* container, resizing it as needed to fit the emitted JSON. */
template<class CharOwningContainer>
substr emitrs_json(Tree const& t, size_t id, CharOwningContainer * cont)
{
substr buf = to_substr(*cont);
substr ret = emit_json(t, id, 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);
}
return ret;
}
/** emit+resize: emit YAML to the given std::string/std::vector-like
* container, resizing it as needed to fit the emitted YAML. */
template<class CharOwningContainer>
CharOwningContainer emitrs_yaml(Tree const& t, size_t id)
{
CharOwningContainer c;
emitrs_yaml(t, id, &c);
return c;
}
template<class CharOwningContainer>
RYML_DEPRECATE_EMITRS CharOwningContainer emitrs(Tree const& t, size_t id)
{
CharOwningContainer c;
emitrs_yaml(t, id, &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. */
template<class CharOwningContainer>
CharOwningContainer emitrs_json(Tree const& t, size_t id)
{
CharOwningContainer c;
emitrs_json(t, id, &c);
return c;
}
/** emit+resize: YAML to the given std::string/std::vector-like
* container, resizing it as needed to fit the emitted YAML. */
template<class CharOwningContainer>
substr emitrs_yaml(Tree const& t, CharOwningContainer * cont)
{
if(t.empty())
return {};
return emitrs_yaml(t, t.root_id(), cont);
}
template<class CharOwningContainer>
RYML_DEPRECATE_EMITRS substr emitrs(Tree const& t, CharOwningContainer * cont)
{
return emitrs_yaml(t, cont);
}
/** emit+resize: JSON to the given std::string/std::vector-like
* container, resizing it as needed to fit the emitted JSON. */
template<class CharOwningContainer>
substr emitrs_json(Tree const& t, CharOwningContainer * cont)
{
if(t.empty())
return {};
return emitrs_json(t, t.root_id(), cont);
}
/** emit+resize: YAML to the given std::string/std::vector-like container,
* resizing it as needed to fit the emitted YAML. */
template<class CharOwningContainer>
CharOwningContainer emitrs_yaml(Tree const& t)
{
CharOwningContainer c;
if(t.empty())
return c;
emitrs_yaml(t, t.root_id(), &c);
return c;
}
template<class CharOwningContainer>
RYML_DEPRECATE_EMITRS CharOwningContainer emitrs(Tree const& t)
{
return emitrs_yaml<CharOwningContainer>(t);
}
/** emit+resize: JSON to the given std::string/std::vector-like container,
* resizing it as needed to fit the emitted JSON. */
template<class CharOwningContainer>
CharOwningContainer emitrs_json(Tree const& t)
{
CharOwningContainer c;
if(t.empty())
return c;
emitrs_json(t, t.root_id(), &c);
return c;
}
/** emit+resize: YAML to the given std::string/std::vector-like container,
* resizing it as needed to fit the emitted YAML. */
template<class CharOwningContainer>
substr emitrs_yaml(ConstNodeRef const& n, CharOwningContainer * cont)
{
_RYML_CB_CHECK(n.tree()->callbacks(), n.valid());
return emitrs_yaml(*n.tree(), n.id(), cont);
}
template<class CharOwningContainer>
RYML_DEPRECATE_EMITRS substr emitrs(ConstNodeRef const& n, CharOwningContainer * cont)
{
return emitrs_yaml(n, cont);
}
/** emit+resize: JSON to the given std::string/std::vector-like container,
* resizing it as needed to fit the emitted JSON. */
template<class CharOwningContainer>
substr emitrs_json(ConstNodeRef const& n, CharOwningContainer * cont)
{
_RYML_CB_CHECK(n.tree()->callbacks(), n.valid());
return emitrs_json(*n.tree(), n.id(), cont);
}
/** emit+resize: YAML to the given std::string/std::vector-like container,
* resizing it as needed to fit the emitted YAML. */
template<class CharOwningContainer>
CharOwningContainer emitrs_yaml(ConstNodeRef const& n)
{
_RYML_CB_CHECK(n.tree()->callbacks(), n.valid());
CharOwningContainer c;
emitrs_yaml(*n.tree(), n.id(), &c);
return c;
}
template<class CharOwningContainer>
RYML_DEPRECATE_EMITRS CharOwningContainer emitrs(ConstNodeRef const& n)
{
return emitrs_yaml<CharOwningContainer>(n);
}
/** emit+resize: JSON to the given std::string/std::vector-like container,
* resizing it as needed to fit the emitted JSON. */
template<class CharOwningContainer>
CharOwningContainer emitrs_json(ConstNodeRef const& n)
{
_RYML_CB_CHECK(n.tree()->callbacks(), n.valid());
CharOwningContainer c;
emitrs_json(*n.tree(), n.id(), &c);
return c;
}
} // namespace yml
} // namespace c4
#undef RYML_DEPRECATE_EMIT
#undef RYML_DEPRECATE_EMITRS
#include "c4/yml/emit.def.hpp"
#endif /* _C4_YML_EMIT_HPP_ */

View File

@ -0,0 +1,18 @@
#ifndef C4_YML_EXPORT_HPP_
#define C4_YML_EXPORT_HPP_
#ifdef _WIN32
#ifdef RYML_SHARED
#ifdef RYML_EXPORTS
#define RYML_EXPORT __declspec(dllexport)
#else
#define RYML_EXPORT __declspec(dllimport)
#endif
#else
#define RYML_EXPORT
#endif
#else
#define RYML_EXPORT
#endif
#endif /* C4_YML_EXPORT_HPP_ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,706 @@
#ifndef _C4_YML_PARSE_HPP_
#define _C4_YML_PARSE_HPP_
#ifndef _C4_YML_TREE_HPP_
#include "c4/yml/tree.hpp"
#endif
#ifndef _C4_YML_NODE_HPP_
#include "c4/yml/node.hpp"
#endif
#ifndef _C4_YML_DETAIL_STACK_HPP_
#include "c4/yml/detail/stack.hpp"
#endif
#include <stdarg.h>
#if defined(_MSC_VER)
# pragma warning(push)
# pragma warning(disable: 4251/*needs to have dll-interface to be used by clients of struct*/)
#endif
namespace c4 {
namespace yml {
struct RYML_EXPORT ParserOptions
{
private:
typedef enum : uint32_t {
LOCATIONS = (1 << 0),
DEFAULTS = 0,
} Flags_e;
uint32_t flags = DEFAULTS;
public:
ParserOptions() = default;
/** @name source location tracking */
/** @{ */
/** enable/disable source location tracking */
ParserOptions& locations(bool enabled)
{
if(enabled)
flags |= LOCATIONS;
else
flags &= ~LOCATIONS;
return *this;
}
bool locations() const { return (flags & LOCATIONS) != 0u; }
/** @} */
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
class RYML_EXPORT Parser
{
public:
/** @name construction and assignment */
/** @{ */
Parser(Callbacks const& cb, ParserOptions opts={});
Parser(ParserOptions opts={}) : Parser(get_callbacks(), opts) {}
~Parser();
Parser(Parser &&);
Parser(Parser const&);
Parser& operator=(Parser &&);
Parser& operator=(Parser 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 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.
*
* @note the tree and the arena can (and should) also be reserved. */
void reserve_stack(size_t capacity)
{
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);
}
/** Reserve a certain capacity for the character arena used to
* filter scalars. */
void reserve_filter_arena(size_t num_characters)
{
_resize_filter_arena(num_characters);
}
/** @} */
public:
/** @name getters and modifiers */
/** @{ */
/** Get the current callbacks in the parser. */
Callbacks callbacks() const { return 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; }
size_t stack_capacity() const { return m_stack.capacity(); }
size_t locations_capacity() const { return m_newline_offsets_capacity; }
size_t filter_arena_capacity() const { return m_filter_arena.len; }
ParserOptions const& options() const { return m_options; }
/** @} */
public:
/** @name parse_in_place */
/** @{ */
/** Create a new tree and parse into its root.
* The tree is created with the callbacks currently in the parser. */
Tree parse_in_place(csubstr filename, substr src)
{
Tree t(callbacks());
t.reserve(_estimate_capacity(src));
this->parse_in_place(filename, src, &t, t.root_id());
return t;
}
/** Parse into an existing tree, starting at its root node.
* The callbacks in the tree are kept, and used to allocate
* the tree members, if any allocation is required. */
void parse_in_place(csubstr filename, substr src, Tree *t)
{
this->parse_in_place(filename, src, t, t->root_id());
}
/** Parse into an existing node.
* The callbacks in the tree are kept, and used to allocate
* the tree members, if any allocation is required. */
void parse_in_place(csubstr filename, substr src, Tree *t, size_t node_id);
// ^^^^^^^^^^^^^ this is the workhorse overload; everything else is syntactic candy
/** Parse into an existing node.
* The callbacks in the tree are kept, and used to allocate
* the tree members, if any allocation is required. */
void parse_in_place(csubstr filename, substr src, NodeRef node)
{
this->parse_in_place(filename, src, node.tree(), node.id());
}
RYML_DEPRECATED("use parse_in_place() instead") Tree parse(csubstr filename, substr src) { return parse_in_place(filename, src); }
RYML_DEPRECATED("use parse_in_place() instead") void parse(csubstr filename, substr src, Tree *t) { parse_in_place(filename, src, t); }
RYML_DEPRECATED("use parse_in_place() instead") void parse(csubstr filename, substr src, Tree *t, size_t node_id) { parse_in_place(filename, src, t, node_id); }
RYML_DEPRECATED("use parse_in_place() instead") void parse(csubstr filename, substr src, NodeRef node) { parse_in_place(filename, src, node); }
/** @} */
public:
/** @name parse_in_arena: copy the YAML source buffer to the
* tree's arena, then parse the copy in situ
*
* @note overloads receiving a substr YAML buffer are intentionally
* left undefined, such that calling parse_in_arena() with a substr
* will cause a linker error. This is to prevent an accidental
* copy of the source buffer to the tree's arena, because substr
* is implicitly convertible to csubstr. If you really intend to parse
* a mutable buffer in the tree's arena, convert it first to immutable
* by assigning the substr to a csubstr prior to calling parse_in_arena().
* This is not needed for parse_in_place() because csubstr is not
* implicitly convertible to substr. */
/** @{ */
// READ THE NOTE ABOVE!
#define RYML_DONT_PARSE_SUBSTR_IN_ARENA "Do not pass a (mutable) substr to parse_in_arena(); if you have a substr, it should be parsed in place. Consider using parse_in_place() instead, or convert the buffer to csubstr prior to calling. This function is deliberately left undefined and will cause a linker error."
RYML_DEPRECATED(RYML_DONT_PARSE_SUBSTR_IN_ARENA) Tree parse_in_arena(csubstr filename, substr csrc);
RYML_DEPRECATED(RYML_DONT_PARSE_SUBSTR_IN_ARENA) void parse_in_arena(csubstr filename, substr csrc, Tree *t);
RYML_DEPRECATED(RYML_DONT_PARSE_SUBSTR_IN_ARENA) void parse_in_arena(csubstr filename, substr csrc, Tree *t, size_t node_id);
RYML_DEPRECATED(RYML_DONT_PARSE_SUBSTR_IN_ARENA) void parse_in_arena(csubstr filename, substr csrc, NodeRef node);
/** Create a new tree and parse into its root.
* The immutable YAML source is first copied to the tree's arena,
* and parsed from there.
* The callbacks in the tree are kept, and used to allocate
* the tree members, if any allocation is required. */
Tree parse_in_arena(csubstr filename, csubstr csrc)
{
Tree t(callbacks());
substr src = t.copy_to_arena(csrc);
t.reserve(_estimate_capacity(csrc));
this->parse_in_place(filename, src, &t, t.root_id());
return t;
}
/** Parse into an existing tree, starting at its root node.
* The immutable YAML source is first copied to the tree's arena,
* and parsed from there.
* The callbacks in the tree are kept, and used to allocate
* the tree members, if any allocation is required. */
void parse_in_arena(csubstr filename, csubstr csrc, Tree *t)
{
substr src = t->copy_to_arena(csrc);
this->parse_in_place(filename, src, t, t->root_id());
}
/** Parse into a specific node in an existing tree.
* The immutable YAML source is first copied to the tree's arena,
* and parsed from there.
* The callbacks in the tree are kept, and used to allocate
* the tree members, if any allocation is required. */
void parse_in_arena(csubstr filename, csubstr csrc, Tree *t, size_t node_id)
{
substr src = t->copy_to_arena(csrc);
this->parse_in_place(filename, src, t, node_id);
}
/** Parse into a specific node in an existing tree.
* The immutable YAML source is first copied to the tree's arena,
* and parsed from there.
* The callbacks in the tree are kept, and used to allocate
* the tree members, if any allocation is required. */
void parse_in_arena(csubstr filename, csubstr csrc, NodeRef node)
{
substr src = node.tree()->copy_to_arena(csrc);
this->parse_in_place(filename, src, node.tree(), node.id());
}
RYML_DEPRECATED("use parse_in_arena() instead") Tree parse(csubstr filename, csubstr csrc) { return parse_in_arena(filename, csrc); }
RYML_DEPRECATED("use parse_in_arena() instead") void parse(csubstr filename, csubstr csrc, Tree *t) { parse_in_arena(filename, csrc, t); }
RYML_DEPRECATED("use parse_in_arena() instead") void parse(csubstr filename, csubstr csrc, Tree *t, size_t node_id) { parse_in_arena(filename, csrc, t, node_id); }
RYML_DEPRECATED("use parse_in_arena() instead") void parse(csubstr filename, csubstr csrc, NodeRef node) { parse_in_arena(filename, csrc, node); }
/** @} */
public:
/** @name locations */
/** @{ */
/** Get the location of a node of the last tree to be parsed by this parser. */
Location location(Tree const& tree, size_t 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. @p val
* must be pointing to somewhere in the source buffer that was
* last parsed by this object. */
Location val_location(const char *val) const;
/** @} */
private:
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;
private:
using flag_t = int;
static size_t _estimate_capacity(csubstr src) { size_t c = _count_nlines(src); c = c >= 16 ? c : 16; return c; }
void _reset();
bool _finished_file() const;
bool _finished_line() const;
csubstr _peek_next_line(size_t pos=npos) const;
bool _advance_to_peeked();
void _scan_line();
csubstr _slurp_doc_scalar();
/**
* @param [out] quoted
* Will only be written to if this method returns true.
* Will be set to true if the scanned scalar was quoted, by '', "", > or |.
*/
bool _scan_scalar_seq_blck(csubstr *C4_RESTRICT scalar, bool *C4_RESTRICT quoted);
bool _scan_scalar_map_blck(csubstr *C4_RESTRICT scalar, bool *C4_RESTRICT quoted);
bool _scan_scalar_seq_flow(csubstr *C4_RESTRICT scalar, bool *C4_RESTRICT quoted);
bool _scan_scalar_map_flow(csubstr *C4_RESTRICT scalar, bool *C4_RESTRICT quoted);
bool _scan_scalar_unk(csubstr *C4_RESTRICT scalar, bool *C4_RESTRICT quoted);
csubstr _scan_comment();
csubstr _scan_squot_scalar();
csubstr _scan_dquot_scalar();
csubstr _scan_block();
substr _scan_plain_scalar_blck(csubstr currscalar, csubstr peeked_line, size_t indentation);
substr _scan_plain_scalar_flow(csubstr currscalar, csubstr peeked_line);
substr _scan_complex_key(csubstr currscalar, csubstr peeked_line);
csubstr _scan_to_next_nonempty_line(size_t indentation);
csubstr _extend_scanned_scalar(csubstr currscalar);
csubstr _filter_squot_scalar(const substr s);
csubstr _filter_dquot_scalar(substr s);
csubstr _filter_plain_scalar(substr s, size_t indentation);
csubstr _filter_block_scalar(substr s, BlockStyle_e style, BlockChomp_e chomp, size_t indentation);
template<bool backslash_is_escape, bool keep_trailing_whitespace>
bool _filter_nl(substr scalar, size_t *C4_RESTRICT pos, size_t *C4_RESTRICT filter_arena_pos, size_t indentation);
template<bool keep_trailing_whitespace>
void _filter_ws(substr scalar, size_t *C4_RESTRICT pos, size_t *C4_RESTRICT filter_arena_pos);
bool _apply_chomp(substr buf, size_t *C4_RESTRICT pos, BlockChomp_e chomp);
void _handle_finished_file();
void _handle_line();
bool _handle_indentation();
bool _handle_unk();
bool _handle_map_flow();
bool _handle_map_blck();
bool _handle_seq_flow();
bool _handle_seq_blck();
bool _handle_top();
bool _handle_types();
bool _handle_key_anchors_and_refs();
bool _handle_val_anchors_and_refs();
void _move_val_tag_to_key_tag();
void _move_key_tag_to_val_tag();
void _move_key_tag2_to_key_tag();
void _move_val_anchor_to_key_anchor();
void _move_key_anchor_to_val_anchor();
void _push_level(bool explicit_flow_chars = false);
void _pop_level();
void _start_unk(bool as_child=true);
void _start_map(bool as_child=true);
void _start_map_unk(bool as_child);
void _stop_map();
void _start_seq(bool as_child=true);
void _stop_seq();
void _start_seqimap();
void _stop_seqimap();
void _start_doc(bool as_child=true);
void _stop_doc();
void _start_new_doc(csubstr rem);
void _end_stream();
NodeData* _append_val(csubstr val, flag_t quoted=false);
NodeData* _append_key_val(csubstr val, flag_t val_quoted=false);
bool _rval_dash_start_or_continue_seq();
void _store_scalar(csubstr s, flag_t is_quoted);
csubstr _consume_scalar();
void _move_scalar_from_top();
inline NodeData* _append_val_null(const char *str) { _RYML_CB_ASSERT(m_stack.m_callbacks, str >= m_buf.begin() && str <= m_buf.end()); return _append_val({nullptr, size_t(0)}); }
inline NodeData* _append_key_val_null(const char *str) { _RYML_CB_ASSERT(m_stack.m_callbacks, str >= m_buf.begin() && str <= m_buf.end()); return _append_key_val({nullptr, size_t(0)}); }
inline void _store_scalar_null(const char *str) { _RYML_CB_ASSERT(m_stack.m_callbacks, str >= m_buf.begin() && str <= m_buf.end()); _store_scalar({nullptr, size_t(0)}, false); }
void _set_indentation(size_t behind);
void _save_indentation(size_t behind=0);
bool _maybe_set_indentation_from_anchor_or_tag();
void _write_key_anchor(size_t node_id);
void _write_val_anchor(size_t node_id);
void _handle_directive(csubstr directive);
void _skipchars(char c);
template<size_t N>
void _skipchars(const char (&chars)[N]);
private:
static size_t _count_nlines(csubstr src);
private:
typedef enum : flag_t {
RTOP = 0x01 << 0, ///< reading at top level
RUNK = 0x01 << 1, ///< reading an unknown: 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 {}
QMRK = 0x01 << 5, ///< reading an explicit key (`? key`)
RKEY = 0x01 << 6, ///< reading a scalar as key
RVAL = 0x01 << 7, ///< reading a scalar as val
RNXT = 0x01 << 8, ///< read next val or keyval
SSCL = 0x01 << 9, ///< there's a stored scalar
QSCL = 0x01 << 10, ///< stored scalar was quoted
RSET = 0x01 << 11, ///< the (implicit) map being read is a !!set. @see https://yaml.org/type/set.html
NDOC = 0x01 << 12, ///< no document mode. a document has ended and another has not started yet.
//! 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 << 13,
} State_e;
struct LineContents
{
csubstr full; ///< the full line, including newlines on the right
csubstr stripped; ///< the stripped line, excluding newlines on the right
csubstr 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
LineContents() : full(), stripped(), rem(), indentation() {}
void reset_with_next_line(csubstr buf, size_t pos);
void reset(csubstr full_, csubstr stripped_)
{
full = full_;
stripped = stripped_;
rem = stripped_;
// find the first column where the character is not a space
indentation = full.first_not_of(' ');
}
size_t current_col() const
{
return current_col(rem);
}
size_t current_col(csubstr s) const
{
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;
}
};
struct State
{
flag_t flags;
size_t level;
size_t node_id; // don't hold a pointer to the node as it will be relocated during tree resizes
csubstr scalar;
size_t scalar_col; // the column where the scalar (or its quotes) begin
Location pos;
LineContents line_contents;
size_t indref;
State() : flags(), level(), node_id(), scalar(), scalar_col(), pos(), line_contents(), indref() {}
void reset(const char *file, size_t node_id_)
{
flags = RUNK|RTOP;
level = 0;
pos.name = to_csubstr(file);
pos.offset = 0;
pos.line = 1;
pos.col = 1;
node_id = node_id_;
scalar_col = 0;
scalar.clear();
indref = 0;
}
};
void _line_progressed(size_t ahead);
void _line_ended();
void _line_ended_undo();
void _prepare_pop()
{
RYML_ASSERT(m_stack.size() > 1);
State const& curr = m_stack.top();
State & next = m_stack.top(1);
next.pos = curr.pos;
next.line_contents = curr.line_contents;
next.scalar = curr.scalar;
}
inline bool _at_line_begin() const
{
return m_state->line_contents.rem.begin() == m_state->line_contents.full.begin();
}
inline bool _at_line_end() const
{
csubstr r = m_state->line_contents.rem;
return r.empty() || r.begins_with(' ', r.len);
}
inline bool _token_is_from_this_line(csubstr token) const
{
return token.is_sub(m_state->line_contents.full);
}
inline NodeData * node(State const* s) const { return m_tree->get(s->node_id); }
inline NodeData * node(State const& s) const { return m_tree->get(s .node_id); }
inline NodeData * node(size_t node_id) const { return m_tree->get( node_id); }
inline bool has_all(flag_t f) const { return (m_state->flags & f) == f; }
inline bool has_any(flag_t f) const { return (m_state->flags & f) != 0; }
inline bool has_none(flag_t f) const { return (m_state->flags & f) == 0; }
static inline bool has_all(flag_t f, State const* s) { return (s->flags & f) == f; }
static inline bool has_any(flag_t f, State const* s) { return (s->flags & f) != 0; }
static inline bool has_none(flag_t f, State const* s) { return (s->flags & f) == 0; }
inline void set_flags(flag_t f) { set_flags(f, m_state); }
inline void add_flags(flag_t on) { add_flags(on, m_state); }
inline void addrem_flags(flag_t on, flag_t off) { addrem_flags(on, off, m_state); }
inline void rem_flags(flag_t off) { rem_flags(off, m_state); }
void set_flags(flag_t f, State * s);
void add_flags(flag_t on, State * s);
void addrem_flags(flag_t on, flag_t off, State * s);
void rem_flags(flag_t off, State * s);
void _resize_filter_arena(size_t num_characters);
void _grow_filter_arena(size_t num_characters);
substr _finish_filter_arena(substr dst, size_t pos);
void _prepare_locations();
void _resize_locations(size_t sz);
bool _locations_dirty() const;
bool _location_from_cont(Tree const& tree, size_t node, Location *C4_RESTRICT loc) const;
bool _location_from_node(Tree const& tree, size_t node, Location *C4_RESTRICT loc, size_t level) const;
private:
void _free();
void _clr();
void _cp(Parser const* that);
void _mv(Parser *that);
#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 DumpFn> void _fmt_msg(DumpFn &&dumpfn) const;
static csubstr _prfl(substr buf, flag_t v);
private:
ParserOptions m_options;
csubstr m_file;
substr m_buf;
size_t m_root_id;
Tree * m_tree;
detail::stack<State> m_stack;
State * m_state;
size_t m_key_tag_indentation;
size_t m_key_tag2_indentation;
csubstr m_key_tag;
csubstr m_key_tag2;
size_t m_val_tag_indentation;
csubstr m_val_tag;
bool m_key_anchor_was_before;
size_t m_key_anchor_indentation;
csubstr m_key_anchor;
size_t m_val_anchor_indentation;
csubstr m_val_anchor;
substr m_filter_arena;
size_t *m_newline_offsets;
size_t m_newline_offsets_size;
size_t m_newline_offsets_capacity;
csubstr m_newline_offsets_buf;
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** @name parse_in_place
*
* @desc parse a mutable YAML source buffer.
*
* @note These freestanding functions use a temporary parser object,
* and are convenience functions to easily parse YAML without the need
* to instantiate a separate parser. Note that some properties
* (notably node locations in the original source code) are only
* available through the parser object after it has parsed the
* code. If you need access to any of these properties, use
* Parser::parse_in_place() */
/** @{ */
inline Tree parse_in_place( substr yaml ) { Parser np; return np.parse_in_place({} , yaml); } //!< parse in-situ a modifiable YAML source buffer.
inline Tree parse_in_place(csubstr filename, substr yaml ) { Parser np; return np.parse_in_place(filename, yaml); } //!< parse in-situ a modifiable YAML source buffer, providing a filename for error messages.
inline void parse_in_place( substr yaml, Tree *t ) { Parser np; np.parse_in_place({} , yaml, t); } //!< reusing the YAML tree, parse in-situ a modifiable YAML source buffer
inline void parse_in_place(csubstr filename, substr yaml, Tree *t ) { Parser np; np.parse_in_place(filename, yaml, t); } //!< reusing the YAML tree, parse in-situ a modifiable YAML source buffer, providing a filename for error messages.
inline void parse_in_place( substr yaml, Tree *t, size_t node_id) { Parser np; np.parse_in_place({} , yaml, t, node_id); } //!< reusing the YAML tree, parse in-situ a modifiable YAML source buffer
inline void parse_in_place(csubstr filename, substr yaml, Tree *t, size_t node_id) { Parser np; np.parse_in_place(filename, yaml, t, node_id); } //!< reusing the YAML tree, parse in-situ a modifiable YAML source buffer, providing a filename for error messages.
inline void parse_in_place( substr yaml, NodeRef node ) { Parser np; np.parse_in_place({} , yaml, node); } //!< reusing the YAML tree, parse in-situ a modifiable YAML source buffer
inline void parse_in_place(csubstr filename, substr yaml, NodeRef node ) { Parser np; np.parse_in_place(filename, yaml, node); } //!< reusing the YAML tree, parse in-situ a modifiable YAML source buffer, providing a filename for error messages.
RYML_DEPRECATED("use parse_in_place() instead") inline Tree parse( substr yaml ) { Parser np; return np.parse_in_place({} , yaml); }
RYML_DEPRECATED("use parse_in_place() instead") inline Tree parse(csubstr filename, substr yaml ) { Parser np; return np.parse_in_place(filename, yaml); }
RYML_DEPRECATED("use parse_in_place() instead") inline void parse( substr yaml, Tree *t ) { Parser np; np.parse_in_place({} , yaml, t); }
RYML_DEPRECATED("use parse_in_place() instead") inline void parse(csubstr filename, substr yaml, Tree *t ) { Parser np; np.parse_in_place(filename, yaml, t); }
RYML_DEPRECATED("use parse_in_place() instead") inline void parse( substr yaml, Tree *t, size_t node_id) { Parser np; np.parse_in_place({} , yaml, t, node_id); }
RYML_DEPRECATED("use parse_in_place() instead") inline void parse(csubstr filename, substr yaml, Tree *t, size_t node_id) { Parser np; np.parse_in_place(filename, yaml, t, node_id); }
RYML_DEPRECATED("use parse_in_place() instead") inline void parse( substr yaml, NodeRef node ) { Parser np; np.parse_in_place({} , yaml, node); }
RYML_DEPRECATED("use parse_in_place() instead") inline void parse(csubstr filename, substr yaml, NodeRef node ) { Parser np; np.parse_in_place(filename, yaml, node); }
/** @} */
//-----------------------------------------------------------------------------
/** @name parse_in_arena
* @desc parse a read-only YAML source buffer, copying it first to the tree's arena.
*
* @note These freestanding functions use a temporary parser object,
* and are convenience functions to easily parse YAML without the need
* to instantiate a separate parser. Note that some properties
* (notably node locations in the original source code) are only
* available through the parser object after it has parsed the
* code. If you need access to any of these properties, use
* Parser::parse_in_arena().
*
* @note overloads receiving a substr YAML buffer are intentionally
* left undefined, such that calling parse_in_arena() with a substr
* will cause a linker error. This is to prevent an accidental
* copy of the source buffer to the tree's arena, because substr
* is implicitly convertible to csubstr. If you really intend to parse
* a mutable buffer in the tree's arena, convert it first to immutable
* by assigning the substr to a csubstr prior to calling parse_in_arena().
* This is not needed for parse_in_place() because csubstr is not
* implicitly convertible to substr. */
/** @{ */
/* READ THE NOTE ABOVE! */
RYML_DEPRECATED(RYML_DONT_PARSE_SUBSTR_IN_ARENA) Tree parse_in_arena( substr yaml );
RYML_DEPRECATED(RYML_DONT_PARSE_SUBSTR_IN_ARENA) Tree parse_in_arena(csubstr filename, substr yaml );
RYML_DEPRECATED(RYML_DONT_PARSE_SUBSTR_IN_ARENA) void parse_in_arena( substr yaml, Tree *t );
RYML_DEPRECATED(RYML_DONT_PARSE_SUBSTR_IN_ARENA) void parse_in_arena(csubstr filename, substr yaml, Tree *t );
RYML_DEPRECATED(RYML_DONT_PARSE_SUBSTR_IN_ARENA) void parse_in_arena( substr yaml, Tree *t, size_t node_id);
RYML_DEPRECATED(RYML_DONT_PARSE_SUBSTR_IN_ARENA) void parse_in_arena(csubstr filename, substr yaml, Tree *t, size_t node_id);
RYML_DEPRECATED(RYML_DONT_PARSE_SUBSTR_IN_ARENA) void parse_in_arena( substr yaml, NodeRef node );
RYML_DEPRECATED(RYML_DONT_PARSE_SUBSTR_IN_ARENA) void parse_in_arena(csubstr filename, substr yaml, NodeRef node );
inline Tree parse_in_arena( csubstr yaml ) { Parser np; return np.parse_in_arena({} , yaml); } //!< parse a read-only YAML source buffer, copying it first to the tree's source arena.
inline Tree parse_in_arena(csubstr filename, csubstr yaml ) { Parser np; return np.parse_in_arena(filename, yaml); } //!< parse a read-only YAML source buffer, copying it first to the tree's source arena, providing a filename for error messages.
inline void parse_in_arena( csubstr yaml, Tree *t ) { Parser np; np.parse_in_arena({} , yaml, t); } //!< reusing the YAML tree, parse a read-only YAML source buffer, copying it first to the tree's source arena.
inline void parse_in_arena(csubstr filename, csubstr yaml, Tree *t ) { Parser np; np.parse_in_arena(filename, yaml, t); } //!< reusing the YAML tree, parse a read-only YAML source buffer, copying it first to the tree's source arena, providing a filename for error messages.
inline void parse_in_arena( csubstr yaml, Tree *t, size_t node_id) { Parser np; np.parse_in_arena({} , yaml, t, node_id); } //!< reusing the YAML tree, parse a read-only YAML source buffer, copying it first to the tree's source arena.
inline void parse_in_arena(csubstr filename, csubstr yaml, Tree *t, size_t node_id) { Parser np; np.parse_in_arena(filename, yaml, t, node_id); } //!< reusing the YAML tree, parse a read-only YAML source buffer, copying it first to the tree's source arena, providing a filename for error messages.
inline void parse_in_arena( csubstr yaml, NodeRef node ) { Parser np; np.parse_in_arena({} , yaml, node); } //!< reusing the YAML tree, parse a read-only YAML source buffer, copying it first to the tree's source arena.
inline void parse_in_arena(csubstr filename, csubstr yaml, NodeRef node ) { Parser np; np.parse_in_arena(filename, yaml, node); } //!< reusing the YAML tree, parse a read-only YAML source buffer, copying it first to the tree's source arena, providing a filename for error messages.
RYML_DEPRECATED("use parse_in_arena() instead") inline Tree parse( csubstr yaml ) { Parser np; return np.parse_in_arena({} , yaml); } //!< parse a read-only YAML source buffer, copying it first to the tree's source arena.
RYML_DEPRECATED("use parse_in_arena() instead") inline Tree parse(csubstr filename, csubstr yaml ) { Parser np; return np.parse_in_arena(filename, yaml); } //!< parse a read-only YAML source buffer, copying it first to the tree's source arena, providing a filename for error messages.
RYML_DEPRECATED("use parse_in_arena() instead") inline void parse( csubstr yaml, Tree *t ) { Parser np; np.parse_in_arena({} , yaml, t); } //!< reusing the YAML tree, parse a read-only YAML source buffer, copying it first to the tree's source arena.
RYML_DEPRECATED("use parse_in_arena() instead") inline void parse(csubstr filename, csubstr yaml, Tree *t ) { Parser np; np.parse_in_arena(filename, yaml, t); } //!< reusing the YAML tree, parse a read-only YAML source buffer, copying it first to the tree's source arena, providing a filename for error messages.
RYML_DEPRECATED("use parse_in_arena() instead") inline void parse( csubstr yaml, Tree *t, size_t node_id) { Parser np; np.parse_in_arena({} , yaml, t, node_id); } //!< reusing the YAML tree, parse a read-only YAML source buffer, copying it first to the tree's source arena.
RYML_DEPRECATED("use parse_in_arena() instead") inline void parse(csubstr filename, csubstr yaml, Tree *t, size_t node_id) { Parser np; np.parse_in_arena(filename, yaml, t, node_id); } //!< reusing the YAML tree, parse a read-only YAML source buffer, copying it first to the tree's source arena, providing a filename for error messages.
RYML_DEPRECATED("use parse_in_arena() instead") inline void parse( csubstr yaml, NodeRef node ) { Parser np; np.parse_in_arena({} , yaml, node); } //!< reusing the YAML tree, parse a read-only YAML source buffer, copying it first to the tree's source arena.
RYML_DEPRECATED("use parse_in_arena() instead") inline void parse(csubstr filename, csubstr yaml, NodeRef node ) { Parser np; np.parse_in_arena(filename, yaml, node); } //!< reusing the YAML tree, parse a read-only YAML source buffer, copying it first to the tree's source arena, providing a filename for error messages.
/** @} */
} // namespace yml
} // namespace c4
#if defined(_MSC_VER)
# pragma warning(pop)
#endif
#endif /* _C4_YML_PARSE_HPP_ */

View File

@ -0,0 +1,99 @@
#ifndef _C4_YML_PREPROCESS_HPP_
#define _C4_YML_PREPROCESS_HPP_
/** @file preprocess.hpp Functions for preprocessing YAML prior to parsing. */
/** @defgroup Preprocessors Preprocessor functions
*
* These are the existing preprocessors:
*
* @code{.cpp}
* size_t preprocess_json(csubstr json, substr buf)
* size_t preprocess_rxmap(csubstr json, substr buf)
* @endcode
*/
#ifndef _C4_YML_COMMON_HPP_
#include "./common.hpp"
#endif
#include <c4/substr.hpp>
namespace c4 {
namespace yml {
namespace detail {
using Preprocessor = size_t(csubstr, substr);
template<Preprocessor PP, class CharContainer>
substr preprocess_into_container(csubstr input, CharContainer *out)
{
// try to write once. the preprocessor will stop writing at the end of
// the container, but will process all the input to determine the
// required container size.
size_t sz = PP(input, to_substr(*out));
// if the container size is not enough, resize, and run again in the
// resized container
if(sz > out->size())
{
out->resize(sz);
sz = PP(input, to_substr(*out));
}
return to_substr(*out).first(sz);
}
} // namespace detail
//-----------------------------------------------------------------------------
/** @name preprocess_rxmap
* Convert flow-type relaxed maps (with implicit bools) into strict YAML
* flow map.
*
* @code{.yaml}
* {a, b, c, d: [e, f], g: {a, b}}
* # is converted into this:
* {a: 1, b: 1, c: 1, d: [e, f], g: {a, b}}
* @endcode
* @note this is NOT recursive - conversion happens only in the top-level map
* @param rxmap A relaxed map
* @param buf output buffer
* @param out output container
*/
//@{
/** Write into a given output buffer. This function is safe to call with
* empty or small buffers; it won't write beyond the end of the buffer.
*
* @return the number of characters required for output
*/
RYML_EXPORT size_t preprocess_rxmap(csubstr rxmap, substr buf);
/** Write into an existing container. It is resized to contained the output.
* @return a substr of the container
* @overload preprocess_rxmap */
template<class CharContainer>
substr preprocess_rxmap(csubstr rxmap, CharContainer *out)
{
return detail::preprocess_into_container<preprocess_rxmap>(rxmap, out);
}
/** Create a container with the result.
* @overload preprocess_rxmap */
template<class CharContainer>
CharContainer preprocess_rxmap(csubstr rxmap)
{
CharContainer out;
preprocess_rxmap(rxmap, &out);
return out;
}
//@}
} // namespace yml
} // namespace c4
#endif /* _C4_YML_PREPROCESS_HPP_ */

View File

@ -0,0 +1,45 @@
#ifndef _C4_YML_STD_MAP_HPP_
#define _C4_YML_STD_MAP_HPP_
/** @file map.hpp write/read std::map to/from a YAML tree. */
#include "c4/yml/node.hpp"
#include <map>
namespace c4 {
namespace yml {
// std::map requires child nodes in the data
// tree hierarchy (a MAP node in ryml parlance).
// So it should be serialized via write()/read().
template<class K, class V, class Less, class Alloc>
void write(c4::yml::NodeRef *n, std::map<K, V, Less, Alloc> const& m)
{
*n |= c4::yml::MAP;
for(auto const& C4_RESTRICT p : m)
{
auto ch = n->append_child();
ch << c4::yml::key(p.first);
ch << p.second;
}
}
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)
{
ch >> c4::yml::key(k);
ch >> v;
m->emplace(std::make_pair(std::move(k), std::move(v)));
}
return true;
}
} // namespace yml
} // namespace c4
#endif // _C4_YML_STD_MAP_HPP_

View File

@ -0,0 +1,8 @@
#ifndef _C4_YML_STD_STD_HPP_
#define _C4_YML_STD_STD_HPP_
#include "c4/yml/std/string.hpp"
#include "c4/yml/std/vector.hpp"
#include "c4/yml/std/map.hpp"
#endif // _C4_YML_STD_STD_HPP_

View File

@ -0,0 +1,9 @@
#ifndef C4_YML_STD_STRING_HPP_
#define C4_YML_STD_STRING_HPP_
/** @file string.hpp substring conversions for/from std::string */
// everything we need is implemented here:
#include <c4/std/string.hpp>
#endif // C4_YML_STD_STRING_HPP_

View File

@ -0,0 +1,53 @@
#ifndef _C4_YML_STD_VECTOR_HPP_
#define _C4_YML_STD_VECTOR_HPP_
#include "c4/yml/node.hpp"
#include <c4/std/vector.hpp>
#include <vector>
namespace c4 {
namespace yml {
// vector is a sequence-like type, and it requires child nodes
// in the data tree hierarchy (a SEQ node in ryml parlance).
// So it should be serialized via write()/read().
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)
n->append_child() << v;
}
template<class V, class Alloc>
bool read(c4::yml::ConstNodeRef const& n, std::vector<V, Alloc> *vec)
{
vec->resize(n.num_children());
size_t pos = 0;
for(auto const ch : n)
ch >> (*vec)[pos++];
return true;
}
/** 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());
size_t pos = 0;
bool tmp;
for(auto const ch : n)
{
ch >> tmp;
(*vec)[pos++] = tmp;
}
return true;
}
} // namespace yml
} // namespace c4
#endif // _C4_YML_STD_VECTOR_HPP_

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,229 @@
#ifndef _C4_YML_WRITER_HPP_
#define _C4_YML_WRITER_HPP_
#ifndef _C4_YML_COMMON_HPP_
#include "./common.hpp"
#endif
#include <c4/substr.hpp>
#include <stdio.h> // fwrite(), fputc()
#include <string.h> // memcpy()
namespace c4 {
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};
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** A writer that outputs to a file. Defaults to stdout. */
struct WriterFile
{
FILE * m_file;
size_t m_pos;
WriterFile(FILE *f = nullptr) : m_file(f ? f : stdout), m_pos(0) {}
inline substr _get(bool /*error_on_excess*/)
{
substr sp;
sp.str = nullptr;
sp.len = m_pos;
return sp;
}
template<size_t N>
inline void _do_write(const char (&a)[N])
{
fwrite(a, sizeof(char), N - 1, m_file);
m_pos += N - 1;
}
inline 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);
m_pos += sp.len;
#if defined(__clang__)
# pragma clang diagnostic pop
#elif defined(__GNUC__)
# pragma GCC diagnostic pop
#endif
}
inline void _do_write(const char c)
{
fputc(c, m_file);
++m_pos;
}
inline void _do_write(RepC const rc)
{
for(size_t i = 0; i < rc.num_times; ++i)
{
fputc(rc.c, m_file);
}
m_pos += rc.num_times;
}
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** A writer that outputs to an STL-like ostream. */
template<class OStream>
struct WriterOStream
{
OStream& m_stream;
size_t m_pos;
WriterOStream(OStream &s) : m_stream(s), m_pos(0) {}
inline substr _get(bool /*error_on_excess*/)
{
substr sp;
sp.str = nullptr;
sp.len = m_pos;
return sp;
}
template<size_t N>
inline void _do_write(const char (&a)[N])
{
m_stream.write(a, N - 1);
m_pos += N - 1;
}
inline 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);
m_pos += sp.len;
#if defined(__clang__)
# pragma clang diagnostic pop
#elif defined(__GNUC__)
# pragma GCC diagnostic pop
#endif
}
inline void _do_write(const char c)
{
m_stream.put(c);
++m_pos;
}
inline void _do_write(RepC const rc)
{
for(size_t i = 0; i < rc.num_times; ++i)
{
m_stream.put(rc.c);
}
m_pos += rc.num_times;
}
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** a writer to a substr */
struct WriterBuf
{
substr m_buf;
size_t m_pos;
WriterBuf(substr sp) : m_buf(sp), m_pos(0) {}
inline substr _get(bool error_on_excess)
{
if(m_pos <= m_buf.len)
{
return m_buf.first(m_pos);
}
if(error_on_excess)
{
c4::yml::error("not enough space in the given buffer");
}
substr sp;
sp.str = nullptr;
sp.len = m_pos;
return sp;
}
template<size_t N>
inline 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)
{
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)
{
if(m_pos + 1 <= m_buf.len)
{
m_buf[m_pos] = c;
}
++m_pos;
}
inline void _do_write(RepC const rc)
{
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;
}
};
} // namespace yml
} // namespace c4
#endif /* _C4_YML_WRITER_HPP_ */

View File

@ -0,0 +1,10 @@
#ifndef _C4_YML_YML_HPP_
#define _C4_YML_YML_HPP_
#include "c4/yml/tree.hpp"
#include "c4/yml/node.hpp"
#include "c4/yml/emit.hpp"
#include "c4/yml/parse.hpp"
#include "c4/yml/preprocess.hpp"
#endif // _C4_YML_YML_HPP_

View File

@ -0,0 +1,391 @@
# To make this file known to Qt Creator using:
# Tools > Options > Debugger > Locals & Expressions > Extra Debugging Helpers
# Any contents here will be picked up by GDB, LLDB, and CDB based
# debugging in Qt Creator automatically.
# Example to display a simple type
# template<typename U, typename V> struct MapNode
# {
# U key;
# V data;
# }
#
# def qdump__MapNode(d, value):
# d.putValue("This is the value column contents")
# d.putExpandable()
# if d.isExpanded():
# with Children(d):
# # Compact simple case.
# d.putSubItem("key", value["key"])
# # Same effect, with more customization possibilities.
# with SubItem(d, "data")
# d.putItem("data", value["data"])
# Check http://doc.qt.io/qtcreator/creator-debugging-helpers.html
# for more details or look at qttypes.py, stdtypes.py, boosttypes.py
# for more complex examples.
# to try parsing:
# env PYTHONPATH=/usr/share/qtcreator/debugger/ python src/ryml-gdbtypes.py
import dumper
#from dumper import Dumper, Value, Children, SubItem
#from dumper import SubItem, Children
from dumper import *
import sys
import os
# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------
# QtCreator makes it really hard to figure out problems in this code.
# So here are some debugging utilities.
# FIXME. this decorator is not working; find out why.
def dbglog(func):
"""a decorator that logs entry and exit of functions"""
if not _DBG:
return func
def func_wrapper(*args, **kwargs):
_dbg_enter(func.__name__)
ret = func(*args, **kwargs)
_dbg_exit(func.__name__)
return ret
return func_wrapper
_DBG = False
_dbg_log = None
_dbg_stack = 0
def _dbg(*args, **kwargs):
global _dbg_log, _dbg_stack
if not _DBG:
return
if _dbg_log is None:
filename = os.path.join(os.path.dirname(__file__), "dbg.txt")
_dbg_log = open(filename, "w")
kwargs['file'] = _dbg_log
kwargs['flush'] = True
print(" " * _dbg_stack, *args, **kwargs)
def _dbg_enter(name):
global _dbg_stack
_dbg(name, "- enter")
_dbg_stack += 1
def _dbg_exit(name):
global _dbg_stack
_dbg_stack -= 1
_dbg(name, "- exit!")
# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------
NPOS = 18446744073709551615
MAX_SUBSTR_LEN_DISPLAY = 80
MAX_SUBSTR_LEN_EXPAND = 1000
def get_str_value(d, value, limit=0):
# adapted from dumper.py::Dumper::putCharArrayValue()
m_str = value["str"].pointer()
m_len = value["len"].integer()
if m_len == NPOS:
_dbg("getstr... 1", m_len)
m_str = "!!!!!<npos>!!!!!"
m_len = len(m_str)
return m_str, m_len
if limit == 0:
limit = d.displayStringLimit
elided, shown = d.computeLimit(m_len, limit)
mem = bytes(d.readRawMemory(m_str, shown))
mem = mem.decode('utf8')
return mem, m_len
def __display_csubstr(d, value, limit=0):
m_str, m_len = get_str_value(d, value)
safe_len = min(m_len, MAX_SUBSTR_LEN_DISPLAY)
disp = m_str[0:safe_len]
# ensure the string escapes characters like \n\r\t etc
disp = disp.encode('unicode_escape').decode('utf8')
# WATCHOUT. quotes in the string will make qtcreator hang!!!
disp = disp.replace('"', '\\"')
disp = disp.replace('\'', '\\')
if m_len <= MAX_SUBSTR_LEN_DISPLAY:
d.putValue(f"[{m_len}] '{disp}'")
else:
d.putValue(f"[{m_len}] '{disp}'...")
return m_str, m_len
def qdump__c4__csubstr(d, value):
m_str, m_len = __display_csubstr(d, value)
d.putExpandable()
if d.isExpanded():
with Children(d):
safe_len = min(m_len, MAX_SUBSTR_LEN_EXPAND)
for i in range(safe_len):
ct = d.createType('char')
d.putSubItem(safe_len, d.createValue(value["str"].pointer() + i, ct))
d.putSubItem("len", value["len"])
d.putPtrItem("str", value["str"].pointer())
def qdump__c4__substr(d, value):
qdump__c4__csubstr(d, value)
def qdump__c4__basic_substring(d, value):
qdump__c4__csubstr(d, value)
def qdump__c4__yml__NodeScalar(d, value):
alen = value["anchor"]["len"].integer()
tlen = value["tag" ]["len"].integer()
m_str, m_len = get_str_value(d, value["scalar"])
if alen == 0 and tlen == 0:
d.putValue(f'\'{m_str}\'')
elif alen == 0 and tlen > 0:
d.putValue(f'\'{m_str}\' [Ta]')
elif alen > 0 and tlen == 0:
d.putValue(f'\'{m_str}\' [tA]')
elif alen > 0 and tlen > 0:
d.putValue(f'\'{m_str}\' [TA]')
d.putExpandable()
if d.isExpanded():
with Children(d):
d.putSubItem("[scalar]", value["scalar"])
if tlen > 0:
d.putSubItem("[tag]", value["tag"])
if alen > 0:
d.putSubItem("[anchor or ref]", value["anchor"])
def _format_enum_value(int_value, enum_map):
str_value = enum_map.get(int_value, None)
display = f'{int_value}' if str_value is None else f'{str_value} ({int_value})'
return display
def _format_bitmask_value(int_value, enum_map):
str_value = enum_map.get(int_value, None)
if str_value:
return f'{str_value} ({int_value})'
else:
out = ""
orig = int_value
# do in reverse to get compound flags first
for k, v in reversed(enum_map.items()):
if (k != 0):
if (int_value & k) == k:
if len(out) > 0:
out += '|'
out += v
int_value &= ~k
else:
if len(out) == 0 and int_value == 0:
return v
if out == "":
return f'{int_value}'
return f"{out} ({orig})"
def _c4bit(*ints):
ret = 0
for i in ints:
ret |= 1 << i
return ret
node_types = {
0: "NOTYPE",
_c4bit(0): "VAL" ,
_c4bit(1): "KEY" ,
_c4bit(2): "MAP" ,
_c4bit(3): "SEQ" ,
_c4bit(4): "DOC" ,
_c4bit(5,3): "STREAM",
_c4bit(6): "KEYREF" ,
_c4bit(7): "VALREF" ,
_c4bit(8): "KEYANCH" ,
_c4bit(9): "VALANCH" ,
_c4bit(10): "KEYTAG" ,
_c4bit(11): "VALTAG" ,
_c4bit(12): "VALQUO" ,
_c4bit(13): "KEYQUO" ,
_c4bit(1,0): "KEYVAL",
_c4bit(1,3): "KEYSEQ",
_c4bit(1,2): "KEYMAP",
_c4bit(4,2): "DOCMAP",
_c4bit(4,3): "DOCSEQ",
_c4bit(4,0): "DOCVAL",
#
_c4bit(14): "STYLE_FLOW_SL",
_c4bit(15): "STYLE_FLOW_ML",
_c4bit(16): "STYLE_BLOCK",
#
_c4bit(17): "KEY_LITERAL",
_c4bit(18): "VAL_LITERAL",
_c4bit(19): "KEY_FOLDED",
_c4bit(20): "VAL_FOLDED",
_c4bit(21): "KEY_SQUO",
_c4bit(22): "VAL_SQUO",
_c4bit(23): "KEY_DQUO",
_c4bit(24): "VAL_DQUO",
_c4bit(25): "KEY_PLAIN",
_c4bit(26): "VAL_PLAIN",
}
node_types_rev = {v: k for k, v in node_types.items()}
def _node_type_has_all(node_type_value, type_name):
exp = node_types_rev[type_name]
return (node_type_value & exp) == exp
def _node_type_has_any(node_type_value, type_name):
exp = node_types_rev[type_name]
return (node_type_value & exp) != 0
def qdump__c4__yml__NodeType_e(d, value):
v = _format_bitmask_value(value.integer(), node_types)
d.putValue(v)
def qdump__c4__yml__NodeType(d, value):
qdump__c4__yml__NodeType_e(d, value["type"])
def qdump__c4__yml__NodeData(d, value):
d.putValue("wtf")
ty = _format_bitmask_value(value.integer(), node_types)
t = value["m_type"]["type"].integer()
k = value["m_key"]["scalar"]
v = value["m_val"]["scalar"]
sk, lk = get_str_value(d, k)
sv, lv = get_str_value(d, v)
if _node_type_has_all(t, "KEYVAL"):
d.putValue(f"'{sk}': '{sv}' {ty}")
elif _node_type_has_any(t, "KEY"):
d.putValue(f"'{sk}': {ty}")
elif _node_type_has_any(t, "VAL"):
d.putValue(f"'{sv}' {ty}")
else:
d.putValue(f"{ty}")
d.putExpandable()
if d.isExpanded():
with Children(d):
d.putSubItem("m_type", value["m_type"])
# key
if _node_type_has_any(t, "KEY"):
d.putSubItem("m_key", value["m_key"])
if _node_type_has_any(t, "KEYREF"):
with SubItem(d, "m_key.ref"):
s_, _ = get_str_value(d, value["m_key"]["anchor"])
d.putValue(f"'{s_}'")
if _node_type_has_any(t, "KEYANCH"):
with SubItem(d, "m_key.anchor"):
s_, _ = get_str_value(d, value["m_key"]["anchor"])
d.putValue(f"'{s_}'")
if _node_type_has_any(t, "KEYTAG"):
with SubItem(d, "m_key.tag"):
s_, _ = get_str_value(d, value["m_key"]["tag"])
d.putValue(f"'{s_}'")
# val
if _node_type_has_any(t, "VAL"):
d.putSubItem("m_val", value["m_val"])
if _node_type_has_any(t, "VALREF"):
with SubItem(d, "m_val.ref"):
s_, _ = get_str_value(d, value["m_val"]["anchor"])
d.putValue(f"'{s_}'")
if _node_type_has_any(t, "VALANCH"):
with SubItem(d, "m_val.anchor"):
s_, _ = get_str_value(d, value["m_val"]["anchor"])
d.putValue(f"'{s_}'")
if _node_type_has_any(t, "VALTAG"):
with SubItem(d, "m_val.tag"):
s_, _ = get_str_value(d, value["m_val"]["tag"])
d.putValue(f"'{s_}'")
# hierarchy
_dump_node_index(d, "m_parent", value)
_dump_node_index(d, "m_first_child", value)
_dump_node_index(d, "m_last_child", value)
_dump_node_index(d, "m_next_sibling", value)
_dump_node_index(d, "m_prev_sibling", value)
def _dump_node_index(d, name, value):
if int(value[name].integer()) == NPOS:
pass
#with SubItem(d, name):
# d.putValue("-")
else:
d.putSubItem(name, value[name])
# c4::yml::Tree
def qdump__c4__yml__Tree(d, value):
m_size = value["m_size"].integer()
m_cap = value["m_cap"].integer()
d.putExpandable()
if d.isExpanded():
#d.putArrayData(value["m_buf"], m_size, value["m_buf"].dereference())
with Children(d):
with SubItem(d, f"[nodes]"):
d.putItemCount(m_size)
d.putArrayData(value["m_buf"].pointer(), m_size, value["m_buf"].type.dereference())
d.putPtrItem("m_buf", value["m_buf"].pointer())
d.putIntItem("m_size", value["m_size"])
d.putIntItem("m_cap (capacity)", value["m_cap"])
d.putIntItem("[slack]", m_cap - m_size)
d.putIntItem("m_free_head", value["m_free_head"])
d.putIntItem("m_free_tail", value["m_free_tail"])
d.putSubItem("m_arena", value["m_arena"])
def qdump__c4__yml__detail__stack(d, value):
T = value.type[0]
N = value.type[0]
m_size = value["m_size"].integer()
m_capacity = value["m_capacity"].integer()
d.putItemCount(m_size)
if d.isExpanded():
with Children(d):
with SubItem(d, f"[nodes]"):
d.putItemCount(m_size)
d.putArrayData(value["m_stack"].pointer(), m_size, T)
d.putIntItem("m_size", value["m_size"])
d.putIntItem("m_capacity", value["m_capacity"])
#d.putIntItem("[small capacity]", N)
d.putIntItem("[is large]", value["m_buf"].address() == value["m_stack"].pointer())
d.putPtrItem("m_stack", value["m_stack"].pointer())
d.putPtrItem("m_buf", value["m_buf"].address())
def qdump__c4__yml__detail__ReferenceResolver__refdata(d, value):
node = value["node"].integer()
ty = _format_bitmask_value(value["type"].integer(), node_types)
d.putValue(f'{node} {ty}')
d.putExpandable()
if d.isExpanded():
with Children(d):
d.putSubItem("type", value["type"])
d.putSubItem("node", value["node"])
_dump_node_index(d, "prev_anchor", value)
_dump_node_index(d, "target", value)
_dump_node_index(d, "parent_ref", value)
_dump_node_index(d, "parent_ref_sibling", value)

View File

@ -0,0 +1,11 @@
#ifndef _RYML_HPP_
#define _RYML_HPP_
#include "c4/yml/yml.hpp"
namespace ryml {
using namespace c4::yml;
using namespace c4;
}
#endif /* _RYML_HPP_ */

View File

@ -0,0 +1,194 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Very good intro:
@see https://docs.microsoft.com/en-us/visualstudio/debugger/create-custom-views-of-native-objects?view=vs-2017
@see https://code.msdn.microsoft.com/windowsdesktop/Writing-type-visualizers-2eae77a2
See also:
@see http://blogs.msdn.com/b/vcblog/archive/2013/06/28/using-visual-studio-2013-to-write-maintainable-native-visualizations-natvis.aspx?PageIndex=2
@see http://blogs.msdn.com/b/vcblog/archive/2015/09/28/debug-visualizers-in-visual-c-2015.aspx
@see http://stackoverflow.com/questions/36883414/limit-display-of-char-in-natvis-file-to-specific-length
-->
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="c4::yml::NodeScalar">
<DisplayString Condition="(tag.len == 0) &amp;&amp; (anchor.len == 0)">{scalar.str,[scalar.len]}</DisplayString>
<DisplayString Condition="(tag.len > 0) &amp;&amp; (anchor.len == 0)">{scalar.str,[scalar.len]} [T]</DisplayString>
<DisplayString Condition="(tag.len == 0) &amp;&amp; (anchor.len > 0)">{scalar.str,[scalar.len]} [A]</DisplayString>
<DisplayString Condition="(tag.len > 0) &amp;&amp; (anchor.len > 0)">{scalar.str,[scalar.len]} [T][A]</DisplayString>
<Expand>
<Item Name="scalar">scalar</Item>
<Item Name="tag">tag</Item>
<Item Name="anchor">anchor</Item>
</Expand>
</Type>
<Type Name="c4::yml::NodeType">
<DisplayString>{type}</DisplayString>
<Expand>
<Synthetic Name="[enabled 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="[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>
<Item Name="[5]" Condition="(type &amp; c4::yml::STREAM) != 0">c4::yml::STREAM</Item>
<Item Name="[6]" Condition="(type &amp; c4::yml::KEYREF) != 0">c4::yml::KEYREF</Item>
<Item Name="[7]" Condition="(type &amp; c4::yml::VALREF) != 0">c4::yml::VALREF</Item>
<Item Name="[8]" Condition="(type &amp; c4::yml::KEYANCH) != 0">c4::yml::KEYANCH</Item>
<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>
</Expand>
</Type>
<Type Name="c4::yml::NodeData">
<DisplayString Condition="((m_type.type &amp; c4::yml::KEY ) == c4::yml::KEY) &amp;&amp; ((m_type.type &amp; c4::yml::VAL) == c4::yml::VAL)">[KEYVAL] {m_key.scalar.str,[m_key.scalar.len]}: {m_val.scalar.str,[m_val.scalar.len]}</DisplayString>
<DisplayString Condition="((m_type.type &amp; c4::yml::KEY ) == c4::yml::KEY) &amp;&amp; ((m_type.type &amp; c4::yml::SEQ) == c4::yml::SEQ)">[KEYSEQ] {m_key.scalar.str,[m_key.scalar.len]}</DisplayString>
<DisplayString Condition="((m_type.type &amp; c4::yml::KEY ) == c4::yml::KEY) &amp;&amp; ((m_type.type &amp; c4::yml::MAP) == c4::yml::MAP)">[KEYMAP] {m_key.scalar.str,[m_key.scalar.len]}</DisplayString>
<DisplayString Condition="((m_type.type &amp; c4::yml::DOC ) == c4::yml::DOC) &amp;&amp; ((m_type.type &amp; c4::yml::SEQ) == c4::yml::SEQ)">[DOCSEQ]</DisplayString>
<DisplayString Condition="((m_type.type &amp; c4::yml::DOC ) == c4::yml::DOC) &amp;&amp; ((m_type.type &amp; c4::yml::MAP) == c4::yml::MAP)">[DOCMAP]</DisplayString>
<DisplayString Condition="(m_type.type &amp; c4::yml::VAL ) == c4::yml::VAL" >[VAL] {m_val.scalar.str,[m_val.scalar.len]}</DisplayString>
<DisplayString Condition="(m_type.type &amp; c4::yml::KEY ) == c4::yml::KEY" >[KEY] {m_key.scalar.str,[m_key.scalar.len]}</DisplayString>
<DisplayString Condition="(m_type.type &amp; c4::yml::SEQ ) == c4::yml::SEQ" >[SEQ]</DisplayString>
<DisplayString Condition="(m_type.type &amp; c4::yml::MAP ) == c4::yml::MAP" >[MAP]</DisplayString>
<DisplayString Condition="(m_type.type &amp; c4::yml::DOC ) == c4::yml::DOC" >[DOC]</DisplayString>
<DisplayString Condition="(m_type.type &amp; c4::yml::STREAM) == c4::yml::STREAM">[STREAM]</DisplayString>
<DisplayString Condition="(m_type.type &amp; c4::yml::NOTYPE) == c4::yml::NOTYPE">[NOTYPE]</DisplayString>
<Expand>
<Item Name="type">m_type</Item>
<Item Name="key" Condition="(m_type.type &amp; c4::yml::KEY) != 0">m_key</Item>
<Item Name="val" Condition="(m_type.type &amp; c4::yml::VAL) != 0">m_val</Item>
<Item Name="key quoted" Condition="((m_type.type &amp; c4::yml::KEY) != 0) &amp;&amp; ((m_type.type &amp; c4::yml::KEYQUO) != 0)">c4::yml::KEYQUO</Item>
<Item Name="val quoted" Condition="((m_type.type &amp; c4::yml::VAL) != 0) &amp;&amp; ((m_type.type &amp; c4::yml::VALQUO) != 0)">c4::yml::VALQUO</Item>
<Item Name="key ref" Condition="(m_type.type &amp; c4::yml::KEYREF) != 0">m_key.anchor</Item>
<Item Name="val ref" Condition="(m_type.type &amp; c4::yml::VALREF) != 0">m_val.anchor</Item>
<Item Name="key anchor" Condition="(m_type.type &amp; c4::yml::KEYANCH) != 0">m_key.anchor</Item>
<Item Name="val anchor" Condition="(m_type.type &amp; c4::yml::VALANCH) != 0">m_val.anchor</Item>
<Item Name="parent">m_parent</Item>
<Item Name="first child" Condition="m_first_child != c4::yml::NONE">m_first_child</Item>
<Item Name="last child" Condition="m_last_child != c4::yml::NONE">m_last_child</Item>
<Item Name="prev sibling" Condition="m_prev_sibling != c4::yml::NONE">m_prev_sibling</Item>
<Item Name="next sibling" Condition="m_next_sibling != c4::yml::NONE">m_next_sibling</Item>
</Expand>
</Type>
<Type Name="c4::yml::Tree">
<DisplayString>sz={m_size}, cap={m_cap}</DisplayString>
<Expand>
<Item Name="[size]">m_size</Item>
<Item Name="[capacity]">m_cap</Item>
<Synthetic Name="[buffer]">
<Expand>
<ArrayItems>
<Size>m_cap</Size>
<ValuePointer>m_buf</ValuePointer>
</ArrayItems>
</Expand>
</Synthetic>
<Item Name="free head">m_free_head</Item>
<Item Name="arena">m_arena</Item>
</Expand>
</Type>
<Type Name="c4::yml::Tree::_lookup_path_token">
<DisplayString>{value} ({type})</DisplayString>
<Expand>
<Item Name="value">value</Item>
<Item Name="type">type</Item>
</Expand>
</Type>
<Type Name="c4::yml::Tree::lookup_result">
<DisplayString>{path} -- target={target} closest={closest}</DisplayString>
<Expand>
<Item Name="target">target</Item>
<Item Name="closest">closest</Item>
<Item Name="path_pos">path_pos</Item>
<Item Name="path">path</Item>
<Synthetic Name="[resolved]">
<DisplayString>{path.str,[path_pos]}</DisplayString>
</Synthetic>
<Synthetic Name="[unresolved]">
<DisplayString>{path.str+path_pos,[path.len-path_pos]}</DisplayString>
</Synthetic>
</Expand>
</Type>
<Type Name="c4::yml::NodeRef">
<DisplayString Condition="(m_id == c4::yml::NONE)">(void)</DisplayString>
<DisplayString Condition="(m_seed.len != c4::yml::NONE) &amp;&amp; (m_seed.str == nullptr)">[INDEX SEED for] {*(m_tree->m_buf + m_id)}</DisplayString>
<DisplayString Condition="(m_seed.len != c4::yml::NONE) &amp;&amp; (m_seed.str != nullptr)">[NAMED SEED for] {*(m_tree->m_buf + m_id)}</DisplayString>
<DisplayString>{*(m_tree->m_buf + m_id)}</DisplayString>
<Expand>
<Item Name="id">m_id</Item>
<Item Name="elm">*(m_tree->m_buf + m_id)</Item>
<Item Name="tree">m_tree</Item>
<Synthetic Name="[children]" Condition="(m_id != c4::yml::NONE) &amp;&amp; ((m_tree->m_buf + m_id)->m_type.type &amp; (c4::yml::MAP|c4::yml::SEQ) != 0)">
<Expand>
<CustomListItems>
<Variable Name="tree" InitialValue="m_tree"/>
<Variable Name="buf" InitialValue="m_tree->m_buf"/>
<Variable Name="curr" InitialValue="(m_tree->m_buf + m_id)->m_first_child"/>
<Loop>
<Item>buf + curr</Item>
<Exec>curr = (buf + curr)->m_next_sibling</Exec>
<Break Condition="curr == c4::yml::NONE"/>
</Loop>
</CustomListItems>
</Expand>
</Synthetic>
</Expand>
</Type>
<Type Name="c4::yml::detail::ReferenceResolver">
<DisplayString>#refs={refs.m_size} #nodes={t->m_size}</DisplayString>
<Expand>
<Synthetic Name="[ref_nodes]">
<Expand>
<CustomListItems>
<Variable Name="curr" InitialValue="0"/>
<Loop>
<Item>t->m_buf + (refs.m_stack + curr)->node</Item>
<Exec>curr = curr+1</Exec>
<Break Condition="curr >= refs.m_size"/>
</Loop>
</CustomListItems>
</Expand>
</Synthetic>
<Synthetic Name="[refs]">
<Expand>
<ArrayItems>
<Size>refs.m_size</Size>
<ValuePointer>refs.m_stack</ValuePointer>
</ArrayItems>
</Expand>
</Synthetic>
<Item Name="[tree]">t</Item>
</Expand>
</Type>
<Type Name="c4::yml::detail::stack&lt;*,*&gt;">
<DisplayString>sz={m_size} cap={m_capacity}</DisplayString>
<Expand>
<Item Name="[size]">m_size</Item>
<Item Name="[capacity]">m_capacity</Item>
<Item Name="[is small]">m_buf == m_stack</Item>
<Synthetic Name="[items]">
<Expand>
<ArrayItems>
<Size>m_size</Size>
<ValuePointer>m_stack</ValuePointer>
</ArrayItems>
</Expand>
</Synthetic>
</Expand>
</Type>
</AutoVisualizer>

View File

@ -0,0 +1,6 @@
#ifndef _RYML_STD_HPP_
#define _RYML_STD_HPP_
#include "./c4/yml/std/std.hpp"
#endif /* _RYML_STD_HPP_ */