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