dep: Add rapidyaml

This commit is contained in:
Stenzek 2024-02-04 02:18:15 +10:00
parent f7bed69e7c
commit 5c08fa9d00
No known key found for this signature in database
68 changed files with 25758 additions and 0 deletions

View file

@ -29,6 +29,8 @@ add_subdirectory(reshadefx EXCLUDE_FROM_ALL)
disable_compiler_warnings_for_target(reshadefx) disable_compiler_warnings_for_target(reshadefx)
add_subdirectory(rcheevos EXCLUDE_FROM_ALL) add_subdirectory(rcheevos EXCLUDE_FROM_ALL)
disable_compiler_warnings_for_target(rcheevos) disable_compiler_warnings_for_target(rcheevos)
add_subdirectory(rapidyaml EXCLUDE_FROM_ALL)
disable_compiler_warnings_for_target(rapidyaml)
# Build dependencies on Windows/Android. # Build dependencies on Windows/Android.
if(WIN32 OR ANDROID) if(WIN32 OR ANDROID)

View file

@ -0,0 +1,75 @@
add_library(rapidyaml
include/c4/base64.hpp
include/c4/blob.hpp
include/c4/charconv.hpp
include/c4/compiler.hpp
include/c4/config.hpp
include/c4/cpu.hpp
include/c4/dump.hpp
include/c4/error.hpp
include/c4/export.hpp
include/c4/format.hpp
include/c4/language.hpp
include/c4/memory_util.hpp
include/c4/platform.hpp
include/c4/preprocessor.hpp
include/c4/std/std.hpp
include/c4/std/std_fwd.hpp
include/c4/std/string.hpp
include/c4/std/string_fwd.hpp
include/c4/std/string_view.hpp
include/c4/std/tuple.hpp
include/c4/std/vector.hpp
include/c4/std/vector_fwd.hpp
include/c4/substr.hpp
include/c4/substr_fwd.hpp
include/c4/szconv.hpp
include/c4/types.hpp
include/c4/utf.hpp
include/c4/windows.hpp
include/c4/windows_pop.hpp
include/c4/windows_push.hpp
include/c4/yml/common.hpp
include/c4/yml/detail/parser_dbg.hpp
include/c4/yml/detail/stack.hpp
include/c4/yml/emit.def.hpp
include/c4/yml/emit.hpp
include/c4/yml/export.hpp
include/c4/yml/node.hpp
include/c4/yml/parse.hpp
include/c4/yml/preprocess.hpp
include/c4/yml/std/map.hpp
include/c4/yml/std/std.hpp
include/c4/yml/std/string.hpp
include/c4/yml/std/vector.hpp
include/c4/yml/tree.hpp
include/c4/yml/writer.hpp
include/c4/yml/yml.hpp
include/ryml.hpp
include/ryml_std.hpp
src/c4/base64.cpp
src/c4/error.cpp
src/c4/format.cpp
src/c4/language.cpp
src/c4/memory_util.cpp
src/c4/utf.cpp
src/c4/yml/common.cpp
src/c4/yml/node.cpp
src/c4/yml/parse.cpp
src/c4/yml/preprocess.cpp
src/c4/yml/tree.cpp
)
target_include_directories(rapidyaml PRIVATE
"${CMAKE_CURRENT_SOURCE_DIR}/include"
"${CMAKE_CURRENT_SOURCE_DIR}/src"
"${CMAKE_CURRENT_SOURCE_DIR}/../fast_float/include"
)
target_include_directories(rapidyaml INTERFACE
"${CMAKE_CURRENT_SOURCE_DIR}/include"
)
target_compile_definitions(rapidyaml PUBLIC
"C4_NO_DEBUG_BREAK"
)

20
dep/rapidyaml/LICENSE.txt Normal file
View file

@ -0,0 +1,20 @@
Copyright (c) 2018, Joao Paulo Magalhaes <dev@jpmag.me>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

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_ */

View file

@ -0,0 +1,85 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\msvc\vsprops\Configurations.props" />
<PropertyGroup Label="Globals">
<ProjectGuid>{1AD23A8A-4C20-434C-AE6B-0E07759EEB1E}</ProjectGuid>
</PropertyGroup>
<ItemGroup>
<ClInclude Include="include\c4\base64.hpp" />
<ClInclude Include="include\c4\blob.hpp" />
<ClInclude Include="include\c4\charconv.hpp" />
<ClInclude Include="include\c4\compiler.hpp" />
<ClInclude Include="include\c4\config.hpp" />
<ClInclude Include="include\c4\cpu.hpp" />
<ClInclude Include="include\c4\dump.hpp" />
<ClInclude Include="include\c4\error.hpp" />
<ClInclude Include="include\c4\export.hpp" />
<ClInclude Include="include\c4\format.hpp" />
<ClInclude Include="include\c4\language.hpp" />
<ClInclude Include="include\c4\memory_util.hpp" />
<ClInclude Include="include\c4\platform.hpp" />
<ClInclude Include="include\c4\preprocessor.hpp" />
<ClInclude Include="include\c4\std\std.hpp" />
<ClInclude Include="include\c4\std\std_fwd.hpp" />
<ClInclude Include="include\c4\std\string.hpp" />
<ClInclude Include="include\c4\std\string_fwd.hpp" />
<ClInclude Include="include\c4\std\string_view.hpp" />
<ClInclude Include="include\c4\std\tuple.hpp" />
<ClInclude Include="include\c4\std\vector.hpp" />
<ClInclude Include="include\c4\std\vector_fwd.hpp" />
<ClInclude Include="include\c4\substr.hpp" />
<ClInclude Include="include\c4\substr_fwd.hpp" />
<ClInclude Include="include\c4\szconv.hpp" />
<ClInclude Include="include\c4\types.hpp" />
<ClInclude Include="include\c4\utf.hpp" />
<ClInclude Include="include\c4\windows.hpp" />
<ClInclude Include="include\c4\windows_pop.hpp" />
<ClInclude Include="include\c4\windows_push.hpp" />
<ClInclude Include="include\c4\yml\common.hpp" />
<ClInclude Include="include\c4\yml\detail\checks.hpp" />
<ClInclude Include="include\c4\yml\detail\parser_dbg.hpp" />
<ClInclude Include="include\c4\yml\detail\print.hpp" />
<ClInclude Include="include\c4\yml\detail\stack.hpp" />
<ClInclude Include="include\c4\yml\emit.def.hpp" />
<ClInclude Include="include\c4\yml\emit.hpp" />
<ClInclude Include="include\c4\yml\export.hpp" />
<ClInclude Include="include\c4\yml\node.hpp" />
<ClInclude Include="include\c4\yml\parse.hpp" />
<ClInclude Include="include\c4\yml\preprocess.hpp" />
<ClInclude Include="include\c4\yml\std\map.hpp" />
<ClInclude Include="include\c4\yml\std\std.hpp" />
<ClInclude Include="include\c4\yml\std\string.hpp" />
<ClInclude Include="include\c4\yml\std\vector.hpp" />
<ClInclude Include="include\c4\yml\tree.hpp" />
<ClInclude Include="include\c4\yml\writer.hpp" />
<ClInclude Include="include\c4\yml\yml.hpp" />
<ClInclude Include="include\ryml.hpp" />
<ClInclude Include="include\ryml_std.hpp" />
</ItemGroup>
<ItemGroup>
<Natvis Include="include\c4\c4core.natvis" />
<Natvis Include="include\ryml.natvis" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\c4\base64.cpp" />
<ClCompile Include="src\c4\error.cpp" />
<ClCompile Include="src\c4\format.cpp" />
<ClCompile Include="src\c4\language.cpp" />
<ClCompile Include="src\c4\memory_util.cpp" />
<ClCompile Include="src\c4\utf.cpp" />
<ClCompile Include="src\c4\yml\common.cpp" />
<ClCompile Include="src\c4\yml\node.cpp" />
<ClCompile Include="src\c4\yml\parse.cpp" />
<ClCompile Include="src\c4\yml\preprocess.cpp" />
<ClCompile Include="src\c4\yml\tree.cpp" />
</ItemGroup>
<Import Project="..\msvc\vsprops\StaticLibrary.props" />
<ItemDefinitionGroup>
<ClCompile>
<WarningLevel>TurnOffAllWarnings</WarningLevel>
<AdditionalIncludeDirectories>$(ProjectDir)src;$(ProjectDir)include;$(ProjectDir)..\fast_float\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>C4_NO_DEBUG_BREAK;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
</ItemDefinitionGroup>
<Import Project="..\msvc\vsprops\Targets.props" />
</Project>

