1 // [AsmJit] 2 // Machine Code Generation for C++. 3 // 4 // [License] 5 // Zlib - See LICENSE.md file in the package. 6 7 #ifndef _ASMJIT_CORE_STRING_H 8 #define _ASMJIT_CORE_STRING_H 9 10 #include "../core/support.h" 11 #include "../core/zone.h" 12 13 ASMJIT_BEGIN_NAMESPACE 14 15 //! \addtogroup asmjit_support 16 //! \{ 17 18 // ============================================================================ 19 // [asmjit::String] 20 // ============================================================================ 21 22 //! A simple non-reference counted string that uses small string optimization (SSO). 23 //! 24 //! This string has 3 allocation possibilities: 25 //! 26 //! 1. Small - embedded buffer is used for up to `kSSOCapacity` characters. 27 //! This should handle most small strings and thus avoid dynamic 28 //! memory allocation for most use-cases. 29 //! 30 //! 2. Large - string that doesn't fit into an embedded buffer (or string 31 //! that was truncated from a larger buffer) and is owned by 32 //! AsmJit. When you destroy the string AsmJit would automatically 33 //! release the large buffer. 34 //! 35 //! 3. External - like Large (2), however, the large buffer is not owned by 36 //! AsmJit and won't be released when the string is destroyed 37 //! or reallocated. This is mostly useful for working with 38 //! larger temporary strings allocated on stack or with immutable 39 //! strings. 40 class String { 41 public: 42 ASMJIT_NONCOPYABLE(String) 43 44 //! String operation. 45 enum Op : uint32_t { 46 kOpAssign = 0, 47 kOpAppend = 1 48 }; 49 50 //! String format flags. 51 enum FormatFlags : uint32_t { 52 kFormatShowSign = 0x00000001u, 53 kFormatShowSpace = 0x00000002u, 54 kFormatAlternate = 0x00000004u, 55 kFormatSigned = 0x80000000u 56 }; 57 58 //! \cond INTERNAL 59 enum : uint32_t { 60 kLayoutSize = 32, 61 kSSOCapacity = kLayoutSize - 2 62 }; 63 64 //! String type. 65 enum Type : uint8_t { 66 kTypeLarge = 0x1Fu, //!< Large string (owned by String). 67 kTypeExternal = 0x20u //!< External string (zone allocated or not owned by String). 68 }; 69 70 union Raw { 71 uint8_t u8[kLayoutSize]; 72 uint64_t u64[kLayoutSize / sizeof(uint64_t)]; 73 uintptr_t uptr[kLayoutSize / sizeof(uintptr_t)]; 74 }; 75 76 struct Small { 77 uint8_t type; 78 char data[kSSOCapacity + 1u]; 79 }; 80 81 struct Large { 82 uint8_t type; 83 uint8_t reserved[sizeof(uintptr_t) - 1]; 84 size_t size; 85 size_t capacity; 86 char* data; 87 }; 88 89 union { 90 uint8_t _type; 91 Raw _raw; 92 Small _small; 93 Large _large; 94 }; 95 //! \endcond 96 97 //! \name Construction & Destruction 98 //! \{ 99 String()100 inline String() noexcept 101 : _small {} {} 102 String(String && other)103 inline String(String&& other) noexcept { 104 for (size_t i = 0; i < ASMJIT_ARRAY_SIZE(_raw.uptr); i++) 105 _raw.uptr[i] = other._raw.uptr[i]; 106 other._resetInternal(); 107 } 108 ~String()109 inline ~String() noexcept { 110 reset(); 111 } 112 113 //! Reset the string into a construction state. 114 ASMJIT_API Error reset() noexcept; 115 116 //! \} 117 118 //! \name Overloaded Operators 119 //! \{ 120 121 inline bool operator==(const char* other) const noexcept { return eq(other); } 122 inline bool operator!=(const char* other) const noexcept { return !eq(other); } 123 124 inline bool operator==(const String& other) const noexcept { return eq(other); } 125 inline bool operator!=(const String& other) const noexcept { return !eq(other); } 126 127 //! \} 128 129 //! \name Accessors 130 //! \{ 131 isLarge()132 inline bool isLarge() const noexcept { return _type >= kTypeLarge; } isExternal()133 inline bool isExternal() const noexcept { return _type == kTypeExternal; } 134 empty()135 inline bool empty() const noexcept { return size() == 0; } size()136 inline size_t size() const noexcept { return isLarge() ? size_t(_large.size) : size_t(_type); } capacity()137 inline size_t capacity() const noexcept { return isLarge() ? _large.capacity : size_t(kSSOCapacity); } 138 data()139 inline char* data() noexcept { return isLarge() ? _large.data : _small.data; } data()140 inline const char* data() const noexcept { return isLarge() ? _large.data : _small.data; } 141 end()142 inline char* end() noexcept { return data() + size(); } end()143 inline const char* end() const noexcept { return data() + size(); } 144 145 //! \} 146 147 //! \name String Operations 148 //! \{ 149 150 //! Clear the content of the string. 151 ASMJIT_API Error clear() noexcept; 152 153 ASMJIT_API char* prepare(uint32_t op, size_t size) noexcept; 154 155 ASMJIT_API Error _opString(uint32_t op, const char* str, size_t size = SIZE_MAX) noexcept; 156 ASMJIT_API Error _opFormat(uint32_t op, const char* fmt, ...) noexcept; 157 ASMJIT_API Error _opVFormat(uint32_t op, const char* fmt, va_list ap) noexcept; 158 ASMJIT_API Error _opChar(uint32_t op, char c) noexcept; 159 ASMJIT_API Error _opChars(uint32_t op, char c, size_t n) noexcept; 160 ASMJIT_API Error _opNumber(uint32_t op, uint64_t i, uint32_t base = 0, size_t width = 0, uint32_t flags = 0) noexcept; 161 ASMJIT_API Error _opHex(uint32_t op, const void* data, size_t size, char separator = '\0') noexcept; 162 163 //! Replace the string content to a string specified by `data` and `size`. If 164 //! `size` is `SIZE_MAX` then it's considered null-terminated and its length 165 //! will be obtained through `strlen()`. 166 ASMJIT_API Error assignString(const char* data, size_t size = SIZE_MAX) noexcept; 167 168 //! Replace the current content by a formatted string `fmt`. 169 template<typename... Args> assignFormat(const char * fmt,Args &&...args)170 inline Error assignFormat(const char* fmt, Args&&... args) noexcept { 171 return _opFormat(kOpAssign, fmt, std::forward<Args>(args)...); 172 } 173 174 //! Replace the current content by a formatted string `fmt` (va_list version). assignVFormat(const char * fmt,va_list ap)175 inline Error assignVFormat(const char* fmt, va_list ap) noexcept { 176 return _opVFormat(kOpAssign, fmt, ap); 177 } 178 179 //! Replace the current content by a single `c` character. assignChar(char c)180 inline Error assignChar(char c) noexcept { 181 return _opChar(kOpAssign, c); 182 } 183 184 //! Replace the current content by `c` character `n` times. assignChars(char c,size_t n)185 inline Error assignChars(char c, size_t n) noexcept { 186 return _opChars(kOpAssign, c, n); 187 } 188 189 //! Replace the current content by a formatted integer `i` (signed). 190 inline Error assignInt(int64_t i, uint32_t base = 0, size_t width = 0, uint32_t flags = 0) noexcept { 191 return _opNumber(kOpAssign, uint64_t(i), base, width, flags | kFormatSigned); 192 } 193 194 //! Replace the current content by a formatted integer `i` (unsigned). 195 inline Error assignUInt(uint64_t i, uint32_t base = 0, size_t width = 0, uint32_t flags = 0) noexcept { 196 return _opNumber(kOpAssign, i, base, width, flags); 197 } 198 199 //! Replace the current content by the given `data` converted to a HEX string. 200 inline Error assignHex(const void* data, size_t size, char separator = '\0') noexcept { 201 return _opHex(kOpAssign, data, size, separator); 202 } 203 204 //! Append string `str` of size `size` (or possibly null terminated). 205 inline Error appendString(const char* str, size_t size = SIZE_MAX) noexcept { 206 return _opString(kOpAppend, str, size); 207 } 208 209 template<typename... Args> appendFormat(const char * fmt,Args &&...args)210 inline Error appendFormat(const char* fmt, Args&&... args) noexcept { 211 return _opFormat(kOpAppend, fmt, std::forward<Args>(args)...); 212 } 213 214 //! Append a formatted string `fmt` (va_list version). appendVFormat(const char * fmt,va_list ap)215 inline Error appendVFormat(const char* fmt, va_list ap) noexcept { 216 return _opVFormat(kOpAppend, fmt, ap); 217 } 218 219 //! Append a single `c` character. appendChar(char c)220 inline Error appendChar(char c) noexcept { 221 return _opChar(kOpAppend, c); 222 } 223 224 //! Append `c` character `n` times. appendChars(char c,size_t n)225 inline Error appendChars(char c, size_t n) noexcept { 226 return _opChars(kOpAppend, c, n); 227 } 228 229 ASMJIT_API Error padEnd(size_t n, char c = ' ') noexcept; 230 231 //! Append `i`. 232 inline Error appendInt(int64_t i, uint32_t base = 0, size_t width = 0, uint32_t flags = 0) noexcept { 233 return _opNumber(kOpAppend, uint64_t(i), base, width, flags | kFormatSigned); 234 } 235 236 //! Append `i`. 237 inline Error appendUInt(uint64_t i, uint32_t base = 0, size_t width = 0, uint32_t flags = 0) noexcept { 238 return _opNumber(kOpAppend, i, base, width, flags); 239 } 240 241 //! Append the given `data` converted to a HEX string. 242 inline Error appendHex(const void* data, size_t size, char separator = '\0') noexcept { 243 return _opHex(kOpAppend, data, size, separator); 244 } 245 246 //! Truncate the string length into `newSize`. 247 ASMJIT_API Error truncate(size_t newSize) noexcept; 248 249 ASMJIT_API bool eq(const char* other, size_t size = SIZE_MAX) const noexcept; eq(const String & other)250 inline bool eq(const String& other) const noexcept { return eq(other.data(), other.size()); } 251 252 //! \} 253 254 //! \name Internal Functions 255 //! \{ 256 257 //! Resets string to embedded and makes it empty (zero length, zero first char) 258 //! 259 //! \note This is always called internally after an external buffer was released 260 //! as it zeroes all bytes used by String's embedded storage. _resetInternal()261 inline void _resetInternal() noexcept { 262 for (size_t i = 0; i < ASMJIT_ARRAY_SIZE(_raw.uptr); i++) 263 _raw.uptr[i] = 0; 264 } 265 _setSize(size_t newSize)266 inline void _setSize(size_t newSize) noexcept { 267 if (isLarge()) 268 _large.size = newSize; 269 else 270 _small.type = uint8_t(newSize); 271 } 272 273 //! \} 274 }; 275 276 // ============================================================================ 277 // [asmjit::StringTmp] 278 // ============================================================================ 279 280 //! Temporary string builder, has statically allocated `N` bytes. 281 template<size_t N> 282 class StringTmp : public String { 283 public: 284 ASMJIT_NONCOPYABLE(StringTmp<N>) 285 286 //! Embedded data. 287 char _embeddedData[Support::alignUp(N + 1, sizeof(size_t))]; 288 289 //! \name Construction & Destruction 290 //! \{ 291 StringTmp()292 inline StringTmp() noexcept { 293 _resetToTemporary(); 294 } 295 _resetToTemporary()296 inline void _resetToTemporary() noexcept { 297 _large.type = kTypeExternal; 298 _large.capacity = ASMJIT_ARRAY_SIZE(_embeddedData) - 1; 299 _large.data = _embeddedData; 300 _embeddedData[0] = '\0'; 301 } 302 303 //! \} 304 }; 305 306 // ============================================================================ 307 // [asmjit::FixedString] 308 // ============================================================================ 309 310 //! A fixed string - only useful for strings that would never exceed `N - 1` 311 //! characters; always null-terminated. 312 template<size_t N> 313 union FixedString { 314 enum : uint32_t { 315 kNumU32 = uint32_t((N + sizeof(uint32_t) - 1) / sizeof(uint32_t)) 316 }; 317 318 char str[kNumU32 * sizeof(uint32_t)]; 319 uint32_t u32[kNumU32]; 320 321 //! \name Utilities 322 //! \{ 323 eq(const char * other)324 inline bool eq(const char* other) const noexcept { 325 return strcmp(str, other) == 0; 326 } 327 328 //! \} 329 }; 330 331 //! \} 332 333 ASMJIT_END_NAMESPACE 334 335 #endif // _ASMJIT_CORE_STRING_H 336