mirror of
				https://github.com/RetroDECK/Duckstation.git
				synced 2025-04-10 19:15:14 +00:00 
			
		
		
		
	
		
			
	
	
		
			283 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
		
		
			
		
	
	
			283 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
|   | #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_ */
 |