View file

@ -0,0 +1,209 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="yml">
<UniqueIdentifier>{c002fb0c-6a19-4827-8a7a-19e0a309fb0c}</UniqueIdentifier>
</Filter>
<Filter Include="yml\detail">
<UniqueIdentifier>{a3c32dd2-c04e-4122-8aa7-75c26954f557}</UniqueIdentifier>
</Filter>
<Filter Include="yml\std">
<UniqueIdentifier>{df1d37c9-df20-47f2-9031-60d4fa1f8c69}</UniqueIdentifier>
</Filter>
<Filter Include="c4">
<UniqueIdentifier>{6df19960-803e-4406-a9e6-43eaa256d94d}</UniqueIdentifier>
</Filter>
<Filter Include="c4\std">
<UniqueIdentifier>{c53d424e-00a4-4c3c-89f4-f67c57cdee8f}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="include\c4\yml\detail\parser_dbg.hpp">
<Filter>yml\detail</Filter>
</ClInclude>
<ClInclude Include="include\c4\yml\detail\print.hpp">
<Filter>yml\detail</Filter>
</ClInclude>
<ClInclude Include="include\c4\yml\detail\stack.hpp">
<Filter>yml\detail</Filter>
</ClInclude>
<ClInclude Include="include\c4\yml\detail\checks.hpp">
<Filter>yml\detail</Filter>
</ClInclude>
<ClInclude Include="include\c4\yml\std\string.hpp">
<Filter>yml\std</Filter>
</ClInclude>
<ClInclude Include="include\c4\yml\std\vector.hpp">
<Filter>yml\std</Filter>
</ClInclude>
<ClInclude Include="include\c4\yml\std\map.hpp">
<Filter>yml\std</Filter>
</ClInclude>
<ClInclude Include="include\c4\yml\std\std.hpp">
<Filter>yml\std</Filter>
</ClInclude>
<ClInclude Include="include\c4\yml\yml.hpp">
<Filter>yml</Filter>
</ClInclude>
<ClInclude Include="include\c4\yml\common.hpp">
<Filter>yml</Filter>
</ClInclude>
<ClInclude Include="include\c4\yml\emit.def.hpp">
<Filter>yml</Filter>
</ClInclude>
<ClInclude Include="include\c4\yml\emit.hpp">
<Filter>yml</Filter>
</ClInclude>
<ClInclude Include="include\c4\yml\export.hpp">
<Filter>yml</Filter>
</ClInclude>
<ClInclude Include="include\c4\yml\node.hpp">
<Filter>yml</Filter>
</ClInclude>
<ClInclude Include="include\c4\yml\parse.hpp">
<Filter>yml</Filter>
</ClInclude>
<ClInclude Include="include\c4\yml\preprocess.hpp">
<Filter>yml</Filter>
</ClInclude>
<ClInclude Include="include\c4\yml\tree.hpp">
<Filter>yml</Filter>
</ClInclude>
<ClInclude Include="include\c4\yml\writer.hpp">
<Filter>yml</Filter>
</ClInclude>
<ClInclude Include="include\c4\base64.hpp">
<Filter>c4</Filter>
</ClInclude>
<ClInclude Include="include\c4\blob.hpp">
<Filter>c4</Filter>
</ClInclude>
<ClInclude Include="include\c4\charconv.hpp">
<Filter>c4</Filter>
</ClInclude>
<ClInclude Include="include\c4\compiler.hpp">
<Filter>c4</Filter>
</ClInclude>
<ClInclude Include="include\c4\config.hpp">
<Filter>c4</Filter>
</ClInclude>
<ClInclude Include="include\c4\cpu.hpp">
<Filter>c4</Filter>
</ClInclude>
<ClInclude Include="include\c4\dump.hpp">
<Filter>c4</Filter>
</ClInclude>
<ClInclude Include="include\c4\error.hpp">
<Filter>c4</Filter>
</ClInclude>
<ClInclude Include="include\c4\export.hpp">
<Filter>c4</Filter>
</ClInclude>
<ClInclude Include="include\c4\format.hpp">
<Filter>c4</Filter>
</ClInclude>
<ClInclude Include="include\c4\language.hpp">
<Filter>c4</Filter>
</ClInclude>
<ClInclude Include="include\c4\memory_util.hpp">
<Filter>c4</Filter>
</ClInclude>
<ClInclude Include="include\c4\platform.hpp">
<Filter>c4</Filter>
</ClInclude>
<ClInclude Include="include\c4\preprocessor.hpp">
<Filter>c4</Filter>
</ClInclude>
<ClInclude Include="include\c4\substr.hpp">
<Filter>c4</Filter>
</ClInclude>
<ClInclude Include="include\c4\substr_fwd.hpp">
<Filter>c4</Filter>
</ClInclude>
<ClInclude Include="include\c4\szconv.hpp">
<Filter>c4</Filter>
</ClInclude>
<ClInclude Include="include\c4\types.hpp">
<Filter>c4</Filter>
</ClInclude>
<ClInclude Include="include\c4\utf.hpp">
<Filter>c4</Filter>
</ClInclude>
<ClInclude Include="include\c4\windows.hpp">
<Filter>c4</Filter>
</ClInclude>
<ClInclude Include="include\c4\windows_pop.hpp">
<Filter>c4</Filter>
</ClInclude>
<ClInclude Include="include\c4\windows_push.hpp">
<Filter>c4</Filter>
</ClInclude>
<ClInclude Include="include\c4\std\string.hpp">
<Filter>c4\std</Filter>
</ClInclude>
<ClInclude Include="include\c4\std\string_fwd.hpp">
<Filter>c4\std</Filter>
</ClInclude>
<ClInclude Include="include\c4\std\string_view.hpp">
<Filter>c4\std</Filter>
</ClInclude>
<ClInclude Include="include\c4\std\tuple.hpp">
<Filter>c4\std</Filter>
</ClInclude>
<ClInclude Include="include\c4\std\vector.hpp">
<Filter>c4\std</Filter>
</ClInclude>
<ClInclude Include="include\c4\std\vector_fwd.hpp">
<Filter>c4\std</Filter>
</ClInclude>
<ClInclude Include="include\c4\std\std.hpp">
<Filter>c4\std</Filter>
</ClInclude>
<ClInclude Include="include\c4\std\std_fwd.hpp">
<Filter>c4\std</Filter>
</ClInclude>
<ClInclude Include="include\ryml.hpp" />
<ClInclude Include="include\ryml_std.hpp" />
</ItemGroup>
<ItemGroup>
<Natvis Include="include\c4\c4core.natvis">
<Filter>c4</Filter>
</Natvis>
<Natvis Include="include\ryml.natvis" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\c4\base64.cpp">
<Filter>c4</Filter>
</ClCompile>
<ClCompile Include="src\c4\error.cpp">
<Filter>c4</Filter>
</ClCompile>
<ClCompile Include="src\c4\format.cpp">
<Filter>c4</Filter>
</ClCompile>
<ClCompile Include="src\c4\language.cpp">
<Filter>c4</Filter>
</ClCompile>
<ClCompile Include="src\c4\memory_util.cpp">
<Filter>c4</Filter>
</ClCompile>
<ClCompile Include="src\c4\utf.cpp">
<Filter>c4</Filter>
</ClCompile>
<ClCompile Include="src\c4\yml\parse.cpp">
<Filter>yml</Filter>
</ClCompile>
<ClCompile Include="src\c4\yml\preprocess.cpp">
<Filter>yml</Filter>
</ClCompile>
<ClCompile Include="src\c4\yml\tree.cpp">
<Filter>yml</Filter>
</ClCompile>
<ClCompile Include="src\c4\yml\common.cpp">
<Filter>yml</Filter>
</ClCompile>
<ClCompile Include="src\c4\yml\node.cpp">
<Filter>yml</Filter>
</ClCompile>
</ItemGroup>
</Project>

