mirror of
				https://github.com/RetroDECK/Duckstation.git
				synced 2025-04-10 19:15:14 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			471 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			471 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /***************************************************************************************************
 | |
| 
 | |
|   Zyan Disassembler Library (Zydis)
 | |
| 
 | |
|   Original Author : Florian Bernd, Joel Hoener
 | |
| 
 | |
|  * Permission is hereby granted, free of charge, to any person obtaining a copy
 | |
|  * of this software and associated documentation files (the "Software"), to deal
 | |
|  * in the Software without restriction, including without limitation the rights
 | |
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | |
|  * copies of the Software, and to permit persons to whom the Software is
 | |
|  * furnished to do so, subject to the following conditions:
 | |
|  *
 | |
|  * The above copyright notice and this permission notice shall be included in all
 | |
|  * copies or substantial portions of the Software.
 | |
|  *
 | |
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | |
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | |
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | |
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | |
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | |
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | |
|  * SOFTWARE.
 | |
| 
 | |
| ***************************************************************************************************/
 | |
| 
 | |
| /**
 | |
|  * @file
 | |
|  * Provides some internal, more performant, but unsafe helper functions for the `ZyanString`
 | |
|  * data-type.
 | |
|  *
 | |
|  * Most of these functions are very similar to the ones in `Zycore/String.h`, but inlined and
 | |
|  * without optional overhead like parameter-validation checks, etc ...
 | |
|  *
 | |
|  * The `ZyanString` data-type is able to dynamically allocate memory on the heap, but as `Zydis` is
 | |
|  * designed to be a non-'malloc'ing library, all functions in this file assume that the instances
 | |
|  * they are operating on are created with a user-defined static-buffer.
 | |
|  */
 | |
| 
 | |
| #ifndef ZYDIS_INTERNAL_STRING_H
 | |
| #define ZYDIS_INTERNAL_STRING_H
 | |
| 
 | |
| #include <Zycore/LibC.h>
 | |
| #include <Zycore/String.h>
 | |
| #include <Zycore/Types.h>
 | |
| #include <Zycore/Format.h>
 | |
| #include <Zydis/ShortString.h>
 | |
| #include <Zycore/Defines.h>
 | |
| #include <Zycore/Status.h>
 | |
| #include <Zycore/Vector.h>
 | |
| 
 | |
| #ifdef __cplusplus
 | |
