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_BLSTRING_H |
8 | #define BLEND2D_BLSTRING_H |
9 | |
10 | #include "./blvariant.h" |
11 | |
12 | //! \addtogroup blend2d_api_globals |
13 | //! \{ |
14 | |
15 | // ============================================================================ |
16 | // [BLString - Core] |
17 | // ============================================================================ |
18 | |
19 | //! Byte string [C Interface - Impl]. |
20 | struct BLStringImpl { |
21 | union { |
22 | struct { |
23 | //! String data [null terminated]. |
24 | char* data; |
25 | //! String size [in bytes]. |
26 | size_t size; |
27 | }; |
28 | //! String data and size as `BLStringView`. |
29 | BLStringView view; |
30 | }; |
31 | //! String capacity [in bytes]. |
32 | size_t capacity; |
33 | |
34 | //! Reference count. |
35 | volatile size_t refCount; |
36 | //! Impl type. |
37 | uint8_t implType; |
38 | //! Impl traits. |
39 | uint8_t implTraits; |
40 | //! Memory pool data. |
41 | uint16_t memPoolData; |
42 | |
43 | //! Reserved, will be part of string data. |
44 | uint8_t reserved[4]; |
45 | }; |
46 | |
47 | //! Byte string [C Interface - Core]. |
48 | struct BLStringCore { |
49 | BLStringImpl* impl; |
50 | }; |
51 | |
52 | // ============================================================================ |
53 | // [BLString - C++] |
54 | // ============================================================================ |
55 | |
56 | #ifdef __cplusplus |
57 | //! Byte string [C++ API]. |
58 | //! |
59 | //! Blend2D always uses UTF-8 encoding in public APIs so all strings are assumed |
60 | //! UTF-8 by default. However, `BLString` can hold arbitrary byte sequence and |
61 | //! act as a raw byte-string when this functionality is required. |
62 | class BLString : public BLStringCore { |
63 | public: |
64 | //! \cond INTERNAL |
65 | static constexpr const uint32_t kImplType = BL_IMPL_TYPE_STRING; |
66 | //! \endcond |
67 | |
68 | //! \name Construction & Destruction |
69 | //! \{ |
70 | |
71 | BL_INLINE BLString() noexcept { this->impl = none().impl; } |
72 | BL_INLINE BLString(BLString&& other) noexcept { blVariantInitMove(this, &other); } |
73 | BL_INLINE BLString(const BLString& other) noexcept { blVariantInitWeak(this, &other); } |
74 | BL_INLINE explicit BLString(BLStringImpl* impl) noexcept { this->impl = impl; } |
75 | BL_INLINE ~BLString() noexcept { blStringReset(this); } |
76 | |
77 | //! \} |
78 | |
79 | //! \name Overloaded Operators |
80 | //! \{ |
81 | |
82 | BL_INLINE explicit operator bool() const noexcept { return !empty(); } |
83 | |
84 | BL_INLINE BLString& operator=(BLString&& other) noexcept { blStringAssignMove(this, &other); return *this; } |
85 | BL_INLINE BLString& operator=(const BLString& other) noexcept { blStringAssignWeak(this, &other); return *this; } |
86 | |
87 | BL_INLINE bool operator==(const BLString& other) const noexcept { return equals(other); } |
88 | BL_INLINE bool operator!=(const BLString& other) const noexcept { return !equals(other); } |
89 | BL_INLINE bool operator< (const BLString& other) const noexcept { return compare(other) < 0; } |
90 | BL_INLINE bool operator<=(const BLString& other) const noexcept { return compare(other) <= 0; } |
91 | BL_INLINE bool operator> (const BLString& other) const noexcept { return compare(other) > 0; } |
92 | BL_INLINE bool operator>=(const BLString& other) const noexcept { return compare(other) >= 0; } |
93 | |
94 | BL_INLINE bool operator==(const BLStringView& view) const noexcept { return equals(view); } |
95 | BL_INLINE bool operator!=(const BLStringView& view) const noexcept { return !equals(view); } |
96 | BL_INLINE bool operator< (const BLStringView& view) const noexcept { return compare(view) < 0; } |
97 | BL_INLINE bool operator<=(const BLStringView& view) const noexcept { return compare(view) <= 0; } |
98 | BL_INLINE bool operator> (const BLStringView& view) const noexcept { return compare(view) > 0; } |
99 | BL_INLINE bool operator>=(const BLStringView& view) const noexcept { return compare(view) >= 0; } |
100 | |
101 | BL_INLINE bool operator==(const char* str) const noexcept { return equals(str); } |
102 | BL_INLINE bool operator!=(const char* str) const noexcept { return !equals(str); } |
103 | BL_INLINE bool operator< (const char* str) const noexcept { return compare(str) < 0; } |
104 | BL_INLINE bool operator<=(const char* str) const noexcept { return compare(str) <= 0; } |
105 | BL_INLINE bool operator> (const char* str) const noexcept { return compare(str) > 0; } |
106 | BL_INLINE bool operator>=(const char* str) const noexcept { return compare(str) >= 0; } |
107 | |
108 | BL_INLINE char operator[](size_t index) const noexcept { return at(index); } |
109 | |
110 | //! \} |
111 | |
112 | //! \name Common Functionality |
113 | //! \{ |
114 | |
115 | //! Clears the content of the string and releases its data. |
116 | //! |
117 | //! After reset the string content matches a default constructed string. |
118 | BL_INLINE BLResult reset() noexcept { return blStringReset(this); } |
119 | BL_INLINE void swap(BLString& other) noexcept { std::swap(this->impl, other.impl); } |
120 | |
121 | BL_INLINE BLResult assign(char c, size_t n = 1) noexcept { return blStringApplyOpChar(this, BL_MODIFY_OP_ASSIGN_FIT, c, n); } |
122 | BL_INLINE BLResult assign(BLString&& other) noexcept { return blStringAssignMove(this, &other); } |
123 | BL_INLINE BLResult assign(const BLStringCore& other) noexcept { return blStringAssignWeak(this, &other); } |
124 | BL_INLINE BLResult assign(const BLStringView& view) noexcept { return blStringAssignData(this, view.data, view.size); } |
125 | BL_INLINE BLResult assign(const char* str, size_t n = SIZE_MAX) noexcept { return blStringAssignData(this, str, n); } |
126 | BL_INLINE BLResult assignDeep(const BLStringCore& other) noexcept { return blStringAssignDeep(this, &other); } |
127 | |
128 | template<typename... Args> |
129 | BL_INLINE BLResult assignFormat(const char* fmt, Args&&... args) noexcept { return blStringApplyOpFormat(this, BL_MODIFY_OP_ASSIGN_FIT, fmt, std::forward<Args>(args)...); } |
130 | BL_INLINE BLResult assignFormatV(const char* fmt, va_list ap) noexcept { return blStringApplyOpFormatV(this, BL_MODIFY_OP_ASSIGN_FIT, fmt, ap); } |
131 | |
132 | BL_INLINE bool empty() const noexcept { return impl->size == 0; } |
133 | |
134 | //! \} |
135 | |
136 | //! \name Accessors |
137 | //! \{ |
138 | |
139 | //! Returns the character at the given `index`. |
140 | //! |
141 | //! \note Index must be valid and cannot be out of bounds, otherwise the |
142 | //! result is undefined and would trigger an assertion failure in debug mode. |
143 | BL_INLINE char at(size_t index) const noexcept { |
144 | BL_ASSERT(index < size()); |
145 | return data()[index]; |
146 | } |
147 | |
148 | //! Returns the size of the string [in bytes]. |
149 | BL_INLINE size_t size() const noexcept { return impl->size; } |
150 | //! Returns the capacity of the string [in bytes]. |
151 | BL_INLINE size_t capacity() const noexcept { return impl->capacity; } |
152 | |
153 | //! Returns a read-only data of the string. |
154 | BL_INLINE const char* data() const noexcept { return impl->data; } |
155 | //! Returns the end of the string data. |
156 | //! |
157 | //! The returned pointer points to the null terminator, the data still can |
158 | //! be read, but it's not considered as string data by Blend2D anymore. |
159 | BL_INLINE const char* end() const noexcept { return impl->data + impl->size; } |
160 | |
161 | //! Returns the content of the string as `BLStringView`. |
162 | BL_INLINE const BLStringView& view() const noexcept { return impl->view; } |
163 | |
164 | //! \} |
165 | |
166 | //! \name String Manipulation |
167 | //! \{ |
168 | |
169 | //! Clears the content of the string without releasing its dynamically allocated data, if possible. |
170 | BL_INLINE BLResult clear() noexcept { return blStringClear(this); } |
171 | //! Shrinks the capacity of the string to match the current content. |
172 | BL_INLINE BLResult shrink() noexcept { return blStringShrink(this); } |
173 | //! Reserves at least `n` bytes in the string for further manipulation (most probably appending). |
174 | BL_INLINE BLResult reserve(size_t n) noexcept { return blStringReserve(this, n); } |
175 | //! Resizes the string to `n` and fills the additional data by `fill` pattern. |
176 | BL_INLINE BLResult resize(size_t n, char fill = '\0') noexcept { return blStringResize(this, n, fill); } |
177 | |
178 | //! Truncates the string length to `n`. |
179 | //! |
180 | //! It does nothing if the the string length is less than `n`. |
181 | BL_INLINE BLResult truncate(size_t n) noexcept { return blStringResize(this, blMin(n, impl->size), '\0'); } |
182 | |
183 | //! Makes the string mutable. |
184 | //! |
185 | //! This operation checks whether the string is mutable and if not it makes a |
186 | //! deep copy of its content so it can be modified. Please not that you can |
187 | //! only modify the content that is defined by its length property. Even if |
188 | //! the string had higher capacity before `makeMutable()` it's not guaranteed |
189 | //! that the possible new data would match that capacity. |
190 | //! |
191 | //! If you want to make the string mutable for the purpose of appending or |
192 | //! making other modifications please consider using `modifyOp()` and |
193 | //! `insertOp()` functions instead. |
194 | BL_INLINE BLResult makeMutable(char** dataOut) noexcept { return blStringMakeMutable(this, dataOut); } |
195 | BL_INLINE BLResult modifyOp(uint32_t op, size_t n, char** dataOut) noexcept { return blStringModifyOp(this, op, n, dataOut); } |
196 | BL_INLINE BLResult insertOp(size_t index, size_t n, char** dataOut) noexcept { return blStringInsertOp(this, index, n, dataOut); } |
197 | |
198 | BL_INLINE BLResult append(char c, size_t n = 1) noexcept { return blStringApplyOpChar(this, BL_MODIFY_OP_APPEND_GROW, c, n); } |
199 | BL_INLINE BLResult append(const BLStringCore& other) noexcept { return blStringApplyOpString(this, BL_MODIFY_OP_APPEND_GROW, &other); } |
200 | BL_INLINE BLResult append(const BLStringView& view) noexcept { return blStringApplyOpData(this, BL_MODIFY_OP_APPEND_GROW, view.data, view.size); } |
201 | BL_INLINE BLResult append(const char* str, size_t n = SIZE_MAX) noexcept { return blStringApplyOpData(this, BL_MODIFY_OP_APPEND_GROW, str, n); } |
202 | |
203 | template<typename... Args> |
204 | BL_INLINE BLResult appendFormat(const char* fmt, Args&&... args) noexcept { return blStringApplyOpFormat(this, BL_MODIFY_OP_APPEND_GROW, fmt, std::forward<Args>(args)...); } |
205 | BL_INLINE BLResult appendFormatV(const char* fmt, va_list ap) noexcept { return blStringApplyOpFormatV(this, BL_MODIFY_OP_APPEND_GROW, fmt, ap); } |
206 | |
207 | BL_INLINE BLResult prepend(char c, size_t n = 1) noexcept { return blStringInsertChar(this, 0, c, n); } |
208 | BL_INLINE BLResult prepend(const BLStringCore& other) noexcept { return blStringInsertString(this, 0, &other); } |
209 | BL_INLINE BLResult prepend(const BLStringView& view) noexcept { return blStringInsertData(this, 0, view.data, view.size); } |
210 | BL_INLINE BLResult prepend(const char* str, size_t n = SIZE_MAX) noexcept { return blStringInsertData(this, 0, str, n); } |
211 | |
212 | BL_INLINE BLResult insert(size_t index, char c, size_t n = 1) noexcept { return blStringInsertChar(this, index, c, n); } |
213 | BL_INLINE BLResult insert(size_t index, const BLStringCore& other) noexcept { return blStringInsertString(this, index, &other); } |
214 | BL_INLINE BLResult insert(size_t index, const BLStringView& view) noexcept { return blStringInsertData(this, index, view.data, view.size); } |
215 | BL_INLINE BLResult insert(size_t index, const char* str, size_t n = SIZE_MAX) noexcept { return blStringInsertData(this, index, str, n); } |
216 | |
217 | BL_INLINE BLResult remove(const BLRange& range) noexcept { return blStringRemoveRange(this, range.start, range.end); } |
218 | |
219 | //! \name Equality & Comparison |
220 | //! \{ |
221 | |
222 | //! Returns whether this string and `other` are equal (i.e. their contents match). |
223 | BL_INLINE bool equals(const BLString& other) const noexcept { return blStringEquals(this, &other); } |
224 | //! Returns whether this string and other string `view` are equal. |
225 | BL_INLINE bool equals(const BLStringView& view) const noexcept { return blStringEqualsData(this, view.data, view.size); } |
226 | //! Returns whether this string and the given string data `str` of length `n` are equal. |
227 | BL_INLINE bool equals(const char* str, size_t n = SIZE_MAX) const noexcept { return blStringEqualsData(this, str, n); } |
228 | |
229 | //! Compares this string with `other` and returns either `-1`, `0`, or `1`. |
230 | BL_INLINE int compare(const BLString& other) const noexcept { return blStringCompare(this, &other); } |
231 | //! Compares this string with other string `view` and returns either `-1`, `0`, or `1`. |
232 | BL_INLINE int compare(const BLStringView& view) const noexcept { return blStringCompareData(this, view.data, view.size); } |
233 | //! Compares this string with other string data and returns either `-1`, `0`, or `1`. |
234 | BL_INLINE int compare(const char* str, size_t n = SIZE_MAX) const noexcept { return blStringCompareData(this, str, n); } |
235 | |
236 | //! \} |
237 | |
238 | //! \name Search |
239 | //! \{ |
240 | |
241 | //! Returns the first index at which a given character `c` can be found in |
242 | //! the string, or `SIZE_MAX` if not present. |
243 | BL_INLINE size_t indexOf(char c) const noexcept { |
244 | return indexOf(c, 0); |
245 | } |
246 | |
247 | //! Returns the index at which a given character `c` can be found in |
248 | //! the string starting from `fromIndex`, or `SIZE_MAX` if not present. |
249 | BL_INLINE size_t indexOf(char c, size_t fromIndex) const noexcept { |
250 | const char* p = data(); |
251 | size_t iEnd = size(); |
252 | |
253 | for (size_t i = fromIndex; i < iEnd; i++) |
254 | if (p[i] == c) |
255 | return i; |
256 | |
257 | return SIZE_MAX; |
258 | } |
259 | |
260 | //! Returns the last index at which a given character `c` can be found in |
261 | //! the string, or `SIZE_MAX` if not present. |
262 | BL_INLINE size_t lastIndexOf(char c) const noexcept { |
263 | const char* p = data(); |
264 | size_t i = size(); |
265 | |
266 | while (--i != SIZE_MAX && !(p[i] == c)) |
267 | continue; |
268 | |
269 | return i; |
270 | } |
271 | |
272 | //! Returns the index at which a given character `c` can be found in |
273 | //! the string starting from `fromIndex` and ending at `0`, or `SIZE_MAX` |
274 | //! if not present. |
275 | BL_INLINE size_t lastIndexOf(char c, size_t fromIndex) const noexcept { |
276 | const char* p = data(); |
277 | size_t i = size() - 1; |
278 | |
279 | if (i == SIZE_MAX) |
280 | return i; |
281 | |
282 | i = blMin<size_t>(i, fromIndex); |
283 | while (!(p[i] == c) && --i != SIZE_MAX) |
284 | continue; |
285 | |
286 | return i; |
287 | } |
288 | |
289 | //! \} |
290 | |
291 | static BL_INLINE const BLString& none() noexcept { return reinterpret_cast<const BLString*>(blNone)[kImplType]; } |
292 | }; |
293 | #endif |
294 | |
295 | //! \} |
296 | |
297 | #endif // BLEND2D_BLSTRING_H |
298 | |