View file

@ -0,0 +1,221 @@
#include "c4/base64.hpp"
#ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wchar-subscripts" // array subscript is of type 'char'
# pragma clang diagnostic ignored "-Wold-style-cast"
#elif defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wchar-subscripts"
# pragma GCC diagnostic ignored "-Wtype-limits"
# pragma GCC diagnostic ignored "-Wold-style-cast"
#endif
namespace c4 {
namespace detail {
constexpr static const char base64_sextet_to_char_[64] = {
/* 0/ 65*/ 'A', /* 1/ 66*/ 'B', /* 2/ 67*/ 'C', /* 3/ 68*/ 'D',
/* 4/ 69*/ 'E', /* 5/ 70*/ 'F', /* 6/ 71*/ 'G', /* 7/ 72*/ 'H',
/* 8/ 73*/ 'I', /* 9/ 74*/ 'J', /*10/ 75*/ 'K', /*11/ 74*/ 'L',
/*12/ 77*/ 'M', /*13/ 78*/ 'N', /*14/ 79*/ 'O', /*15/ 78*/ 'P',
/*16/ 81*/ 'Q', /*17/ 82*/ 'R', /*18/ 83*/ 'S', /*19/ 82*/ 'T',
/*20/ 85*/ 'U', /*21/ 86*/ 'V', /*22/ 87*/ 'W', /*23/ 88*/ 'X',
/*24/ 89*/ 'Y', /*25/ 90*/ 'Z', /*26/ 97*/ 'a', /*27/ 98*/ 'b',
/*28/ 99*/ 'c', /*29/100*/ 'd', /*30/101*/ 'e', /*31/102*/ 'f',
/*32/103*/ 'g', /*33/104*/ 'h', /*34/105*/ 'i', /*35/106*/ 'j',
/*36/107*/ 'k', /*37/108*/ 'l', /*38/109*/ 'm', /*39/110*/ 'n',
/*40/111*/ 'o', /*41/112*/ 'p', /*42/113*/ 'q', /*43/114*/ 'r',
/*44/115*/ 's', /*45/116*/ 't', /*46/117*/ 'u', /*47/118*/ 'v',
/*48/119*/ 'w', /*49/120*/ 'x', /*50/121*/ 'y', /*51/122*/ 'z',
/*52/ 48*/ '0', /*53/ 49*/ '1', /*54/ 50*/ '2', /*55/ 51*/ '3',
/*56/ 52*/ '4', /*57/ 53*/ '5', /*58/ 54*/ '6', /*59/ 55*/ '7',
/*60/ 56*/ '8', /*61/ 57*/ '9', /*62/ 43*/ '+', /*63/ 47*/ '/',
};
// https://www.cs.cmu.edu/~pattis/15-1XX/common/handouts/ascii.html
constexpr static const char base64_char_to_sextet_[128] = {
#define __ char(-1) // undefined below
/* 0 NUL*/ __, /* 1 SOH*/ __, /* 2 STX*/ __, /* 3 ETX*/ __,
/* 4 EOT*/ __, /* 5 ENQ*/ __, /* 6 ACK*/ __, /* 7 BEL*/ __,
/* 8 BS */ __, /* 9 TAB*/ __, /* 10 LF */ __, /* 11 VT */ __,
/* 12 FF */ __, /* 13 CR */ __, /* 14 SO */ __, /* 15 SI */ __,
/* 16 DLE*/ __, /* 17 DC1*/ __, /* 18 DC2*/ __, /* 19 DC3*/ __,
/* 20 DC4*/ __, /* 21 NAK*/ __, /* 22 SYN*/ __, /* 23 ETB*/ __,
/* 24 CAN*/ __, /* 25 EM */ __, /* 26 SUB*/ __, /* 27 ESC*/ __,
/* 28 FS */ __, /* 29 GS */ __, /* 30 RS */ __, /* 31 US */ __,
/* 32 SPC*/ __, /* 33 ! */ __, /* 34 " */ __, /* 35 # */ __,
/* 36 $ */ __, /* 37 % */ __, /* 38 & */ __, /* 39 ' */ __,
/* 40 ( */ __, /* 41 ) */ __, /* 42 * */ __, /* 43 + */ 62,
/* 44 , */ __, /* 45 - */ __, /* 46 . */ __, /* 47 / */ 63,
/* 48 0 */ 52, /* 49 1 */ 53, /* 50 2 */ 54, /* 51 3 */ 55,
/* 52 4 */ 56, /* 53 5 */ 57, /* 54 6 */ 58, /* 55 7 */ 59,
/* 56 8 */ 60, /* 57 9 */ 61, /* 58 : */ __, /* 59 ; */ __,
/* 60 < */ __, /* 61 = */ __, /* 62 > */ __, /* 63 ? */ __,
/* 64 @ */ __, /* 65 A */ 0, /* 66 B */ 1, /* 67 C */ 2,
/* 68 D */ 3, /* 69 E */ 4, /* 70 F */ 5, /* 71 G */ 6,
/* 72 H */ 7, /* 73 I */ 8, /* 74 J */ 9, /* 75 K */ 10,
/* 76 L */ 11, /* 77 M */ 12, /* 78 N */ 13, /* 79 O */ 14,
/* 80 P */ 15, /* 81 Q */ 16, /* 82 R */ 17, /* 83 S */ 18,
/* 84 T */ 19, /* 85 U */ 20, /* 86 V */ 21, /* 87 W */ 22,
/* 88 X */ 23, /* 89 Y */ 24, /* 90 Z */ 25, /* 91 [ */ __,
/* 92 \ */ __, /* 93 ] */ __, /* 94 ^ */ __, /* 95 _ */ __,
/* 96 ` */ __, /* 97 a */ 26, /* 98 b */ 27, /* 99 c */ 28,
/*100 d */ 29, /*101 e */ 30, /*102 f */ 31, /*103 g */ 32,
/*104 h */ 33, /*105 i */ 34, /*106 j */ 35, /*107 k */ 36,
/*108 l */ 37, /*109 m */ 38, /*110 n */ 39, /*111 o */ 40,
/*112 p */ 41, /*113 q */ 42, /*114 r */ 43, /*115 s */ 44,
/*116 t */ 45, /*117 u */ 46, /*118 v */ 47, /*119 w */ 48,
/*120 x */ 49, /*121 y */ 50, /*122 z */ 51, /*123 { */ __,
/*124 | */ __, /*125 } */ __, /*126 ~ */ __, /*127 DEL*/ __,
#undef __
};
#ifndef NDEBUG
void base64_test_tables()
{
for(size_t i = 0; i < C4_COUNTOF(detail::base64_sextet_to_char_); ++i)
{
char s2c = base64_sextet_to_char_[i];
char c2s = base64_char_to_sextet_[(int)s2c];
C4_CHECK((size_t)c2s == i);
}
for(size_t i = 0; i < C4_COUNTOF(detail::base64_char_to_sextet_); ++i)
{
char c2s = base64_char_to_sextet_[i];
if(c2s == char(-1))
continue;
char s2c = base64_sextet_to_char_[(int)c2s];
C4_CHECK((size_t)s2c == i);
}
}
#endif
} // namespace detail
bool base64_valid(csubstr encoded)
{
if(encoded.len & 3u) // (encoded.len % 4u)
return false;
for(const char c : encoded)
{
if(c < 0/* || c >= 128*/)
return false;
if(c == '=')
continue;
if(detail::base64_char_to_sextet_[c] == char(-1))
return false;
}
return true;
}
size_t base64_encode(substr buf, cblob data)
{
#define c4append_(c) { if(pos < buf.len) { buf.str[pos] = (c); } ++pos; }
#define c4append_idx_(char_idx) \
{\
C4_XASSERT((char_idx) < sizeof(detail::base64_sextet_to_char_));\
c4append_(detail::base64_sextet_to_char_[(char_idx)]);\
}
size_t rem, pos = 0;
constexpr const uint32_t sextet_mask = uint32_t(1 << 6) - 1;
const unsigned char *C4_RESTRICT d = (const unsigned char *) data.buf; // cast to unsigned to avoid wrapping high-bits
for(rem = data.len; rem >= 3; rem -= 3, d += 3)
{
const uint32_t val = ((uint32_t(d[0]) << 16) | (uint32_t(d[1]) << 8) | (uint32_t(d[2])));
c4append_idx_((val >> 18) & sextet_mask);
c4append_idx_((val >> 12) & sextet_mask);
c4append_idx_((val >> 6) & sextet_mask);
c4append_idx_((val ) & sextet_mask);
}
C4_ASSERT(rem < 3);
if(rem == 2)
{
const uint32_t val = ((uint32_t(d[0]) << 16) | (uint32_t(d[1]) << 8));
c4append_idx_((val >> 18) & sextet_mask);
c4append_idx_((val >> 12) & sextet_mask);
c4append_idx_((val >> 6) & sextet_mask);
c4append_('=');
}
else if(rem == 1)
{
const uint32_t val = ((uint32_t(d[0]) << 16));
c4append_idx_((val >> 18) & sextet_mask);
c4append_idx_((val >> 12) & sextet_mask);
c4append_('=');
c4append_('=');
}
return pos;
#undef c4append_
#undef c4append_idx_
}
size_t base64_decode(csubstr encoded, blob data)
{
#define c4append_(c) { if(wpos < data.len) { data.buf[wpos] = static_cast<c4::byte>(c); } ++wpos; }
#define c4appendval_(c, shift)\
{\
C4_XASSERT(c >= 0);\
C4_XASSERT(size_t(c) < sizeof(detail::base64_char_to_sextet_));\
val |= static_cast<uint32_t>(detail::base64_char_to_sextet_[(c)]) << ((shift) * 6);\
}
C4_ASSERT(base64_valid(encoded));
C4_CHECK((encoded.len & 3u) == 0);
size_t wpos = 0; // the write position
const char *C4_RESTRICT d = encoded.str;
constexpr const uint32_t full_byte = 0xff;
// process every quartet of input 6 bits --> triplet of output bytes
for(size_t rpos = 0; rpos < encoded.len; rpos += 4, d += 4)
{
if(d[2] == '=' || d[3] == '=') // skip the last quartet if it is padded
{
C4_ASSERT(d + 4 == encoded.str + encoded.len);
break;
}
uint32_t val = 0;
c4appendval_(d[3], 0);
c4appendval_(d[2], 1);
c4appendval_(d[1], 2);
c4appendval_(d[0], 3);
c4append_((val >> (2 * 8)) & full_byte);
c4append_((val >> (1 * 8)) & full_byte);
c4append_((val ) & full_byte);
}
// deal with the last quartet when it is padded
if(d == encoded.str + encoded.len)
return wpos;
if(d[2] == '=') // 2 padding chars
{
C4_ASSERT(d + 4 == encoded.str + encoded.len);
C4_ASSERT(d[3] == '=');
uint32_t val = 0;
c4appendval_(d[1], 2);
c4appendval_(d[0], 3);
c4append_((val >> (2 * 8)) & full_byte);
}
else if(d[3] == '=') // 1 padding char
{
C4_ASSERT(d + 4 == encoded.str + encoded.len);
uint32_t val = 0;
c4appendval_(d[2], 1);
c4appendval_(d[1], 2);
c4appendval_(d[0], 3);
c4append_((val >> (2 * 8)) & full_byte);
c4append_((val >> (1 * 8)) & full_byte);
}
return wpos;
#undef c4append_
#undef c4appendval_
}
} // namespace c4
#ifdef __clang__
# pragma clang diagnostic pop
#elif defined(__GNUC__)
# pragma GCC diagnostic pop
#endif