| extern "C" {
 | |
| #endif
 | |
| 
 | |
| /* ============================================================================================== */
 | |
| /* Enums and types                                                                                */
 | |
| /* ============================================================================================== */
 | |
| 
 | |
| /* ---------------------------------------------------------------------------------------------- */
 | |
| /* Letter Case                                                                                    */
 | |
| /* ---------------------------------------------------------------------------------------------- */
 | |
| 
 | |
| /**
 | |
|  * Defines the `ZydisLetterCase` enum.
 | |
|  */
 | |
| typedef enum ZydisLetterCase_
 | |
| {
 | |
|     /**
 | |
|      * Uses the given text "as is".
 | |
|      */
 | |
|     ZYDIS_LETTER_CASE_DEFAULT,
 | |
|     /**
 | |
|      * Converts the given text to lowercase letters.
 | |
|      */
 | |
|     ZYDIS_LETTER_CASE_LOWER,
 | |
|     /**
 | |
|      * Converts the given text to uppercase letters.
 | |
|      */
 | |
|     ZYDIS_LETTER_CASE_UPPER,
 | |
| 
 | |
|     /**
 | |
|      * Maximum value of this enum.
 | |
|      */
 | |
|     ZYDIS_LETTER_CASE_MAX_VALUE = ZYDIS_LETTER_CASE_UPPER,
 | |
|     /**
 | |
|      * The minimum number of bits required to represent all values of this enum.
 | |
|      */
 | |
|     ZYDIS_LETTER_CASE_REQUIRED_BITS = ZYAN_BITS_TO_REPRESENT(ZYDIS_LETTER_CASE_MAX_VALUE)
 | |
| } ZydisLetterCase;
 | |
| 
 | |
| /* ---------------------------------------------------------------------------------------------- */
 | |
| 
 | |
| /* ============================================================================================== */
 | |
| /* Macros                                                                                         */
 | |
| /* ============================================================================================== */
 | |
| 
 | |
| /* ---------------------------------------------------------------------------------------------- */
 | |
| /* Internal macros                                                                                */
 | |
| /* ---------------------------------------------------------------------------------------------- */
 | |
| 
 | |
| /**
 | |
|  * Checks for a terminating '\0' character at the end of the string data.
 | |
|  */
 | |
| #define ZYDIS_STRING_ASSERT_NULLTERMINATION(string) \
 | |
|       ZYAN_ASSERT(*(char*)((ZyanU8*)(string)->vector.data + (string)->vector.size - 1) == '\0');
 | |
| 
 | |
| /**
 | |
|  * Writes a terminating '\0' character at the end of the string data.
 | |
|  */
 | |
| #define ZYDIS_STRING_NULLTERMINATE(string) \
 | |
|       *(char*)((ZyanU8*)(string)->vector.data + (string)->vector.size - 1) = '\0';
 | |
| 
 | |
| /* ---------------------------------------------------------------------------------------------- */
 | |
| 
 | |
| /* ============================================================================================== */
 | |
| /* Internal Functions                                                                             */
 | |
| /* ============================================================================================== */
 | |
| 
 | |
| /* ---------------------------------------------------------------------------------------------- */
 | |
| /* Appending                                                                                      */
 | |
| /* ---------------------------------------------------------------------------------------------- */
 | |
| 
 | |
| /**
 | |
|  * Appends the content of the source string to the end of the destination string.
 | |
|  *
 | |
|  * @param   destination The destination string.
 | |
|  * @param   source      The source string.
 | |
|  *
 | |
|  * @return  A zyan status code.
 | |
|  */
 | |
| ZYAN_INLINE ZyanStatus ZydisStringAppend(ZyanString* destination, const ZyanStringView* source)
 | |
| {
 | |
|     ZYAN_ASSERT(destination && source);
 | |
|     ZYAN_ASSERT(!destination->vector.allocator);
 | |
|     ZYAN_ASSERT(destination->vector.size && source->string.vector.size);
 | |
| 
 | |
|     if (destination->vector.size + source->string.vector.size - 1 > destination->vector.capacity)
 | |
|     {
 | |
|         return ZYAN_STATUS_INSUFFICIENT_BUFFER_SIZE;
 | |
|     }
 | |
| 
 | |
|     ZYAN_MEMCPY((char*)destination->vector.data + destination->vector.size - 1,
 | |
|         source->string.vector.data, source->string.vector.size - 1);
 | |
| 
 | |
|     destination->vector.size += source->string.vector.size - 1;
 | |
|     ZYDIS_STRING_NULLTERMINATE(destination);
 | |
| 
 | |
|     return ZYAN_STATUS_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Appends the content of the source string to the end of the destination
 | |
|  * string, converting the characters to the specified letter-case.
 | |
|  *
 | |
|  * @param   destination The destination string.
 | |
|  * @param   source      The source string.
 | |
|  * @param   letter_case The desired letter-case.
 | |
|  *
 | |
|  * @return  A zyan status code.
 | |
|  */
 | |
| ZYAN_INLINE ZyanStatus ZydisStringAppendCase(ZyanString* destination, const ZyanStringView* source,
 | |
|     ZydisLetterCase letter_case)
 | |
| {
 | |
|     ZYAN_ASSERT(destination && source);
 | |
|     ZYAN_ASSERT(!destination->vector.allocator);
 | |
|     ZYAN_ASSERT(destination->vector.size && source->string.vector.size);
 | |
| 
 | |
|     if (destination->vector.size + source->string.vector.size - 1 > destination->vector.capacity)
 | |
|     {
 | |
|         return ZYAN_STATUS_INSUFFICIENT_BUFFER_SIZE;
 | |
|     }
 | |
| 
 | |
|     ZYAN_MEMCPY((char*)destination->vector.data + destination->vector.size - 1,
 | |
|         source->string.vector.data, source->string.vector.size - 1);
 | |
| 
 | |
|     switch (letter_case)
 | |
|     {
 | |
|     case ZYDIS_LETTER_CASE_DEFAULT:
 | |
|         break;
 | |
|     case ZYDIS_LETTER_CASE_LOWER:
 | |
|     {
 | |
|         const ZyanUSize index = destination->vector.size - 1;
 | |
|         const ZyanUSize count = source->string.vector.size - 1;
 | |
|         char* s = (char*)destination->vector.data + index;
 | |
|         for (ZyanUSize i = index; i < index + count; ++i)
 | |
|         {
 | |
|             const char c = *s;
 | |
|             if ((c >= 'A') && (c <= 'Z'))
 | |
|             {
 | |
|                 *s = c | 32;
 | |
|             }
 | |
|             ++s;
 | |
|         }
 | |
|         break;
 | |
|     }
 | |
|     case ZYDIS_LETTER_CASE_UPPER:
 | |
|     {
 | |
|         const ZyanUSize index = destination->vector.size - 1;
 | |
|         const ZyanUSize count = source->string.vector.size - 1;
 | |
|         char* s = (char*)destination->vector.data + index;
 | |
|         for (ZyanUSize i = index; i < index + count; ++i)
 | |
|         {
 | |
|             const char c = *s;
 | |
|             if ((c >= 'a') && (c <= 'z'))
 | |
|             {
 | |
|                 *s = c & ~32;
 | |
|             }
 | |
|             ++s;
 | |
|         }
 | |
|         break;
 | |
|     }
 | |
|     default:
 | |
|         ZYAN_UNREACHABLE;
 | |
|     }
 | |
| 
 | |
|     destination->vector.size += source->string.vector.size - 1;
 | |
|     ZYDIS_STRING_NULLTERMINATE(destination);
 | |
| 
 | |
|     return ZYAN_STATUS_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Appends the content of the source short-string to the end of the destination string.
 | |
|  *
 | |
|  * @param   destination The destination string.
 | |
|  * @param   source      The source string.
 | |
|  *
 | |
|  * @return  A zyan status code.
 | |
|  */
 | |
| ZYAN_INLINE ZyanStatus ZydisStringAppendShort(ZyanString* destination,
 | |
|     const ZydisShortString* source)
 | |
| {
 | |
|     ZYAN_ASSERT(destination && source);
 | |
|     ZYAN_ASSERT(!destination->vector.allocator);
 | |
|     ZYAN_ASSERT(destination->vector.size && source->size);
 | |
| 
 | |
|     if (destination->vector.size + source->size > destination->vector.capacity)
 | |
|     {
 | |
|         return ZYAN_STATUS_INSUFFICIENT_BUFFER_SIZE;
 | |
|     }
 | |
| 
 | |
|     ZYAN_MEMCPY((char*)destination->vector.data + destination->vector.size - 1, source->data,
 | |
|         (ZyanUSize)source->size + 1);
 | |
| 
 | |
|     destination->vector.size += source->size;
 | |
|     ZYDIS_STRING_ASSERT_NULLTERMINATION(destination);
 | |
| 
 | |
|     return ZYAN_STATUS_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Appends the content of the source short-string to the end of the destination string,
 | |
|  * converting the characters to the specified letter-case.
 | |
|  *
 | |
|  * @param   destination The destination string.
 | |
|  * @param   source      The source string.
 | |
|  * @param   letter_case The desired letter-case.
 | |
|  *
 | |
|  * @return  A zyan status code.
 | |
|  */
 | |
| ZYAN_INLINE ZyanStatus ZydisStringAppendShortCase(ZyanString* destination,
 | |
|     const ZydisShortString* source, ZydisLetterCase letter_case)
 | |
| {
 | |
|     ZYAN_ASSERT(destination && source);
 | |
|     ZYAN_ASSERT(!destination->vector.allocator);
 | |
|     ZYAN_ASSERT(destination->vector.size && source->size);
 | |
| 
 | |
|     if (destination->vector.size + source->size > destination->vector.capacity)
 | |
|     {
 | |
|         return ZYAN_STATUS_INSUFFICIENT_BUFFER_SIZE;
 | |
|     }
 | |
| 
 | |
|     ZYAN_MEMCPY((char*)destination->vector.data + destination->vector.size - 1, source->data,
 | |
|         (ZyanUSize)source->size + 1);
 | |
| 
 | |
|     switch (letter_case)
 | |
|     {
 | |
|     case ZYDIS_LETTER_CASE_DEFAULT:
 | |
|         break;
 | |
|     case ZYDIS_LETTER_CASE_LOWER:
 | |
|     {
 | |
|         const ZyanUSize index = destination->vector.size - 1;
 | |
|         const ZyanUSize count = source->size;
 | |
|         char* s = (char*)destination->vector.data + index;
 | |
|         for (ZyanUSize i = index; i < index + count; ++i)
 | |
|         {
 | |
|             const char c = *s;
 | |
|             if ((c >= 'A') && (c <= 'Z'))
 | |
|             {
 | |
|                 *s = c | 32;
 | |
|             }
 | |
|             ++s;
 | |
|         }
 | |
|         break;
 | |
|     }
 | |
|     case ZYDIS_LETTER_CASE_UPPER:
 | |
|     {
 | |
|         const ZyanUSize index = destination->vector.size - 1;
 | |
|         const ZyanUSize count = source->size;
 | |
|         char* s = (char*)destination->vector.data + index;
 | |
|         for (ZyanUSize i = index; i < index + count; ++i)
 | |
|         {
 | |
|             const char c = *s;
 | |
|             if ((c >= 'a') && (c <= 'z'))
 | |
|             {
 | |
|                 *s = c & ~32;
 | |
|             }
 | |
|             ++s;
 | |
|         }
 | |
|         break;
 | |
|     }
 | |
|     default:
 | |
|         ZYAN_UNREACHABLE;
 | |
|     }
 | |
| 
 | |
|     destination->vector.size += source->size;
 | |
|     ZYDIS_STRING_ASSERT_NULLTERMINATION(destination);
 | |
| 
 | |
|     return ZYAN_STATUS_SUCCESS;
 | |
| }
 | |
| 
 | |
| /* ---------------------------------------------------------------------------------------------- */
 | |
| /* Formatting                                                                                     */
 | |
| /* ---------------------------------------------------------------------------------------------- */
 | |
| 
 | |
| /**
 | |
|  * Formats the given unsigned ordinal `value` to its decimal text-representation and
 | |
|  * appends it to the `string`.
 | |
|  *
 | |
|  * @param   string          A pointer to the `ZyanString` instance.
 | |
|  * @param   value           The value to append.
 | |
|  * @param   padding_length  Padds the converted value with leading zeros, if the number of chars is
 | |
|  *                          less than the `padding_length`.
 | |
|  * @param   prefix          The string to use as prefix or `ZYAN_NULL`, if not needed.
 | |
|  * @param   suffix          The string to use as suffix or `ZYAN_NULL`, if not needed.
 | |
|  *
 | |
|  * @return  A zyan status code.
 | |
|  *
 | |
|  * This function will fail, if the `ZYAN_STRING_IS_IMMUTABLE` flag is set for the specified
 | |
|  * `ZyanString` instance.
 | |
|  */
 | |
| ZyanStatus ZydisStringAppendDecU(ZyanString* string, ZyanU64 value, ZyanU8 padding_length,
 | |
|     const ZyanStringView* prefix, const ZyanStringView* suffix);
 | |
| 
 | |
| /**
 | |
|  * Formats the given signed ordinal `value` to its decimal text-representation and
 | |
|  * appends it to the `string`.
 | |
|  *
 | |
|  * @param   string          A pointer to the `ZyanString` instance.
 | |
|  * @param   value           The value to append.
 | |
|  * @param   padding_length  Padds the converted value with leading zeros, if the number of chars is
 | |
|  *                          less than the `padding_length`.
 | |
|  * @param   force_sign      Enable this option to print the `+` sign for positive numbers.
 | |
|  * @param   prefix          The string to use as prefix or `ZYAN_NULL`, if not needed.
 | |
|  * @param   suffix          The string to use as suffix or `ZYAN_NULL`, if not needed.
 | |
|  *
 | |
|  * @return  A zyan status code.
 | |
|  *
 | |
|  * This function will fail, if the `ZYAN_STRING_IS_IMMUTABLE` flag is set for the specified
 | |
|  * `ZyanString` instance.
 | |
|  */
 | |
| ZYAN_INLINE ZyanStatus ZydisStringAppendDecS(ZyanString* string, ZyanI64 value,
 | |
|     ZyanU8 padding_length, ZyanBool force_sign, const ZyanStringView* prefix,
 | |
|     const ZyanStringView* suffix)
 | |
| {
 | |
|     static const ZydisShortString str_add = ZYDIS_MAKE_SHORTSTRING("+");
 | |
|     static const ZydisShortString str_sub = ZYDIS_MAKE_SHORTSTRING("-");
 | |
| 
 | |
|     if (value < 0)
 | |
|     {
 | |
|         ZYAN_CHECK(ZydisStringAppendShort(string, &str_sub));
 | |
|         if (prefix)
 | |
|         {
 | |
|             ZYAN_CHECK(ZydisStringAppend(string, prefix));
 | |
|         }
 | |
|         return ZydisStringAppendDecU(string, ZyanAbsI64(value), padding_length,
 | |
|             (const ZyanStringView*)ZYAN_NULL, suffix);
 | |
|     }
 | |
| 
 | |
|     if (force_sign)
 | |
|     {
 | |
|         ZYAN_ASSERT(value >= 0);
 | |
|         ZYAN_CHECK(ZydisStringAppendShort(string, &str_add));
 | |
|     }
 | |
|     return ZydisStringAppendDecU(string, value, padding_length, prefix, suffix);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Formats the given unsigned ordinal `value` to its hexadecimal text-representation and
 | |
|  * appends it to the `string`.
 | |
|  *
 | |
|  * @param   string                  A pointer to the `ZyanString` instance.
 | |
|  * @param   value                   The value to append.
 | |
|  * @param   padding_length          Pads the converted value with leading zeros if the number of
 | |
|  *                                  chars is less than the `padding_length`.
 | |
|  * @param   force_leading_number    Enable this option to prepend a leading `0` if the first
 | |
|  *                                  character is non-numeric.
 | |
|  * @param   uppercase               Enable this option to use uppercase letters ('A'-'F') instead
 | |
|  *                                  of lowercase ones ('a'-'f').
 | |
|  * @param   prefix                  The string to use as prefix or `ZYAN_NULL`, if not needed.
 | |
|  * @param   suffix                  The string to use as suffix or `ZYAN_NULL`, if not needed.
 | |
|  *
 | |
|  * @return  A zyan status code.
 | |
|  *
 | |
|  * This function will fail, if the `ZYAN_STRING_IS_IMMUTABLE` flag is set for the specified
 | |
|  * `ZyanString` instance.
 | |
|  */
 | |
| ZyanStatus ZydisStringAppendHexU(ZyanString* string, ZyanU64 value, ZyanU8 padding_length,
 | |
|     ZyanBool force_leading_number, ZyanBool uppercase, const ZyanStringView* prefix,
 | |
|     const ZyanStringView* suffix);
 | |
| 
 | |
| /**
 | |
|  * Formats the given signed ordinal `value` to its hexadecimal text-representation and
 | |
|  * appends it to the `string`.
 | |
|  *
 | |
|  * @param   string                  A pointer to the `ZyanString` instance.
 | |
|  * @param   value                   The value to append.
 | |
|  * @param   padding_length          Padds the converted value with leading zeros, if the number of
 | |
|  *                                  chars is less than the `padding_length` (the sign char does not
 | |
|  *                                  count).
 | |
|  * @param   force_leading_number    Enable this option to prepend a leading `0`, if the first
 | |
|  *                                  character is non-numeric.
 | |
|  * @param   uppercase               Enable this option to use uppercase letters ('A'-'F') instead
 | |
|  *                                  of lowercase ones ('a'-'f').
 | |
|  * @param   force_sign              Enable this option to print the `+` sign for positive numbers.
 | |
|  * @param   prefix                  The string to use as prefix or `ZYAN_NULL`, if not needed.
 | |
|  * @param   suffix                  The string to use as suffix or `ZYAN_NULL`, if not needed.
 | |
|  *
 | |
|  * @return  A zyan status code.
 | |
|  *
 | |
|  * This function will fail if the `ZYAN_STRING_IS_IMMUTABLE` flag is set for the specified
 | |
|  * `ZyanString` instance.
 | |
|  */
 | |
| ZYAN_INLINE ZyanStatus ZydisStringAppendHexS(ZyanString* string, ZyanI64 value,
 | |
|     ZyanU8 padding_length, ZyanBool force_leading_number, ZyanBool uppercase, ZyanBool force_sign,
 | |
|     const ZyanStringView* prefix, const ZyanStringView* suffix)
 | |
| {
 | |
|     static const ZydisShortString str_add = ZYDIS_MAKE_SHORTSTRING("+");
 | |
|     static const ZydisShortString str_sub = ZYDIS_MAKE_SHORTSTRING("-");
 | |
| 
 | |
|     if (value < 0)
 | |
|     {
 | |
|         ZYAN_CHECK(ZydisStringAppendShort(string, &str_sub));
 | |
|         if (prefix)
 | |
|         {
 | |
|             ZYAN_CHECK(ZydisStringAppend(string, prefix));
 | |
|         }
 | |
|         return ZydisStringAppendHexU(string, ZyanAbsI64(value), padding_length,
 | |
|             force_leading_number, uppercase, (const ZyanStringView*)ZYAN_NULL, suffix);
 | |
|     }
 | |
| 
 | |
|     if (force_sign)
 | |
|     {
 | |
|         ZYAN_ASSERT(value >= 0);
 | |
|         ZYAN_CHECK(ZydisStringAppendShort(string, &str_add));
 | |
|     }
 | |
|     return ZydisStringAppendHexU(string, value, padding_length, force_leading_number, uppercase,
 | |
|         prefix, suffix);
 | |
| }
 | |
| 
 | |
| /* ---------------------------------------------------------------------------------------------- */
 | |
| 
 | |
| /* ============================================================================================== */
 | |
| 
 | |
| #ifdef __cplusplus
 | |
| }
 | |
| #endif
 | |
| 
 | |
| #endif // ZYDIS_INTERNAL_STRING_H
 | 
