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

Format.c (18002B)


      1 /***************************************************************************************************
      2 
      3   Zyan Core Library (Zycore-C)
      4 
      5   Original Author : Florian Bernd
      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 #include <Zycore/Format.h>
     28 #include <Zycore/LibC.h>
     29 
     30 /* ============================================================================================== */
     31 /* Constants                                                                                      */
     32 /* ============================================================================================== */
     33 
     34 /* ---------------------------------------------------------------------------------------------- */
     35 /* Defines                                                                                        */
     36 /* ---------------------------------------------------------------------------------------------- */
     37 
     38 #define ZYCORE_MAXCHARS_DEC_32 10
     39 #define ZYCORE_MAXCHARS_DEC_64 20
     40 #define ZYCORE_MAXCHARS_HEX_32  8
     41 #define ZYCORE_MAXCHARS_HEX_64 16
     42 
     43 /* ---------------------------------------------------------------------------------------------- */
     44 /* Lookup Tables                                                                                  */
     45 /* ---------------------------------------------------------------------------------------------- */
     46 
     47 static const char* const DECIMAL_LOOKUP =
     48     "00010203040506070809"
     49     "10111213141516171819"
     50     "20212223242526272829"
     51     "30313233343536373839"
     52     "40414243444546474849"
     53     "50515253545556575859"
     54     "60616263646566676869"
     55     "70717273747576777879"
     56     "80818283848586878889"
     57     "90919293949596979899";
     58 
     59 /* ---------------------------------------------------------------------------------------------- */
     60 /* Static strings                                                                                 */
     61 /* ---------------------------------------------------------------------------------------------- */
     62 
     63 static const ZyanStringView STR_ADD = ZYAN_DEFINE_STRING_VIEW("+");
     64 static const ZyanStringView STR_SUB = ZYAN_DEFINE_STRING_VIEW("-");
     65 
     66 /* ---------------------------------------------------------------------------------------------- */
     67 
     68 /* ============================================================================================== */
     69 /* Internal macros                                                                                */
     70 /* ============================================================================================== */
     71 
     72 /**
     73  * Writes a terminating '\0' character at the end of the string data.
     74  */
     75 #define ZYCORE_STRING_NULLTERMINATE(string) \
     76       *(char*)((ZyanU8*)(string)->vector.data + (string)->vector.size - 1) = '\0';
     77 
     78 /* ============================================================================================== */
     79 /* Internal functions                                                                             */
     80 /* ============================================================================================== */
     81 
     82 /* ---------------------------------------------------------------------------------------------- */
     83 /* Decimal                                                                                        */
     84 /* ---------------------------------------------------------------------------------------------- */
     85 
     86 #if defined(ZYAN_X86) || defined(ZYAN_ARM) || defined(ZYAN_EMSCRIPTEN) || defined(ZYAN_WASM) || defined(ZYAN_PPC)
     87 ZyanStatus ZyanStringAppendDecU32(ZyanString* string, ZyanU32 value, ZyanU8 padding_length)
     88 {
     89     if (!string)
     90     {
     91         return ZYAN_STATUS_INVALID_ARGUMENT;
     92     }
     93 
     94     char buffer[ZYCORE_MAXCHARS_DEC_32];
     95     char *buffer_end = &buffer[ZYCORE_MAXCHARS_DEC_32];
     96     char *buffer_write_pointer = buffer_end;
     97     while (value >= 100)
     98     {
     99         const ZyanU32 value_old = value;
    100         buffer_write_pointer -= 2;
    101         value /= 100;
    102         ZYAN_MEMCPY(buffer_write_pointer, &DECIMAL_LOOKUP[(value_old - (value * 100)) * 2], 2);
    103     }
    104     buffer_write_pointer -= 2;
    105     ZYAN_MEMCPY(buffer_write_pointer, &DECIMAL_LOOKUP[value * 2], 2);
    106 
    107     const ZyanUSize offset_odd    = (ZyanUSize)(value < 10);
    108     const ZyanUSize length_number = buffer_end - buffer_write_pointer - offset_odd;
    109     const ZyanUSize length_total  = ZYAN_MAX(length_number, padding_length);
    110     const ZyanUSize length_target = string->vector.size;
    111 
    112     if (string->vector.size + length_total > string->vector.capacity)
    113     {
    114         ZYAN_CHECK(ZyanStringResize(string, string->vector.size + length_total - 1));
    115     }
    116 
    117     ZyanUSize offset_write = 0;
    118     if (padding_length > length_number)
    119     {
    120         offset_write = padding_length - length_number;
    121         ZYAN_MEMSET((char*)string->vector.data + length_target - 1, '0', offset_write);
    122     }
    123 
    124     ZYAN_MEMCPY((char*)string->vector.data + length_target + offset_write - 1,
    125         buffer_write_pointer + offset_odd, length_number);
    126     string->vector.size = length_target + length_total;
    127     ZYCORE_STRING_NULLTERMINATE(string);
    128 
    129     return ZYAN_STATUS_SUCCESS;
    130 }
    131 #endif
    132 
    133 ZyanStatus ZyanStringAppendDecU64(ZyanString* string, ZyanU64 value, ZyanU8 padding_length)
    134 {
    135     if (!string)
    136     {
    137         return ZYAN_STATUS_INVALID_ARGUMENT;
    138     }
    139 
    140     char buffer[ZYCORE_MAXCHARS_DEC_64];
    141     char *buffer_end = &buffer[ZYCORE_MAXCHARS_DEC_64];
    142     char *buffer_write_pointer = buffer_end;
    143     while (value >= 100)
    144     {
    145         const ZyanU64 value_old = value;
    146         buffer_write_pointer -= 2;
    147         value /= 100;
    148         ZYAN_MEMCPY(buffer_write_pointer, &DECIMAL_LOOKUP[(value_old - (value * 100)) * 2], 2);
    149     }
    150     buffer_write_pointer -= 2;
    151     ZYAN_MEMCPY(buffer_write_pointer, &DECIMAL_LOOKUP[value * 2], 2);
    152 
    153     const ZyanUSize offset_odd    = (ZyanUSize)(value < 10);
    154     const ZyanUSize length_number = buffer_end - buffer_write_pointer - offset_odd;
    155     const ZyanUSize length_total  = ZYAN_MAX(length_number, padding_length);
    156     const ZyanUSize length_target = string->vector.size;
    157 
    158     if (string->vector.size + length_total > string->vector.capacity)
    159     {
    160         ZYAN_CHECK(ZyanStringResize(string, string->vector.size + length_total - 1));
    161     }
    162 
    163     ZyanUSize offset_write = 0;
    164     if (padding_length > length_number)
    165     {
    166         offset_write = padding_length - length_number;
    167         ZYAN_MEMSET((char*)string->vector.data + length_target - 1, '0', offset_write);
    168     }
    169 
    170     ZYAN_MEMCPY((char*)string->vector.data + length_target + offset_write - 1,
    171         buffer_write_pointer + offset_odd, length_number);
    172     string->vector.size = length_target + length_total;
    173     ZYCORE_STRING_NULLTERMINATE(string);
    174 
    175     return ZYAN_STATUS_SUCCESS;
    176 }
    177 
    178 /* ---------------------------------------------------------------------------------------------- */
    179 /* Hexadecimal                                                                                    */
    180 /* ---------------------------------------------------------------------------------------------- */
    181 
    182 #if defined(ZYAN_X86) || defined(ZYAN_ARM) || defined(ZYAN_EMSCRIPTEN) || defined(ZYAN_WASM) || defined(ZYAN_PPC)
    183 ZyanStatus ZyanStringAppendHexU32(ZyanString* string, ZyanU32 value, ZyanU8 padding_length,
    184     ZyanBool uppercase)
    185 {
    186     if (!string)
    187     {
    188         return ZYAN_STATUS_INVALID_ARGUMENT;
    189     }
    190 
    191     const ZyanUSize len = string->vector.size;
    192     ZyanUSize remaining = string->vector.capacity - string->vector.size;
    193 
    194     if (remaining < (ZyanUSize)padding_length)
    195     {
    196         ZYAN_CHECK(ZyanStringResize(string, len + padding_length - 1));
    197         remaining = padding_length;
    198     }
    199 
    200     if (!value)
    201     {
    202         const ZyanU8 n = (padding_length ? padding_length : 1);
    203 
    204         if (remaining < (ZyanUSize)n)
    205         {
    206             ZYAN_CHECK(ZyanStringResize(string, string->vector.size + n - 1));
    207         }
    208 
    209         ZYAN_MEMSET((char*)string->vector.data + len - 1, '0', n);
    210         string->vector.size = len + n;
    211         ZYCORE_STRING_NULLTERMINATE(string);
    212 
    213         return ZYAN_STATUS_SUCCESS;
    214     }
    215 
    216     ZyanU8 n = 0;
    217     char* buffer = ZYAN_NULL;
    218     for (ZyanI8 i = ZYCORE_MAXCHARS_HEX_32 - 1; i >= 0; --i)
    219     {
    220         const ZyanU8 v = (value >> i * 4) & 0x0F;
    221         if (!n)
    222         {
    223             if (!v)
    224             {
    225                 continue;
    226             }
    227             if (remaining <= (ZyanU8)i)
    228             {
    229                 ZYAN_CHECK(ZyanStringResize(string, string->vector.size + i));
    230             }
    231             buffer = (char*)string->vector.data + len - 1;
    232             if (padding_length > i)
    233             {
    234                 n = padding_length - i - 1;
    235                 ZYAN_MEMSET(buffer, '0', n);
    236             }
    237         }
    238         ZYAN_ASSERT(buffer);
    239         if (uppercase)
    240         {
    241             buffer[n++] = "0123456789ABCDEF"[v];
    242         } else
    243         {
    244             buffer[n++] = "0123456789abcdef"[v];
    245         }
    246     }
    247     string->vector.size = len + n;
    248     ZYCORE_STRING_NULLTERMINATE(string);
    249 
    250     return ZYAN_STATUS_SUCCESS;
    251 }
    252 #endif
    253 
    254 ZyanStatus ZyanStringAppendHexU64(ZyanString* string, ZyanU64 value, ZyanU8 padding_length,
    255     ZyanBool uppercase)
    256 {
    257     if (!string)
    258     {
    259         return ZYAN_STATUS_INVALID_ARGUMENT;
    260     }
    261 
    262     const ZyanUSize len = string->vector.size;
    263     ZyanUSize remaining = string->vector.capacity - string->vector.size;
    264 
    265     if (remaining < (ZyanUSize)padding_length)
    266     {
    267         ZYAN_CHECK(ZyanStringResize(string, len + padding_length - 1));
    268         remaining = padding_length;
    269     }
    270 
    271     if (!value)
    272     {
    273         const ZyanU8 n = (padding_length ? padding_length : 1);
    274 
    275         if (remaining < (ZyanUSize)n)
    276         {
    277             ZYAN_CHECK(ZyanStringResize(string, string->vector.size + n - 1));
    278         }
    279 
    280         ZYAN_MEMSET((char*)string->vector.data + len - 1, '0', n);
    281         string->vector.size = len + n;
    282         ZYCORE_STRING_NULLTERMINATE(string);
    283 
    284         return ZYAN_STATUS_SUCCESS;
    285     }
    286 
    287     ZyanU8 n = 0;
    288     char* buffer = ZYAN_NULL;
    289     for (ZyanI8 i = ((value & 0xFFFFFFFF00000000) ?
    290         ZYCORE_MAXCHARS_HEX_64 : ZYCORE_MAXCHARS_HEX_32) - 1; i >= 0; --i)
    291     {
    292         const ZyanU8 v = (value >> i * 4) & 0x0F;
    293         if (!n)
    294         {
    295             if (!v)
    296             {
    297                 continue;
    298             }
    299             if (remaining <= (ZyanU8)i)
    300             {
    301                 ZYAN_CHECK(ZyanStringResize(string, string->vector.size + i));
    302             }
    303             buffer = (char*)string->vector.data + len - 1;
    304             if (padding_length > i)
    305             {
    306                 n = padding_length - i - 1;
    307                 ZYAN_MEMSET(buffer, '0', n);
    308             }
    309         }
    310         ZYAN_ASSERT(buffer);
    311         if (uppercase)
    312         {
    313             buffer[n++] = "0123456789ABCDEF"[v];
    314         } else
    315         {
    316             buffer[n++] = "0123456789abcdef"[v];
    317         }
    318     }
    319     string->vector.size = len + n;
    320     ZYCORE_STRING_NULLTERMINATE(string);
    321 
    322     return ZYAN_STATUS_SUCCESS;
    323 }
    324 
    325 /* ---------------------------------------------------------------------------------------------- */
    326 
    327 /* ============================================================================================== */
    328 /* Exported functions                                                                             */
    329 /* ============================================================================================== */
    330 
    331 /* ---------------------------------------------------------------------------------------------- */
    332 /* Insertion                                                                                      */
    333 /* ---------------------------------------------------------------------------------------------- */
    334 
    335 //ZyanStatus ZyanStringInsertFormat(ZyanString* string, ZyanUSize index, const char* format, ...)
    336 //{
    337 //
    338 //}
    339 //
    340 ///* ---------------------------------------------------------------------------------------------- */
    341 //
    342 //ZyanStatus ZyanStringInsertDecU(ZyanString* string, ZyanUSize index, ZyanU64 value,
    343 //    ZyanUSize padding_length)
    344 //{
    345 //
    346 //}
    347 //
    348 //ZyanStatus ZyanStringInsertDecS(ZyanString* string, ZyanUSize index, ZyanI64 value,
    349 //    ZyanUSize padding_length, ZyanBool force_sign, const ZyanString* prefix)
    350 //{
    351 //
    352 //}
    353 //
    354 //ZyanStatus ZyanStringInsertHexU(ZyanString* string, ZyanUSize index, ZyanU64 value,
    355 //    ZyanUSize padding_length, ZyanBool uppercase)
    356 //{
    357 //
    358 //}
    359 //
    360 //ZyanStatus ZyanStringInsertHexS(ZyanString* string, ZyanUSize index, ZyanI64 value,
    361 //    ZyanUSize padding_length, ZyanBool uppercase, ZyanBool force_sign, const ZyanString* prefix)
    362 //{
    363 //
    364 //}
    365 
    366 /* ---------------------------------------------------------------------------------------------- */
    367 /* Appending                                                                                      */
    368 /* ---------------------------------------------------------------------------------------------- */
    369 
    370 #ifndef ZYAN_NO_LIBC
    371 
    372 ZyanStatus ZyanStringAppendFormat(ZyanString* string, const char* format, ...)
    373 {
    374     if (!string || !format)
    375     {
    376         return ZYAN_STATUS_INVALID_ARGUMENT;
    377     }
    378 
    379     ZyanVAList arglist;
    380     ZYAN_VA_START(arglist, format);
    381 
    382     const ZyanUSize len = string->vector.size;
    383 
    384     ZyanI32 w = ZYAN_VSNPRINTF((char*)string->vector.data + len - 1,
    385         string->vector.capacity - len + 1, format, arglist);
    386     if (w < 0)
    387     {
    388         ZYAN_VA_END(arglist);
    389         return ZYAN_STATUS_FAILED;
    390     }
    391     if (w <= (ZyanI32)(string->vector.capacity - len))
    392     {
    393         string->vector.size = len + w;
    394 
    395         ZYAN_VA_END(arglist);
    396         return ZYAN_STATUS_SUCCESS;
    397     }
    398 
    399     // The remaining capacity was not sufficent to fit the formatted string. Trying to resize ..
    400     const ZyanStatus status = ZyanStringResize(string, string->vector.size + w - 1);
    401     if (!ZYAN_SUCCESS(status))
    402     {
    403         ZYAN_VA_END(arglist);
    404         return status;
    405     }
    406 
    407     w = ZYAN_VSNPRINTF((char*)string->vector.data + len - 1,
    408         string->vector.capacity - string->vector.size + 1, format, arglist);
    409     if (w < 0)
    410     {
    411         ZYAN_VA_END(arglist);
    412         return ZYAN_STATUS_FAILED;
    413     }
    414     ZYAN_ASSERT(w <= (ZyanI32)(string->vector.capacity - string->vector.size));
    415 
    416     ZYAN_VA_END(arglist);
    417     return ZYAN_STATUS_SUCCESS;
    418 }
    419 
    420 #endif // ZYAN_NO_LIBC
    421 
    422 /* ---------------------------------------------------------------------------------------------- */
    423 
    424 ZyanStatus ZyanStringAppendDecU(ZyanString* string, ZyanU64 value, ZyanU8 padding_length)
    425 {
    426 #if defined(ZYAN_X64) || defined(ZYAN_AARCH64) || defined(ZYAN_PPC64) || defined(ZYAN_RISCV64)
    427     return ZyanStringAppendDecU64(string, value, padding_length);
    428 #else
    429     // Working with 64-bit values is slow on non 64-bit systems
    430     if (value & 0xFFFFFFFF00000000)
    431     {
    432         return ZyanStringAppendDecU64(string, value, padding_length);
    433     }
    434     return ZyanStringAppendDecU32(string, (ZyanU32)value, padding_length);
    435 #endif
    436 }
    437 
    438 ZyanStatus ZyanStringAppendDecS(ZyanString* string, ZyanI64 value, ZyanU8 padding_length,
    439     ZyanBool force_sign, const ZyanStringView* prefix)
    440 {
    441     if (value < 0)
    442     {
    443         ZYAN_CHECK(ZyanStringAppend(string, &STR_SUB));
    444         if (prefix)
    445         {
    446             ZYAN_CHECK(ZyanStringAppend(string, prefix));
    447         }
    448         return ZyanStringAppendDecU(string, ZyanAbsI64(value), padding_length);
    449     }
    450 
    451     if (force_sign)
    452     {
    453         ZYAN_ASSERT(value >= 0);
    454         ZYAN_CHECK(ZyanStringAppend(string, &STR_ADD));
    455     }
    456 
    457     if (prefix)
    458     {
    459         ZYAN_CHECK(ZyanStringAppend(string, prefix));
    460     }
    461     return ZyanStringAppendDecU(string, value, padding_length);
    462 }
    463 
    464 ZyanStatus ZyanStringAppendHexU(ZyanString* string, ZyanU64 value, ZyanU8 padding_length,
    465     ZyanBool uppercase)
    466 {
    467 #if defined(ZYAN_X64) || defined(ZYAN_AARCH64) || defined(ZYAN_PPC64) || defined(ZYAN_RISCV64)
    468     return ZyanStringAppendHexU64(string, value, padding_length, uppercase);
    469 #else
    470     // Working with 64-bit values is slow on non 64-bit systems
    471     if (value & 0xFFFFFFFF00000000)
    472     {
    473         return ZyanStringAppendHexU64(string, value, padding_length, uppercase);
    474     }
    475     return ZyanStringAppendHexU32(string, (ZyanU32)value, padding_length, uppercase);
    476 #endif
    477 }
    478 
    479 ZyanStatus ZyanStringAppendHexS(ZyanString* string, ZyanI64 value, ZyanU8 padding_length,
    480     ZyanBool uppercase, ZyanBool force_sign, const ZyanStringView* prefix)
    481 {
    482     if (value < 0)
    483     {
    484         ZYAN_CHECK(ZyanStringAppend(string, &STR_SUB));
    485         if (prefix)
    486         {
    487             ZYAN_CHECK(ZyanStringAppend(string, prefix));
    488         }
    489         return ZyanStringAppendHexU(string, ZyanAbsI64(value), padding_length, uppercase);
    490     }
    491 
    492     if (force_sign)
    493     {
    494         ZYAN_ASSERT(value >= 0);
    495         ZYAN_CHECK(ZyanStringAppend(string, &STR_ADD));
    496     }
    497 
    498     if (prefix)
    499     {
    500         ZYAN_CHECK(ZyanStringAppend(string, prefix));
    501     }
    502     return ZyanStringAppendHexU(string, value, padding_length, uppercase);
    503 }
    504 
    505 /* ---------------------------------------------------------------------------------------------- */
    506 
    507 /* ============================================================================================== */