View file

@ -0,0 +1,229 @@
#include "c4/error.hpp"
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#define C4_LOGF_ERR(...) fprintf(stderr, __VA_ARGS__); fflush(stderr)
#define C4_LOGF_WARN(...) fprintf(stderr, __VA_ARGS__); fflush(stderr)
#define C4_LOGP(msg, ...) printf(msg)
#if defined(C4_XBOX) || (defined(C4_WIN) && defined(C4_MSVC))
# include "c4/windows.hpp"
#elif defined(C4_PS4)
# include <libdbg.h>
#elif defined(C4_UNIX) || defined(C4_LINUX)
# include <sys/stat.h>
# include <cstring>
# include <fcntl.h>
#elif defined(C4_MACOS) || defined(C4_IOS)
# include <assert.h>
# include <stdbool.h>
# include <sys/types.h>
# include <sys/sysctl.h>
#endif
// the amalgamation tool is dumb and was omitting this include under MACOS.
// So do it only once:
#if defined(C4_UNIX) || defined(C4_LINUX) || defined(C4_MACOS) || defined(C4_IOS)
# include <unistd.h>
#endif
#if defined(C4_EXCEPTIONS_ENABLED) && defined(C4_ERROR_THROWS_EXCEPTION)
# include <exception>
#endif
#ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wformat-nonliteral"
# pragma clang diagnostic ignored "-Wold-style-cast"
#elif defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wformat-nonliteral"
# pragma GCC diagnostic ignored "-Wold-style-cast"
#endif
//-----------------------------------------------------------------------------
namespace c4 {
static error_flags s_error_flags = ON_ERROR_DEFAULTS;
static error_callback_type s_error_callback = nullptr;
//-----------------------------------------------------------------------------
error_flags get_error_flags()
{
return s_error_flags;
}
void set_error_flags(error_flags flags)
{
s_error_flags = flags;
}
error_callback_type get_error_callback()
{
return s_error_callback;
}
/** Set the function which is called when an error occurs. */
void set_error_callback(error_callback_type cb)
{
s_error_callback = cb;
}
//-----------------------------------------------------------------------------
void handle_error(srcloc where, const char *fmt, ...)
{
char buf[1024];
size_t msglen = 0;
if(s_error_flags & (ON_ERROR_LOG|ON_ERROR_CALLBACK))
{
va_list args;
va_start(args, fmt);
int ilen = vsnprintf(buf, sizeof(buf), fmt, args); // ss.vprintf(fmt, args);
va_end(args);
msglen = ilen >= 0 && ilen < (int)sizeof(buf) ? static_cast<size_t>(ilen) : sizeof(buf)-1;
}
if(s_error_flags & ON_ERROR_LOG)
{
C4_LOGF_ERR("\n");
#if defined(C4_ERROR_SHOWS_FILELINE) && defined(C4_ERROR_SHOWS_FUNC)
C4_LOGF_ERR("%s:%d: ERROR: %s\n", where.file, where.line, buf);
C4_LOGF_ERR("%s:%d: ERROR here: %s\n", where.file, where.line, where.func);
#elif defined(C4_ERROR_SHOWS_FILELINE)
C4_LOGF_ERR("%s:%d: ERROR: %s\n", where.file, where.line, buf);
#elif ! defined(C4_ERROR_SHOWS_FUNC)
C4_LOGF_ERR("ERROR: %s\n", buf);
#endif
}
if(s_error_flags & ON_ERROR_CALLBACK)
{
if(s_error_callback)
{
s_error_callback(buf, msglen/*ss.c_strp(), ss.tellp()*/);
}
}
if(s_error_flags & ON_ERROR_ABORT)
{
abort();
}
if(s_error_flags & ON_ERROR_THROW)
{
#if defined(C4_EXCEPTIONS_ENABLED) && defined(C4_ERROR_THROWS_EXCEPTION)
throw Exception(buf);
#else
abort();
#endif
}
}
//-----------------------------------------------------------------------------
void handle_warning(srcloc where, const char *fmt, ...)
{
va_list args;
char buf[1024]; //sstream<c4::string> ss;
va_start(args, fmt);
vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
C4_LOGF_WARN("\n");
#if defined(C4_ERROR_SHOWS_FILELINE) && defined(C4_ERROR_SHOWS_FUNC)
C4_LOGF_WARN("%s:%d: WARNING: %s\n", where.file, where.line, buf/*ss.c_strp()*/);
C4_LOGF_WARN("%s:%d: WARNING: here: %s\n", where.file, where.line, where.func);
#elif defined(C4_ERROR_SHOWS_FILELINE)
C4_LOGF_WARN("%s:%d: WARNING: %s\n", where.file, where.line, buf/*ss.c_strp()*/);
#elif ! defined(C4_ERROR_SHOWS_FUNC)
C4_LOGF_WARN("WARNING: %s\n", buf/*ss.c_strp()*/);
#endif
//c4::log.flush();
}
//-----------------------------------------------------------------------------
bool is_debugger_attached()
{
#if defined(C4_UNIX) || defined(C4_LINUX)
static bool first_call = true;
static bool first_call_result = false;
if(first_call)
{
first_call = false;
//! @see http://stackoverflow.com/questions/3596781/how-to-detect-if-the-current-process-is-being-run-by-gdb
//! (this answer: http://stackoverflow.com/a/24969863/3968589 )
char buf[1024] = "";
int status_fd = open("/proc/self/status", O_RDONLY);
if (status_fd == -1)
{
return 0;
}
ssize_t num_read = ::read(status_fd, buf, sizeof(buf));
if (num_read > 0)
{
static const char TracerPid[] = "TracerPid:";
char *tracer_pid;
if(num_read < 1024)
{
buf[num_read] = 0;
}
tracer_pid = strstr(buf, TracerPid);
if (tracer_pid)
{
first_call_result = !!::atoi(tracer_pid + sizeof(TracerPid) - 1);
}
}
}
return first_call_result;
#elif defined(C4_PS4)
return (sceDbgIsDebuggerAttached() != 0);
#elif defined(C4_XBOX) || (defined(C4_WIN) && defined(C4_MSVC))
return IsDebuggerPresent() != 0;
#elif defined(C4_MACOS) || defined(C4_IOS)
// https://stackoverflow.com/questions/2200277/detecting-debugger-on-mac-os-x
// Returns true if the current process is being debugged (either
// running under the debugger or has a debugger attached post facto).
int junk;
int mib[4];
struct kinfo_proc info;
size_t size;
// Initialize the flags so that, if sysctl fails for some bizarre
// reason, we get a predictable result.
info.kp_proc.p_flag = 0;
// Initialize mib, which tells sysctl the info we want, in this case
// we're looking for information about a specific process ID.
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PID;
mib[3] = getpid();
// Call sysctl.
size = sizeof(info);
junk = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0);
assert(junk == 0);
// We're being debugged if the P_TRACED flag is set.
return ((info.kp_proc.p_flag & P_TRACED) != 0);
#else
return false;
#endif
} // is_debugger_attached()
} // namespace c4
#ifdef __clang__
# pragma clang diagnostic pop
#elif defined(__GNUC__)
# pragma GCC diagnostic pop
#endif

