duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

String.h (18215B)


      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