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].
20struct 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].
48struct 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.
62class BLString : public BLStringCore {
63public:
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