View file

@ -0,0 +1,60 @@
#include "c4/format.hpp"
#include <memory> // for std::align
#ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wformat-nonliteral"
# pragma clang diagnostic ignored "-Wold-style-cast"
#elif defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wformat-nonliteral"
# pragma GCC diagnostic ignored "-Wold-style-cast"
#endif
namespace c4 {
size_t to_chars(substr buf, fmt::const_raw_wrapper r)
{
void * vptr = buf.str;
size_t space = buf.len;
auto ptr = (decltype(buf.str)) std::align(r.alignment, r.len, vptr, space);
if(ptr == nullptr)
{
// if it was not possible to align, return a conservative estimate
// of the required space
return r.alignment + r.len;
}
C4_CHECK(ptr >= buf.begin() && ptr <= buf.end());
size_t sz = static_cast<size_t>(ptr - buf.str) + r.len;
if(sz <= buf.len)
{
memcpy(ptr, r.buf, r.len);
}
return sz;
}
bool from_chars(csubstr buf, fmt::raw_wrapper *r)
{
C4_SUPPRESS_WARNING_GCC_WITH_PUSH("-Wcast-qual")
void * vptr = (void*)buf.str;
C4_SUPPRESS_WARNING_GCC_POP
size_t space = buf.len;
auto ptr = (decltype(buf.str)) std::align(r->alignment, r->len, vptr, space);
C4_CHECK(ptr != nullptr);
C4_CHECK(ptr >= buf.begin() && ptr <= buf.end());
//size_t dim = (ptr - buf.str) + r->len;
memcpy(r->buf, ptr, r->len);
return true;
}
} // namespace c4
#ifdef __clang__
# pragma clang diagnostic pop
#elif defined(__GNUC__)
# pragma GCC diagnostic pop
#endif

