1 | // [Blend2D] |
2 | // 2D Vector Graphics Powered by a JIT Compiler. |
3 | // |
4 | // [License] |
5 | // Zlib - See LICENSE.md file in the package. |
6 | |
7 | #ifndef BLEND2D_BLAPI_IMPL_H |
8 | #define BLEND2D_BLAPI_IMPL_H |
9 | |
10 | #include "./blapi.h" |
11 | #include "./blvariant.h" |
12 | |
13 | // This should be the only place in Blend2D that includes <atomic>. |
14 | #include <atomic> |
15 | |
16 | //! \addtogroup blend2d_api_impl |
17 | //! \{ |
18 | |
19 | // ============================================================================ |
20 | // [Atomic Operations] |
21 | // ============================================================================ |
22 | |
23 | //! \name Atomic Operations |
24 | //! \{ |
25 | |
26 | // Atomic operations are used extensively across Blend2D for reference counting |
27 | // and caching. You should always use operations defined here as they implement |
28 | // all possible cases that Blend2D deals with in a correct way (and if there is |
29 | // a bug it makes it fixable in a single place). |
30 | |
31 | //! \ingroup blend2d_api_impl |
32 | //! |
33 | //! Atomically increments `n` to value `x`. The old value is returned. |
34 | template<typename T> |
35 | static BL_INLINE typename std::remove_volatile<T>::type blAtomicFetchAdd(T* x, typename std::remove_volatile<T>::type n = 1) noexcept { |
36 | typedef typename std::remove_volatile<T>::type RawT; |
37 | return ((std::atomic<RawT>*)x)->fetch_add(n, std::memory_order_relaxed); |
38 | } |
39 | |
40 | //! \ingroup blend2d_api_impl |
41 | //! |
42 | //! Atomically decrements `n` from value `x`. The old value is returned. |
43 | template<typename T> |
44 | static BL_INLINE typename std::remove_volatile<T>::type blAtomicFetchSub(T* x, typename std::remove_volatile<T>::type n = 1) noexcept { |
45 | typedef typename std::remove_volatile<T>::type RawT; |
46 | return ((std::atomic<RawT>*)x)->fetch_sub(n, std::memory_order_acq_rel); |
47 | } |
48 | |
49 | //! \} |
50 | |
51 | // ============================================================================ |
52 | // [Impl API] |
53 | // ============================================================================ |
54 | |
55 | //! Blend2D supports external data in almost every container. When a container |
56 | //! is created from external data it adds a preface before its data, which |
57 | //! stores user provided `destroyFunc` and `destroyData`. |
58 | //! |
59 | //! Use `blImplDestroyExternal` to call destroy function of Impl. |
60 | struct BLExternalImplPreface { |
61 | BLDestroyImplFunc destroyFunc; |
62 | void* destroyData; |
63 | }; |
64 | |
65 | // These are additional functions that are exported and used by various object |
66 | // implementations inside Blend2D itself. Since Blend2D 'Impl' can use memory |
67 | // pools it's required that any third party code that extends Blend2D must also |
68 | // use these functions to allocate and free 'Impl'. |
69 | |
70 | //! \name Impl Memory Management |
71 | //! \{ |
72 | |
73 | #ifdef __cplusplus |
74 | extern "C" { |
75 | #endif |
76 | |
77 | // Implemented in 'blruntime.cpp'. |
78 | BL_API void* BL_CDECL blRuntimeAllocImpl(size_t implSize, uint16_t* memPoolDataOut) noexcept; |
79 | BL_API BLResult BL_CDECL blRuntimeFreeImpl(void* impl_, size_t implSize, uint32_t memPoolData) noexcept; |
80 | |
81 | #ifdef __cplusplus |
82 | } // {Extern:C} |
83 | #endif |
84 | |
85 | template<typename Impl> |
86 | static BL_INLINE Impl* blRuntimeAllocImplT(size_t implSize, uint16_t* memPoolDataOut) noexcept { |
87 | return static_cast<Impl*>(blRuntimeAllocImpl(implSize, memPoolDataOut)); |
88 | } |
89 | |
90 | //! \} |
91 | |
92 | //! \name Impl Reference Counting |
93 | //! \{ |
94 | |
95 | template<typename Impl> |
96 | static BL_INLINE bool blImplIsMutable(Impl* impl) noexcept { return impl->refCount == 1; } |
97 | |
98 | template<typename T> |
99 | static BL_INLINE T* blImplIncRef(T* impl, size_t n = 1) noexcept { |
100 | if (impl->refCount != 0) |
101 | blAtomicFetchAdd(&impl->refCount, n); |
102 | return impl; |
103 | } |
104 | |
105 | template<typename T> |
106 | static BL_INLINE bool blImplDecRefAndTest(T* impl) noexcept { |
107 | size_t base = impl->implTraits & 0x3u; |
108 | // Zero `base` means it's a built-in none object or object that doesn't use |
109 | // reference counting. We cannot decrease the reference count of such Impl. |
110 | if (base == 0) |
111 | return false; |
112 | |
113 | return blAtomicFetchSub(&impl->refCount) == base; |
114 | } |
115 | |
116 | //! \} |
117 | |
118 | //! \name Impl Initialization and Destruction |
119 | //! \{ |
120 | |
121 | static BL_INLINE uint32_t blImplTraitsFromDataAccessFlags(uint32_t dataAccessFlags) noexcept { |
122 | return (dataAccessFlags & BL_DATA_ACCESS_RW) == BL_DATA_ACCESS_RW |
123 | ? BL_IMPL_TRAIT_MUTABLE |
124 | : BL_IMPL_TRAIT_IMMUTABLE; |
125 | } |
126 | |
127 | template<typename T> |
128 | static BL_INLINE void blImplInit(T* impl, uint32_t implType, uint32_t implTraits, uint16_t memPoolData) noexcept { |
129 | impl->refCount = (implTraits & 0x3u); |
130 | impl->implType = uint8_t(implType); |
131 | impl->implTraits = uint8_t(implTraits); |
132 | impl->memPoolData = memPoolData; |
133 | } |
134 | |
135 | template<typename T> |
136 | static BL_INLINE T* blImplInitExternal(T* impl, BLDestroyImplFunc destroyFunc, void* destroyData) noexcept { |
137 | BLExternalImplPreface* preface = reinterpret_cast<BLExternalImplPreface*>(impl); |
138 | preface->destroyFunc = destroyFunc; |
139 | preface->destroyData = destroyData; |
140 | |
141 | impl = reinterpret_cast<T*>(reinterpret_cast<uint8_t*>(impl) + sizeof(BLExternalImplPreface)); |
142 | return impl; |
143 | } |
144 | |
145 | static BL_INLINE void blImplDestroyExternal(void* impl) noexcept { |
146 | BLExternalImplPreface* preface = |
147 | reinterpret_cast<BLExternalImplPreface*>( |
148 | reinterpret_cast<uint8_t*>(impl) - sizeof(BLExternalImplPreface)); |
149 | preface->destroyFunc(impl, preface->destroyData); |
150 | } |
151 | |
152 | template<typename T> |
153 | static BL_INLINE BLResult blImplReleaseVirt(T* impl) noexcept { |
154 | return blImplDecRefAndTest(impl) ? impl->virt->destroy(impl) : BL_SUCCESS; |
155 | } |
156 | |
157 | //! \} |
158 | |
159 | //! \name Miscellaneous |
160 | //! \{ |
161 | |
162 | template<typename T, typename F> |
163 | static BL_INLINE void blAssignFunc(T** dst, F f) noexcept { *(void**)dst = (void*)f; } |
164 | |
165 | //! \} |
166 | |
167 | //! \} |
168 | |
169 | #endif // BLEND2D_BLAPI_IMPL_H |
170 | |