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.
34template<typename T>
35static 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.
43template<typename T>
44static 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.
60struct 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
74extern "C" {
75#endif
76
77// Implemented in 'blruntime.cpp'.
78BL_API void* BL_CDECL blRuntimeAllocImpl(size_t implSize, uint16_t* memPoolDataOut) noexcept;
79BL_API BLResult BL_CDECL blRuntimeFreeImpl(void* impl_, size_t implSize, uint32_t memPoolData) noexcept;
80
81#ifdef __cplusplus
82} // {Extern:C}
83#endif
84
85template<typename Impl>
86static 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
95template<typename Impl>
96static BL_INLINE bool blImplIsMutable(Impl* impl) noexcept { return impl->refCount == 1; }
97
98template<typename T>
99static 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
105template<typename T>
106static 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
121static 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
127template<typename T>
128static 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
135template<typename T>
136static 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
145static 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
152template<typename T>
153static 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
162template<typename T, typename F>
163static 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