View file

@ -0,0 +1,16 @@
#include "c4/language.hpp"
namespace c4 {
namespace detail {
#ifndef __GNUC__
void use_char_pointer(char const volatile* v)
{
C4_UNUSED(v);
}
#else
void foo() {} // to avoid empty file warning from the linker
#endif
} // namespace detail
} // namespace c4

View file

@ -0,0 +1,32 @@
#include "c4/memory_util.hpp"
#include "c4/error.hpp"
namespace c4 {
/** Fills 'dest' with the first 'pattern_size' bytes at 'pattern', 'num_times'. */
void mem_repeat(void* dest, void const* pattern, size_t pattern_size, size_t num_times)
{
if(C4_UNLIKELY(num_times == 0))
return;
C4_ASSERT( ! mem_overlaps(dest, pattern, num_times*pattern_size, pattern_size));
char *begin = static_cast<char*>(dest);
char *end = begin + num_times * pattern_size;
// copy the pattern once
::memcpy(begin, pattern, pattern_size);
// now copy from dest to itself, doubling up every time
size_t n = pattern_size;
while(begin + 2*n < end)
{
::memcpy(begin + n, begin, n);
n <<= 1; // double n
}
// copy the missing part
if(begin + n < end)
{
::memcpy(begin + n, begin, static_cast<size_t>(end - (begin + n)));
}
}
} // namespace c4

View file

@ -0,0 +1,60 @@
#include "c4/utf.hpp"
#include "c4/charconv.hpp"
namespace c4 {
C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wold-style-cast")
size_t decode_code_point(uint8_t *C4_RESTRICT buf, size_t buflen, const uint32_t code)
{
C4_UNUSED(buflen);
C4_ASSERT(buflen >= 4);
if (code <= UINT32_C(0x7f))
{
buf[0] = (uint8_t)code;
return 1u;
}
else if(code <= UINT32_C(0x7ff))
{
buf[0] = (uint8_t)(UINT32_C(0xc0) | (code >> 6)); /* 110xxxxx */
buf[1] = (uint8_t)(UINT32_C(0x80) | (code & UINT32_C(0x3f))); /* 10xxxxxx */
return 2u;
}
else if(code <= UINT32_C(0xffff))
{
buf[0] = (uint8_t)(UINT32_C(0xe0) | ((code >> 12))); /* 1110xxxx */
buf[1] = (uint8_t)(UINT32_C(0x80) | ((code >> 6) & UINT32_C(0x3f))); /* 10xxxxxx */
buf[2] = (uint8_t)(UINT32_C(0x80) | ((code ) & UINT32_C(0x3f))); /* 10xxxxxx */
return 3u;
}
else if(code <= UINT32_C(0x10ffff))
{
buf[0] = (uint8_t)(UINT32_C(0xf0) | ((code >> 18))); /* 11110xxx */
buf[1] = (uint8_t)(UINT32_C(0x80) | ((code >> 12) & UINT32_C(0x3f))); /* 10xxxxxx */
buf[2] = (uint8_t)(UINT32_C(0x80) | ((code >> 6) & UINT32_C(0x3f))); /* 10xxxxxx */
buf[3] = (uint8_t)(UINT32_C(0x80) | ((code ) & UINT32_C(0x3f))); /* 10xxxxxx */
return 4u;
}
return 0;
}
substr decode_code_point(substr out, csubstr code_point)
{
C4_ASSERT(out.len >= 4);
C4_ASSERT(!code_point.begins_with("U+"));
C4_ASSERT(!code_point.begins_with("\\x"));
C4_ASSERT(!code_point.begins_with("\\u"));
C4_ASSERT(!code_point.begins_with("\\U"));
C4_ASSERT(!code_point.begins_with('0'));
C4_ASSERT(code_point.len <= 8);
C4_ASSERT(code_point.len > 0);
uint32_t code_point_val;
C4_CHECK(read_hex(code_point, &code_point_val));
size_t ret = decode_code_point((uint8_t*)out.str, out.len, code_point_val);
C4_ASSERT(ret <= 4);
return out.first(ret);
}
C4_SUPPRESS_WARNING_GCC_CLANG_POP
} // namespace c4

View file

