Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/dep/zydis/include/Zydis/Internal/String.h
4219 views
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
472