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