@ -0,0 +1,121 @@
#include "c4/yml/common.hpp"
#ifndef RYML_NO_DEFAULT_CALLBACKS
# include <stdlib.h>
# include <stdio.h>
#endif // RYML_NO_DEFAULT_CALLBACKS
namespace c4 {
namespace yml {
C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wold-style-cast")
namespace {
Callbacks s_default_callbacks;
} // anon namespace
#ifndef RYML_NO_DEFAULT_CALLBACKS
void report_error_impl(const char* msg, size_t length, Location loc, FILE *f)
{
if(!f)
f = stderr;
if(loc)
{
if(!loc.name.empty())
{
fwrite(loc.name.str, 1, loc.name.len, f);
fputc(':', f);
}
fprintf(f, "%zu:", loc.line);
if(loc.col)
fprintf(f, "%zu:", loc.col);
if(loc.offset)
fprintf(f, " (%zuB):", loc.offset);
}
fprintf(f, "%.*s\n", (int)length, msg);
fflush(f);
}
void error_impl(const char* msg, size_t length, Location loc, void * /*user_data*/)
{
report_error_impl(msg, length, loc, nullptr);
::abort();
}
void* allocate_impl(size_t length, void * /*hint*/, void * /*user_data*/)
{
void *mem = ::malloc(length);
if(mem == nullptr)
{
const char msg[] = "could not allocate memory";
error_impl(msg, sizeof(msg)-1, {}, nullptr);
}
return mem;
}
void free_impl(void *mem, size_t /*length*/, void * /*user_data*/)
{
::free(mem);
}
#endif // RYML_NO_DEFAULT_CALLBACKS
Callbacks::Callbacks()
:
m_user_data(nullptr),
#ifndef RYML_NO_DEFAULT_CALLBACKS
m_allocate(allocate_impl),
m_free(free_impl),
m_error(error_impl)
#else
m_allocate(nullptr),
m_free(nullptr),
m_error(nullptr)
#endif
{
}
Callbacks::Callbacks(void *user_data, pfn_allocate alloc_, pfn_free free_, pfn_error error_)
:
m_user_data(user_data),
#ifndef RYML_NO_DEFAULT_CALLBACKS
m_allocate(alloc_ ? alloc_ : allocate_impl),
m_free(free_ ? free_ : free_impl),
m_error(error_ ? error_ : error_impl)
#else
m_allocate(alloc_),
m_free(free_),
m_error(error_)
#endif
{
C4_CHECK(m_allocate);
C4_CHECK(m_free);
C4_CHECK(m_error);
}
void set_callbacks(Callbacks const& c)
{
s_default_callbacks = c;
}
Callbacks const& get_callbacks()
{
return s_default_callbacks;
}
void reset_callbacks()
{
set_callbacks(Callbacks());
}
void error(const char *msg, size_t msg_len, Location loc)
{
s_default_callbacks.m_error(msg, msg_len, loc, s_default_callbacks.m_user_data);
}
C4_SUPPRESS_WARNING_GCC_CLANG_POP
} // namespace yml
} // namespace c4

View file

@ -0,0 +1,30 @@
#include "c4/yml/node.hpp"
namespace c4 {
namespace yml {
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
size_t NodeRef::set_key_serialized(c4::fmt::const_base64_wrapper w)
{
_apply_seed();
csubstr encoded = this->to_arena(w);
this->set_key(encoded);
return encoded.len;
}
size_t NodeRef::set_val_serialized(c4::fmt::const_base64_wrapper w)
{
_apply_seed();
csubstr encoded = this->to_arena(w);
this->set_val(encoded);
return encoded.len;
}
} // namespace yml
} // namespace c4

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,112 @@
#include "c4/yml/preprocess.hpp"
#include "c4/yml/detail/parser_dbg.hpp"
/** @file preprocess.hpp Functions for preprocessing YAML prior to parsing. */
namespace c4 {
namespace yml {
C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wold-style-cast")
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
namespace {
C4_ALWAYS_INLINE bool _is_idchar(char c)
{
return (c >= 'a' && c <= 'z')
|| (c >= 'A' && c <= 'Z')
|| (c >= '0' && c <= '9')
|| (c == '_' || c == '-' || c == '~' || c == '$');
}
typedef enum { kReadPending = 0, kKeyPending = 1, kValPending = 2 } _ppstate;
C4_ALWAYS_INLINE _ppstate _next(_ppstate s)
{
int n = (int)s + 1;
return (_ppstate)(n <= (int)kValPending ? n : 0);
}
} // empty namespace
//-----------------------------------------------------------------------------
size_t preprocess_rxmap(csubstr s, substr buf)
{
detail::_SubstrWriter writer(buf);
_ppstate state = kReadPending;
size_t last = 0;
if(s.begins_with('{'))
{
RYML_CHECK(s.ends_with('}'));
s = s.offs(1, 1);
}
writer.append('{');
for(size_t i = 0; i < s.len; ++i)
{
const char curr = s[i];
const char next = i+1 < s.len ? s[i+1] : '\0';
if(curr == '\'' || curr == '"')
{
csubstr ss = s.sub(i).pair_range_esc(curr, '\\');
i += static_cast<size_t>(ss.end() - (s.str + i));
state = _next(state);
}
else if(state == kReadPending && _is_idchar(curr))
{
state = _next(state);
}
switch(state)
{
case kKeyPending:
{
if(curr == ':' && next == ' ')
{
state = _next(state);
}
else if(curr == ',' && next == ' ')
{
writer.append(s.range(last, i));
writer.append(": 1, ");
last = i + 2;
}
break;
}
case kValPending:
{
if(curr == '[' || curr == '{' || curr == '(')
{
csubstr ss = s.sub(i).pair_range_nested(curr, '\\');
i += static_cast<size_t>(ss.end() - (s.str + i));
state = _next(state);
}
else if(curr == ',' && next == ' ')
{
state = _next(state);
}
break;
}
default:
// nothing to do
break;
}
}
writer.append(s.sub(last));
if(state == kKeyPending)
writer.append(": 1");
writer.append('}');
return writer.pos;
}
C4_SUPPRESS_WARNING_GCC_CLANG_POP
} // namespace yml
} // namespace c4

File diff suppressed because it is too large Load diff

View file

