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