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