@ -73,6 +73,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "d3d12ma", "dep\d3d12ma\d3d1
EndProject EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "reshadefx", "dep\reshadefx\reshadefx.vcxproj", "{27B8D4BB-4F01-4432-BC14-9BF6CA458EEE}" Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "reshadefx", "dep\reshadefx\reshadefx.vcxproj", "{27B8D4BB-4F01-4432-BC14-9BF6CA458EEE}"
EndProject EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rapidyaml", "dep\rapidyaml\rapidyaml.vcxproj", "{1AD23A8A-4C20-434C-AE6B-0E07759EEB1E}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM64 = Debug|ARM64 Debug|ARM64 = Debug|ARM64
@ -1031,6 +1033,38 @@ Global
{27B8D4BB-4F01-4432-BC14-9BF6CA458EEE}.ReleaseLTCG-Clang|ARM64.Build.0 = ReleaseLTCG-Clang|ARM64 {27B8D4BB-4F01-4432-BC14-9BF6CA458EEE}.ReleaseLTCG-Clang|ARM64.Build.0 = ReleaseLTCG-Clang|ARM64
{27B8D4BB-4F01-4432-BC14-9BF6CA458EEE}.ReleaseLTCG-Clang|x64.ActiveCfg = ReleaseLTCG-Clang|x64 {27B8D4BB-4F01-4432-BC14-9BF6CA458EEE}.ReleaseLTCG-Clang|x64.ActiveCfg = ReleaseLTCG-Clang|x64
{27B8D4BB-4F01-4432-BC14-9BF6CA458EEE}.ReleaseLTCG-Clang|x64.Build.0 = ReleaseLTCG-Clang|x64 {27B8D4BB-4F01-4432-BC14-9BF6CA458EEE}.ReleaseLTCG-Clang|x64.Build.0 = ReleaseLTCG-Clang|x64
{1AD23A8A-4C20-434C-AE6B-0E07759EEB1E}.Debug|ARM64.ActiveCfg = Debug|ARM64
{1AD23A8A-4C20-434C-AE6B-0E07759EEB1E}.Debug|ARM64.Build.0 = Debug|ARM64
{1AD23A8A-4C20-434C-AE6B-0E07759EEB1E}.Debug|x64.ActiveCfg = Debug|x64
{1AD23A8A-4C20-434C-AE6B-0E07759EEB1E}.Debug|x64.Build.0 = Debug|x64
{1AD23A8A-4C20-434C-AE6B-0E07759EEB1E}.Debug-Clang|ARM64.ActiveCfg = Debug-Clang|ARM64
{1AD23A8A-4C20-434C-AE6B-0E07759EEB1E}.Debug-Clang|ARM64.Build.0 = Debug-Clang|ARM64
{1AD23A8A-4C20-434C-AE6B-0E07759EEB1E}.Debug-Clang|x64.ActiveCfg = Debug-Clang|x64
{1AD23A8A-4C20-434C-AE6B-0E07759EEB1E}.Debug-Clang|x64.Build.0 = Debug-Clang|x64
{1AD23A8A-4C20-434C-AE6B-0E07759EEB1E}.DebugFast|ARM64.ActiveCfg = DebugFast|ARM64
{1AD23A8A-4C20-434C-AE6B-0E07759EEB1E}.DebugFast|ARM64.Build.0 = DebugFast|ARM64
{1AD23A8A-4C20-434C-AE6B-0E07759EEB1E}.DebugFast|x64.ActiveCfg = DebugFast|x64
{1AD23A8A-4C20-434C-AE6B-0E07759EEB1E}.DebugFast|x64.Build.0 = DebugFast|x64
{1AD23A8A-4C20-434C-AE6B-0E07759EEB1E}.DebugFast-Clang|ARM64.ActiveCfg = DebugFast-Clang|ARM64
{1AD23A8A-4C20-434C-AE6B-0E07759EEB1E}.DebugFast-Clang|ARM64.Build.0 = DebugFast-Clang|ARM64
{1AD23A8A-4C20-434C-AE6B-0E07759EEB1E}.DebugFast-Clang|x64.ActiveCfg = DebugFast-Clang|x64
{1AD23A8A-4C20-434C-AE6B-0E07759EEB1E}.DebugFast-Clang|x64.Build.0 = DebugFast-Clang|x64
{1AD23A8A-4C20-434C-AE6B-0E07759EEB1E}.Release|ARM64.ActiveCfg = Release|ARM64
{1AD23A8A-4C20-434C-AE6B-0E07759EEB1E}.Release|ARM64.Build.0 = Release|ARM64
{1AD23A8A-4C20-434C-AE6B-0E07759EEB1E}.Release|x64.ActiveCfg = Release|x64
{1AD23A8A-4C20-434C-AE6B-0E07759EEB1E}.Release|x64.Build.0 = Release|x64
{1AD23A8A-4C20-434C-AE6B-0E07759EEB1E}.Release-Clang|ARM64.ActiveCfg = Release-Clang|ARM64
{1AD23A8A-4C20-434C-AE6B-0E07759EEB1E}.Release-Clang|ARM64.Build.0 = Release-Clang|ARM64
{1AD23A8A-4C20-434C-AE6B-0E07759EEB1E}.Release-Clang|x64.ActiveCfg = Release-Clang|x64
{1AD23A8A-4C20-434C-AE6B-0E07759EEB1E}.Release-Clang|x64.Build.0 = Release-Clang|x64
{1AD23A8A-4C20-434C-AE6B-0E07759EEB1E}.ReleaseLTCG|ARM64.ActiveCfg = ReleaseLTCG|ARM64
{1AD23A8A-4C20-434C-AE6B-0E07759EEB1E}.ReleaseLTCG|ARM64.Build.0 = ReleaseLTCG|ARM64
{1AD23A8A-4C20-434C-AE6B-0E07759EEB1E}.ReleaseLTCG|x64.ActiveCfg = ReleaseLTCG|x64
{1AD23A8A-4C20-434C-AE6B-0E07759EEB1E}.ReleaseLTCG|x64.Build.0 = ReleaseLTCG|x64
{1AD23A8A-4C20-434C-AE6B-0E07759EEB1E}.ReleaseLTCG-Clang|ARM64.ActiveCfg = ReleaseLTCG-Clang|ARM64
{1AD23A8A-4C20-434C-AE6B-0E07759EEB1E}.ReleaseLTCG-Clang|ARM64.Build.0 = ReleaseLTCG-Clang|ARM64
{1AD23A8A-4C20-434C-AE6B-0E07759EEB1E}.ReleaseLTCG-Clang|x64.ActiveCfg = ReleaseLTCG-Clang|x64
{1AD23A8A-4C20-434C-AE6B-0E07759EEB1E}.ReleaseLTCG-Clang|x64.Build.0 = ReleaseLTCG-Clang|x64
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@ -1059,6 +1093,7 @@ Global
{C51A346A-86B2-46DF-9BB3-D0AA7E5D8699} = {BA490C0E-497D-4634-A21E-E65012006385} {C51A346A-86B2-46DF-9BB3-D0AA7E5D8699} = {BA490C0E-497D-4634-A21E-E65012006385}
{F351C4D8-594A-4850-B77B-3C1249812CCE} = {BA490C0E-497D-4634-A21E-E65012006385} {F351C4D8-594A-4850-B77B-3C1249812CCE} = {BA490C0E-497D-4634-A21E-E65012006385}
{27B8D4BB-4F01-4432-BC14-9BF6CA458EEE} = {BA490C0E-497D-4634-A21E-E65012006385} {27B8D4BB-4F01-4432-BC14-9BF6CA458EEE} = {BA490C0E-497D-4634-A21E-E65012006385}
{1AD23A8A-4C20-434C-AE6B-0E07759EEB1E} = {BA490C0E-497D-4634-A21E-E65012006385}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {26E40B32-7C1D-48D0-95F4-1A500E054028} SolutionGuid = {26E40B32-7C1D-48D0-95F4-1A500E054028}