string.h (13787B)
1 // This file is part of AsmJit project <https://asmjit.com> 2 // 3 // See <asmjit/core.h> or LICENSE.md for license and copyright information 4 // SPDX-License-Identifier: Zlib 5 6 #ifndef ASMJIT_CORE_STRING_H_INCLUDED 7 #define ASMJIT_CORE_STRING_H_INCLUDED 8 9 #include "../core/span.h" 10 #include "../core/support.h" 11 12 ASMJIT_BEGIN_NAMESPACE 13 14 //! \addtogroup asmjit_utilities 15 //! \{ 16 17 //! Format flags used by \ref String API. 18 enum class StringFormatFlags : uint32_t { 19 //! No flags. 20 kNone = 0x00000000u, 21 //! Show sign. 22 kShowSign = 0x00000001u, 23 //! Show space. 24 kShowSpace = 0x00000002u, 25 //! Alternate form (use 0x when formatting HEX number). 26 kAlternate = 0x00000004u, 27 //! The input is signed. 28 kSigned = 0x80000000u 29 }; 30 ASMJIT_DEFINE_ENUM_FLAGS(StringFormatFlags) 31 32 //! Fixed string - only useful for strings that would never exceed `N - 1` characters; always null-terminated. 33 template<size_t N> 34 union FixedString { 35 //! \name Constants 36 //! \{ 37 38 static inline constexpr uint32_t kNumUInt32Words = uint32_t((N + sizeof(uint32_t) - 1) / sizeof(uint32_t)); 39 40 //! \} 41 42 //! \name Members 43 //! \{ 44 45 char str[kNumUInt32Words * sizeof(uint32_t)]; 46 uint32_t u32[kNumUInt32Words]; 47 48 //! \} 49 50 //! \name Utilities 51 //! \{ 52 53 [[nodiscard]] 54 inline bool equals(const char* other) const noexcept { return strcmp(str, other) == 0; } 55 56 //! \} 57 }; 58 59 //! A simple non-reference counted string that uses small string optimization (SSO). 60 //! 61 //! This string has 3 allocation possibilities: 62 //! 63 //! 1. Small - embedded buffer is used for up to `kSSOCapacity` characters. This should handle most small 64 //! strings and thus avoid dynamic memory allocation for most use-cases. 65 //! 66 //! 2. Large - string that doesn't fit into an embedded buffer (or string that was truncated from a larger 67 //! buffer) and is owned by AsmJit. When you destroy the string AsmJit would automatically 68 //! release the large buffer. 69 //! 70 //! 3. External - like Large (2), however, the large buffer is not owned by AsmJit and won't be released when 71 //! the string is destroyed or reallocated. This is mostly useful for working with larger temporary 72 //! strings allocated on stack or with immutable strings. 73 class String { 74 public: 75 ASMJIT_NONCOPYABLE(String) 76 77 //! String operation. 78 enum class ModifyOp : uint32_t { 79 //! Assignment - a new content replaces the current one. 80 kAssign = 0, 81 //! Append - a new content is appended to the string. 82 kAppend = 1 83 }; 84 85 //! \cond INTERNAL 86 static inline constexpr uint32_t kLayoutSize = 32; 87 static inline constexpr uint32_t kSSOCapacity = kLayoutSize - 2; 88 89 //! Large string (owned by String). 90 static inline constexpr uint8_t kTypeLarge = 0x1Fu; 91 //! External string (arena allocated or not owned by String). 92 static inline constexpr uint8_t kTypeExternal = 0x20u; 93 94 [[nodiscard]] 95 static ASMJIT_INLINE_NODEBUG bool is_external(uint8_t type) noexcept { return type == kTypeExternal; } 96 97 [[nodiscard]] 98 static ASMJIT_INLINE_NODEBUG bool is_large_or_external(uint8_t type) noexcept { return type >= kTypeLarge; } 99 100 union Raw { 101 uint8_t u8[kLayoutSize]; 102 uint64_t u64[kLayoutSize / sizeof(uint64_t)]; 103 uintptr_t uptr[kLayoutSize / sizeof(uintptr_t)]; 104 }; 105 106 struct Small { 107 uint8_t type; 108 char data[kSSOCapacity + 1u]; 109 }; 110 111 struct Large { 112 uint8_t type; 113 uint8_t reserved[sizeof(uintptr_t) - 1]; 114 size_t size; 115 size_t capacity; 116 char* data; 117 }; 118 119 union { 120 uint8_t _type; 121 Raw _raw; 122 Small _small; 123 Large _large; 124 }; 125 //! \endcond 126 127 //! \name Construction & Destruction 128 //! \{ 129 130 //! Creates a default-initialized string if zero length. 131 ASMJIT_INLINE_NODEBUG String() noexcept 132 : _small {} {} 133 134 //! Creates a string that takes ownership of the content of the `other` string. 135 ASMJIT_INLINE_NODEBUG String(String&& other) noexcept { 136 _raw = other._raw; 137 other._reset_internal(); 138 } 139 140 ASMJIT_INLINE_NODEBUG ~String() noexcept { 141 reset(); 142 } 143 144 //! Reset the string into a construction state. 145 ASMJIT_API Error reset() noexcept; 146 147 //! \} 148 149 //! \name Overloaded Operators 150 //! \{ 151 152 inline String& operator=(String&& other) noexcept { 153 swap(other); 154 other.reset(); 155 return *this; 156 } 157 158 [[nodiscard]] 159 ASMJIT_INLINE_NODEBUG bool operator==(const char* other) const noexcept { return equals(other); } 160 161 [[nodiscard]] 162 ASMJIT_INLINE_NODEBUG bool operator!=(const char* other) const noexcept { return !equals(other); } 163 164 [[nodiscard]] 165 ASMJIT_INLINE_NODEBUG bool operator==(const String& other) const noexcept { return equals(other); } 166 167 [[nodiscard]] 168 ASMJIT_INLINE_NODEBUG bool operator!=(const String& other) const noexcept { return !equals(other); } 169 170 //! \} 171 172 //! \name Accessors 173 //! \{ 174 175 [[nodiscard]] 176 ASMJIT_INLINE_NODEBUG bool is_external() const noexcept { return is_external(_type); } 177 178 [[nodiscard]] 179 ASMJIT_INLINE_NODEBUG bool is_large_or_external() const noexcept { return is_large_or_external(_type); } 180 181 //! Tests whether the string is empty. 182 [[nodiscard]] 183 ASMJIT_INLINE_NODEBUG bool is_empty() const noexcept { return size() == 0; } 184 185 //! Returns the size of the string. 186 [[nodiscard]] 187 ASMJIT_INLINE_NODEBUG size_t size() const noexcept { return is_large_or_external() ? size_t(_large.size) : size_t(_type); } 188 189 //! Returns the capacity of the string. 190 [[nodiscard]] 191 ASMJIT_INLINE_NODEBUG size_t capacity() const noexcept { return is_large_or_external() ? _large.capacity : size_t(kSSOCapacity); } 192 193 //! Returns the data of the string. 194 [[nodiscard]] 195 ASMJIT_INLINE_NODEBUG char* data() noexcept { return is_large_or_external() ? _large.data : _small.data; } 196 197 //! \overload 198 [[nodiscard]] 199 ASMJIT_INLINE_NODEBUG const char* data() const noexcept { return is_large_or_external() ? _large.data : _small.data; } 200 201 [[nodiscard]] 202 ASMJIT_INLINE_NODEBUG char* begin() noexcept { return data(); } 203 204 [[nodiscard]] 205 ASMJIT_INLINE_NODEBUG const char* begin() const noexcept { return data(); } 206 207 [[nodiscard]] 208 ASMJIT_INLINE_NODEBUG char* end() noexcept { return data() + size(); } 209 210 [[nodiscard]] 211 ASMJIT_INLINE_NODEBUG const char* end() const noexcept { return data() + size(); } 212 213 [[nodiscard]] 214 ASMJIT_INLINE_NODEBUG Span<char> as_span() noexcept { return Span<char>(data(), size()); } 215 216 [[nodiscard]] 217 ASMJIT_INLINE_NODEBUG Span<const char> as_span() const noexcept { return Span<const char>(data(), size()); } 218 219 //! \} 220 221 //! \name String Operations 222 //! \{ 223 224 //! Swaps the content of this string with `other`. 225 ASMJIT_INLINE_NODEBUG void swap(String& other) noexcept { 226 std::swap(_raw, other._raw); 227 } 228 229 //! Clears the content of the string. 230 ASMJIT_API Error clear() noexcept; 231 232 [[nodiscard]] 233 ASMJIT_API char* prepare(ModifyOp op, size_t size) noexcept; 234 235 //! \cond INTERNAL 236 ASMJIT_API Error _op_string(ModifyOp op, const char* str, size_t size = SIZE_MAX) noexcept; 237 ASMJIT_API Error _op_char(ModifyOp op, char c) noexcept; 238 ASMJIT_API Error _op_chars(ModifyOp op, char c, size_t n) noexcept; 239 ASMJIT_API Error _op_number(ModifyOp op, uint64_t i, uint32_t base = 0, size_t width = 0, StringFormatFlags flags = StringFormatFlags::kNone) noexcept; 240 ASMJIT_API Error _op_hex(ModifyOp op, const void* data, size_t size, char separator = '\0') noexcept; 241 ASMJIT_API Error _op_format(ModifyOp op, const char* fmt, ...) noexcept; 242 ASMJIT_API Error _op_vformat(ModifyOp op, const char* fmt, va_list ap) noexcept; 243 //! \endcond 244 245 //! Replaces the current of the string with `data` of the given `size`. 246 //! 247 //! Null terminated strings can set `size` to `SIZE_MAX`. 248 ASMJIT_API Error assign(const char* data, size_t size = SIZE_MAX) noexcept; 249 250 //! Appends the given `span` to the string. 251 ASMJIT_INLINE_NODEBUG Error assign(Span<const char> span) noexcept { 252 return _op_string(ModifyOp::kAssign, span.data(), span.size()); 253 } 254 255 //! Replaces the current of the string with `other` string. 256 ASMJIT_INLINE_NODEBUG Error assign(const String& other) noexcept { 257 return assign(other.data(), other.size()); 258 } 259 260 //! Replaces the current of the string by a single `c` character. 261 ASMJIT_INLINE_NODEBUG Error assign(char c) noexcept { 262 return _op_char(ModifyOp::kAssign, c); 263 } 264 265 //! Replaces the current of the string by a `c` character, repeated `n` times. 266 ASMJIT_INLINE_NODEBUG Error assign_chars(char c, size_t n) noexcept { 267 return _op_chars(ModifyOp::kAssign, c, n); 268 } 269 270 //! Replaces the current of the string by a formatted integer `i` (signed). 271 ASMJIT_INLINE_NODEBUG Error assign_int(int64_t i, uint32_t base = 0, size_t width = 0, StringFormatFlags flags = StringFormatFlags::kNone) noexcept { 272 return _op_number(ModifyOp::kAssign, uint64_t(i), base, width, flags | StringFormatFlags::kSigned); 273 } 274 275 //! Replaces the current of the string by a formatted integer `i` (unsigned). 276 ASMJIT_INLINE_NODEBUG Error assign_uint(uint64_t i, uint32_t base = 0, size_t width = 0, StringFormatFlags flags = StringFormatFlags::kNone) noexcept { 277 return _op_number(ModifyOp::kAssign, i, base, width, flags); 278 } 279 280 //! Replaces the current of the string by the given `data` converted to a HEX string. 281 ASMJIT_INLINE_NODEBUG Error assign_hex(const void* data, size_t size, char separator = '\0') noexcept { 282 return _op_hex(ModifyOp::kAssign, data, size, separator); 283 } 284 285 //! Replaces the current of the string by a formatted string `fmt`. 286 template<typename... Args> 287 ASMJIT_INLINE_NODEBUG Error assign_format(const char* fmt, Args&&... args) noexcept { 288 return _op_format(ModifyOp::kAssign, fmt, std::forward<Args>(args)...); 289 } 290 291 //! Replaces the current of the string by a formatted string `fmt` (va_list version). 292 ASMJIT_INLINE_NODEBUG Error assign_vformat(const char* fmt, va_list ap) noexcept { 293 return _op_vformat(ModifyOp::kAssign, fmt, ap); 294 } 295 296 //! Appends `str` having the given size `size` to the string. 297 //! 298 //! Null terminated strings can set `size` to `SIZE_MAX`. 299 ASMJIT_INLINE_NODEBUG Error append(const char* str, size_t size = SIZE_MAX) noexcept { 300 return _op_string(ModifyOp::kAppend, str, size); 301 } 302 303 //! Appends the given `span` to the string. 304 ASMJIT_INLINE_NODEBUG Error append(Span<const char> span) noexcept { 305 return _op_string(ModifyOp::kAppend, span.data(), span.size()); 306 } 307 308 //! Appends `other` string to this string. 309 ASMJIT_INLINE_NODEBUG Error append(const String& other) noexcept { 310 return append(other.data(), other.size()); 311 } 312 313 //! Appends a single `c` character. 314 ASMJIT_INLINE_NODEBUG Error append(char c) noexcept { 315 return _op_char(ModifyOp::kAppend, c); 316 } 317 318 //! Appends `c` character repeated `n` times. 319 ASMJIT_INLINE_NODEBUG Error append_chars(char c, size_t n) noexcept { 320 return _op_chars(ModifyOp::kAppend, c, n); 321 } 322 323 //! Appends a formatted integer `i` (signed). 324 ASMJIT_INLINE_NODEBUG Error append_int(int64_t i, uint32_t base = 0, size_t width = 0, StringFormatFlags flags = StringFormatFlags::kNone) noexcept { 325 return _op_number(ModifyOp::kAppend, uint64_t(i), base, width, flags | StringFormatFlags::kSigned); 326 } 327 328 //! Appends a formatted integer `i` (unsigned). 329 ASMJIT_INLINE_NODEBUG Error append_uint(uint64_t i, uint32_t base = 0, size_t width = 0, StringFormatFlags flags = StringFormatFlags::kNone) noexcept { 330 return _op_number(ModifyOp::kAppend, i, base, width, flags); 331 } 332 333 //! Appends the given `data` converted to a HEX string. 334 ASMJIT_INLINE_NODEBUG Error append_hex(const void* data, size_t size, char separator = '\0') noexcept { 335 return _op_hex(ModifyOp::kAppend, data, size, separator); 336 } 337 338 //! Appends a formatted string `fmt` with `args`. 339 template<typename... Args> 340 ASMJIT_INLINE_NODEBUG Error append_format(const char* fmt, Args&&... args) noexcept { 341 return _op_format(ModifyOp::kAppend, fmt, std::forward<Args>(args)...); 342 } 343 344 //! Appends a formatted string `fmt` (va_list version). 345 ASMJIT_INLINE_NODEBUG Error append_vformat(const char* fmt, va_list ap) noexcept { 346 return _op_vformat(ModifyOp::kAppend, fmt, ap); 347 } 348 349 ASMJIT_API Error pad_end(size_t n, char c = ' ') noexcept; 350 351 //! Truncate the string length into `new_size`. 352 ASMJIT_API Error truncate(size_t new_size) noexcept; 353 354 [[nodiscard]] 355 ASMJIT_API bool equals(const char* other, size_t size = SIZE_MAX) const noexcept; 356 357 [[nodiscard]] 358 ASMJIT_INLINE_NODEBUG bool equals(const String& other) const noexcept { return equals(other.data(), other.size()); } 359 360 //! \} 361 362 //! \name Internal Functions 363 //! \{ 364 365 //! Resets string to embedded and makes it empty (zero length, zero first char) 366 //! 367 //! \note This is always called internally after an external buffer was released as it zeroes all bytes 368 //! used by String's embedded storage. 369 inline void _reset_internal() noexcept { 370 for (size_t i = 0; i < ASMJIT_ARRAY_SIZE(_raw.uptr); i++) { 371 _raw.uptr[i] = 0; 372 } 373 } 374 375 inline void _set_size(size_t new_size) noexcept { 376 if (is_large_or_external()) { 377 _large.size = new_size; 378 } 379 else { 380 _small.type = uint8_t(new_size); 381 } 382 } 383 384 //! \} 385 }; 386 387 //! Temporary string builder, has statically allocated `N` bytes. 388 template<size_t N> 389 class StringTmp : public String { 390 public: 391 ASMJIT_NONCOPYABLE(StringTmp) 392 393 //! Embedded data. 394 char _embedded_data[Support::align_up(N + 1, sizeof(size_t))]; 395 396 //! \name Construction & Destruction 397 //! \{ 398 399 ASMJIT_INLINE_NODEBUG StringTmp() noexcept { 400 _reset_to_temporary(); 401 } 402 403 inline void _reset_to_temporary() noexcept { 404 _large.type = kTypeExternal; 405 _large.capacity = ASMJIT_ARRAY_SIZE(_embedded_data) - 1; 406 _large.data = _embedded_data; 407 _embedded_data[0] = '\0'; 408 } 409 410 //! \} 411 }; 412 413 //! \} 414 415 ASMJIT_END_NAMESPACE 416 417 #endif // ASMJIT_CORE_STRING_H_INCLUDED