xref: /haiku/headers/libs/zydis/Zydis/Internal/String.h (revision 909af08f4328301fbdef1ffb41f566c3b5bec0c7)
1 /***************************************************************************************************
2 
3   Zyan Disassembler Library (Zydis)
4 
5   Original Author : Florian Bernd, Joel Hoener
6 
7  * Permission is hereby granted, free of charge, to any person obtaining a copy
8  * of this software and associated documentation files (the "Software"), to deal
9  * in the Software without restriction, including without limitation the rights
10  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11  * copies of the Software, and to permit persons to whom the Software is
12  * furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in all
15  * copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23  * SOFTWARE.
24 
25 ***************************************************************************************************/
26 
27 /**
28  * @file
29  * Provides some internal, more performant, but unsafe helper functions for the `ZyanString`
30  * data-type.
31  *
32  * Most of these functions are very similar to the ones in `Zycore/String.h`, but inlined and
33  * without optional overhead like parameter-validation checks, etc ...
34  *
35  * The `ZyanString` data-type is able to dynamically allocate memory on the heap, but as `Zydis` is
36  * designed to be a non-'malloc'ing library, all functions in this file assume that the instances
37  * they are operating on are created with a user-defined static-buffer.
38  */
39 
40 #ifndef ZYDIS_INTERNAL_STRING_H
41 #define ZYDIS_INTERNAL_STRING_H
42 
43 #include <Zycore/LibC.h>
44 #include <Zycore/String.h>
45 #include <Zycore/Types.h>
46 #include <Zycore/Format.h>
47 #include <Zydis/ShortString.h>
48 #include <Zycore/Defines.h>
49 #include <Zycore/Status.h>
50 #include <Zycore/Vector.h>
51 
52 #ifdef __cplusplus
53 extern "C" {
54 #endif
55 
56 /* ============================================================================================== */
57 /* Enums and types                                                                                */
58 /* ============================================================================================== */
59 
60 /* ---------------------------------------------------------------------------------------------- */
61 /* Letter Case                                                                                    */
62 /* ---------------------------------------------------------------------------------------------- */
63 
64 /**
65  * Defines the `ZydisLetterCase` enum.
66  */
67 typedef enum ZydisLetterCase_
68 {
69     /**
70      * Uses the given text "as is".
71      */
72     ZYDIS_LETTER_CASE_DEFAULT,
73     /**
74      * Converts the given text to lowercase letters.
75      */
76     ZYDIS_LETTER_CASE_LOWER,
77     /**
78      * Converts the given text to uppercase letters.
79      */
80     ZYDIS_LETTER_CASE_UPPER,
81 
82     /**
83      * Maximum value of this enum.
84      */
85     ZYDIS_LETTER_CASE_MAX_VALUE = ZYDIS_LETTER_CASE_UPPER,
86     /**
87      * The minimum number of bits required to represent all values of this enum.
88      */
89     ZYDIS_LETTER_CASE_REQUIRED_BITS = ZYAN_BITS_TO_REPRESENT(ZYDIS_LETTER_CASE_MAX_VALUE)
90 } ZydisLetterCase;
91 
92 /* ---------------------------------------------------------------------------------------------- */
93 
94 /* ============================================================================================== */
95 /* Macros                                                                                         */
96 /* ============================================================================================== */
97 
98 /* ---------------------------------------------------------------------------------------------- */
99 /* Internal macros                                                                                */
100 /* ---------------------------------------------------------------------------------------------- */
101 
102 /**
103  * Checks for a terminating '\0' character at the end of the string data.
104  */
105 #define ZYDIS_STRING_ASSERT_NULLTERMINATION(string) \
106       ZYAN_ASSERT(*(char*)((ZyanU8*)(string)->vector.data + (string)->vector.size - 1) == '\0');
107 
108 /**
109  * Writes a terminating '\0' character at the end of the string data.
110  */
111 #define ZYDIS_STRING_NULLTERMINATE(string) \
112       *(char*)((ZyanU8*)(string)->vector.data + (string)->vector.size - 1) = '\0';
113 
114 /* ---------------------------------------------------------------------------------------------- */
115 
116 /* ============================================================================================== */
117 /* Internal Functions                                                                             */
118 /* ============================================================================================== */
119 
120 /* ---------------------------------------------------------------------------------------------- */
121 /* Appending                                                                                      */
122 /* ---------------------------------------------------------------------------------------------- */
123 
124 /**
125  * Appends the content of the source string to the end of the destination string.
126  *
127  * @param   destination The destination string.
128  * @param   source      The source string.
129  *
130  * @return  A zyan status code.
131  */
132 ZYAN_INLINE ZyanStatus ZydisStringAppend(ZyanString* destination, const ZyanStringView* source)
133 {
134     ZYAN_ASSERT(destination && source);
135     ZYAN_ASSERT(!destination->vector.allocator);
136     ZYAN_ASSERT(destination->vector.size && source->string.vector.size);
137 
138     if (destination->vector.size + source->string.vector.size - 1 > destination->vector.capacity)
139     {
140         return ZYAN_STATUS_INSUFFICIENT_BUFFER_SIZE;
141     }
142 
143     ZYAN_MEMCPY((char*)destination->vector.data + destination->vector.size - 1,
144         source->string.vector.data, source->string.vector.size - 1);
145 
146     destination->vector.size += source->string.vector.size - 1;
147     ZYDIS_STRING_NULLTERMINATE(destination);
148 
149     return ZYAN_STATUS_SUCCESS;
150 }
151 
152 /**
153  * Appends the content of the source string to the end of the destination
154  * string, converting the characters to the specified letter-case.
155  *
156  * @param   destination The destination string.
157  * @param   source      The source string.
158  * @param   letter_case The desired letter-case.
159  *
160  * @return  A zyan status code.
161  */
162 ZYAN_INLINE ZyanStatus ZydisStringAppendCase(ZyanString* destination, const ZyanStringView* source,
163     ZydisLetterCase letter_case)
164 {
165     ZYAN_ASSERT(destination && source);
166     ZYAN_ASSERT(!destination->vector.allocator);
167     ZYAN_ASSERT(destination->vector.size && source->string.vector.size);
168 
169     if (destination->vector.size + source->string.vector.size - 1 > destination->vector.capacity)
170     {
171         return ZYAN_STATUS_INSUFFICIENT_BUFFER_SIZE;
172     }
173 
174     ZYAN_MEMCPY((char*)destination->vector.data + destination->vector.size - 1,
175         source->string.vector.data, source->string.vector.size - 1);
176 
177     switch (letter_case)
178     {
179     case ZYDIS_LETTER_CASE_DEFAULT:
180         break;
181     case ZYDIS_LETTER_CASE_LOWER:
182     {
183         const ZyanUSize index = destination->vector.size - 1;
184         const ZyanUSize count = source->string.vector.size - 1;
185         char* s = (char*)destination->vector.data + index;
186         for (ZyanUSize i = index; i < index + count; ++i)
187         {
188             const char c = *s;
189             if ((c >= 'A') && (c <= 'Z'))
190             {
191                 *s = c | 32;
192             }
193             ++s;
194         }
195         break;
196     }
197     case ZYDIS_LETTER_CASE_UPPER:
198     {
199         const ZyanUSize index = destination->vector.size - 1;
200         const ZyanUSize count = source->string.vector.size - 1;
201         char* s = (char*)destination->vector.data + index;
202         for (ZyanUSize i = index; i < index + count; ++i)
203         {
204             const char c = *s;
205             if ((c >= 'a') && (c <= 'z'))
206             {
207                 *s = c & ~32;
208             }
209             ++s;
210         }
211         break;
212     }
213     default:
214         ZYAN_UNREACHABLE;
215     }
216 
217     destination->vector.size += source->string.vector.size - 1;
218     ZYDIS_STRING_NULLTERMINATE(destination);
219 
220     return ZYAN_STATUS_SUCCESS;
221 }
222 
223 /**
224  * Appends the content of the source short-string to the end of the destination string.
225  *
226  * @param   destination The destination string.
227  * @param   source      The source string.
228  *
229  * @return  A zyan status code.
230  */
231 ZYAN_INLINE ZyanStatus ZydisStringAppendShort(ZyanString* destination,
232     const ZydisShortString* source)
233 {
234     ZYAN_ASSERT(destination && source);
235     ZYAN_ASSERT(!destination->vector.allocator);
236     ZYAN_ASSERT(destination->vector.size && source->size);
237 
238     if (destination->vector.size + source->size > destination->vector.capacity)
239     {
240         return ZYAN_STATUS_INSUFFICIENT_BUFFER_SIZE;
241     }
242 
243     ZYAN_MEMCPY((char*)destination->vector.data + destination->vector.size - 1, source->data,
244         (ZyanUSize)source->size + 1);
245 
246     destination->vector.size += source->size;
247     ZYDIS_STRING_ASSERT_NULLTERMINATION(destination);
248 
249     return ZYAN_STATUS_SUCCESS;
250 }
251 
252 /**
253  * Appends the content of the source short-string to the end of the destination string,
254  * converting the characters to the specified letter-case.
255  *
256  * @param   destination The destination string.
257  * @param   source      The source string.
258  * @param   letter_case The desired letter-case.
259  *
260  * @return  A zyan status code.
261  */
262 ZYAN_INLINE ZyanStatus ZydisStringAppendShortCase(ZyanString* destination,
263     const ZydisShortString* source, ZydisLetterCase letter_case)
264 {
265     ZYAN_ASSERT(destination && source);
266     ZYAN_ASSERT(!destination->vector.allocator);
267     ZYAN_ASSERT(destination->vector.size && source->size);
268 
269     if (destination->vector.size + source->size > destination->vector.capacity)
270     {
271         return ZYAN_STATUS_INSUFFICIENT_BUFFER_SIZE;
272     }
273 
274     ZYAN_MEMCPY((char*)destination->vector.data + destination->vector.size - 1, source->data,
275         (ZyanUSize)source->size + 1);
276 
277     switch (letter_case)
278     {
279     case ZYDIS_LETTER_CASE_DEFAULT:
280         break;
281     case ZYDIS_LETTER_CASE_LOWER:
282     {
283         const ZyanUSize index = destination->vector.size - 1;
284         const ZyanUSize count = source->size;
285         char* s = (char*)destination->vector.data + index;
286         for (ZyanUSize i = index; i < index + count; ++i)
287         {
288             const char c = *s;
289             if ((c >= 'A') && (c <= 'Z'))
290             {
291                 *s = c | 32;
292             }
293             ++s;
294         }
295         break;
296     }
297     case ZYDIS_LETTER_CASE_UPPER:
298     {
299         const ZyanUSize index = destination->vector.size - 1;
300         const ZyanUSize count = source->size;
301         char* s = (char*)destination->vector.data + index;
302         for (ZyanUSize i = index; i < index + count; ++i)
303         {
304             const char c = *s;
305             if ((c >= 'a') && (c <= 'z'))
306             {
307                 *s = c & ~32;
308             }
309             ++s;
310         }
311         break;
312     }
313     default:
314         ZYAN_UNREACHABLE;
315     }
316 
317     destination->vector.size += source->size;
318     ZYDIS_STRING_ASSERT_NULLTERMINATION(destination);
319 
320     return ZYAN_STATUS_SUCCESS;
321 }
322 
323 /* ---------------------------------------------------------------------------------------------- */
324 /* Formatting                                                                                     */
325 /* ---------------------------------------------------------------------------------------------- */
326 
327 /**
328  * Formats the given unsigned ordinal `value` to its decimal text-representation and
329  * appends it to the `string`.
330  *
331  * @param   string          A pointer to the `ZyanString` instance.
332  * @param   value           The value to append.
333  * @param   padding_length  Padds the converted value with leading zeros, if the number of chars is
334  *                          less than the `padding_length`.
335  * @param   prefix          The string to use as prefix or `ZYAN_NULL`, if not needed.
336  * @param   suffix          The string to use as suffix or `ZYAN_NULL`, if not needed.
337  *
338  * @return  A zyan status code.
339  *
340  * This function will fail, if the `ZYAN_STRING_IS_IMMUTABLE` flag is set for the specified
341  * `ZyanString` instance.
342  */
343 ZyanStatus ZydisStringAppendDecU(ZyanString* string, ZyanU64 value, ZyanU8 padding_length,
344     const ZyanStringView* prefix, const ZyanStringView* suffix);
345 
346 /**
347  * Formats the given signed ordinal `value` to its decimal text-representation and
348  * appends it to the `string`.
349  *
350  * @param   string          A pointer to the `ZyanString` instance.
351  * @param   value           The value to append.
352  * @param   padding_length  Padds the converted value with leading zeros, if the number of chars is
353  *                          less than the `padding_length`.
354  * @param   force_sign      Enable this option to print the `+` sign for positive numbers.
355  * @param   prefix          The string to use as prefix or `ZYAN_NULL`, if not needed.
356  * @param   suffix          The string to use as suffix or `ZYAN_NULL`, if not needed.
357  *
358  * @return  A zyan status code.
359  *
360  * This function will fail, if the `ZYAN_STRING_IS_IMMUTABLE` flag is set for the specified
361  * `ZyanString` instance.
362  */
363 ZYAN_INLINE ZyanStatus ZydisStringAppendDecS(ZyanString* string, ZyanI64 value,
364     ZyanU8 padding_length, ZyanBool force_sign, const ZyanStringView* prefix,
365     const ZyanStringView* suffix)
366 {
367     static const ZydisShortString str_add = ZYDIS_MAKE_SHORTSTRING("+");
368     static const ZydisShortString str_sub = ZYDIS_MAKE_SHORTSTRING("-");
369 
370     if (value < 0)
371     {
372         ZYAN_CHECK(ZydisStringAppendShort(string, &str_sub));
373         if (prefix)
374         {
375             ZYAN_CHECK(ZydisStringAppend(string, prefix));
376         }
377         return ZydisStringAppendDecU(string, ZyanAbsI64(value), padding_length,
378             (const ZyanStringView*)ZYAN_NULL, suffix);
379     }
380 
381     if (force_sign)
382     {
383         ZYAN_ASSERT(value >= 0);
384         ZYAN_CHECK(ZydisStringAppendShort(string, &str_add));
385     }
386     return ZydisStringAppendDecU(string, value, padding_length, prefix, suffix);
387 }
388 
389 /**
390  * Formats the given unsigned ordinal `value` to its hexadecimal text-representation and
391  * appends it to the `string`.
392  *
393  * @param   string                  A pointer to the `ZyanString` instance.
394  * @param   value                   The value to append.
395  * @param   padding_length          Pads the converted value with leading zeros if the number of
396  *                                  chars is less than the `padding_length`.
397  * @param   force_leading_number    Enable this option to prepend a leading `0` if the first
398  *                                  character is non-numeric.
399  * @param   uppercase               Enable this option to use uppercase letters ('A'-'F') instead
400  *                                  of lowercase ones ('a'-'f').
401  * @param   prefix                  The string to use as prefix or `ZYAN_NULL`, if not needed.
402  * @param   suffix                  The string to use as suffix or `ZYAN_NULL`, if not needed.
403  *
404  * @return  A zyan status code.
405  *
406  * This function will fail, if the `ZYAN_STRING_IS_IMMUTABLE` flag is set for the specified
407  * `ZyanString` instance.
408  */
409 ZyanStatus ZydisStringAppendHexU(ZyanString* string, ZyanU64 value, ZyanU8 padding_length,
410     ZyanBool force_leading_number, ZyanBool uppercase, const ZyanStringView* prefix,
411     const ZyanStringView* suffix);
412 
413 /**
414  * Formats the given signed ordinal `value` to its hexadecimal text-representation and
415  * appends it to the `string`.
416  *
417  * @param   string                  A pointer to the `ZyanString` instance.
418  * @param   value                   The value to append.
419  * @param   padding_length          Padds the converted value with leading zeros, if the number of
420  *                                  chars is less than the `padding_length` (the sign char does not
421  *                                  count).
422  * @param   force_leading_number    Enable this option to prepend a leading `0`, if the first
423  *                                  character is non-numeric.
424  * @param   uppercase               Enable this option to use uppercase letters ('A'-'F') instead
425  *                                  of lowercase ones ('a'-'f').
426  * @param   force_sign              Enable this option to print the `+` sign for positive numbers.
427  * @param   prefix                  The string to use as prefix or `ZYAN_NULL`, if not needed.
428  * @param   suffix                  The string to use as suffix or `ZYAN_NULL`, if not needed.
429  *
430  * @return  A zyan status code.
431  *
432  * This function will fail if the `ZYAN_STRING_IS_IMMUTABLE` flag is set for the specified
433  * `ZyanString` instance.
434  */
435 ZYAN_INLINE ZyanStatus ZydisStringAppendHexS(ZyanString* string, ZyanI64 value,
436     ZyanU8 padding_length, ZyanBool force_leading_number, ZyanBool uppercase, ZyanBool force_sign,
437     const ZyanStringView* prefix, const ZyanStringView* suffix)
438 {
439     static const ZydisShortString str_add = ZYDIS_MAKE_SHORTSTRING("+");
440     static const ZydisShortString str_sub = ZYDIS_MAKE_SHORTSTRING("-");
441 
442     if (value < 0)
443     {
444         ZYAN_CHECK(ZydisStringAppendShort(string, &str_sub));
445         if (prefix)
446         {
447             ZYAN_CHECK(ZydisStringAppend(string, prefix));
448         }
449         return ZydisStringAppendHexU(string, ZyanAbsI64(value), padding_length,
450             force_leading_number, uppercase, (const ZyanStringView*)ZYAN_NULL, suffix);
451     }
452 
453     if (force_sign)
454     {
455         ZYAN_ASSERT(value >= 0);
456         ZYAN_CHECK(ZydisStringAppendShort(string, &str_add));
457     }
458     return ZydisStringAppendHexU(string, value, padding_length, force_leading_number, uppercase,
459         prefix, suffix);
460 }
461 
462 /* ---------------------------------------------------------------------------------------------- */
463 
464 /* ============================================================================================== */
465 
466 #ifdef __cplusplus
467 }
468 #endif
469 
470 #endif // ZYDIS_INTERNAL_STRING_H
471