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) || defined(ZYAN_LOONGARCH) 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) || defined(ZYAN_LOONGARCH) 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 /* ============================================================================================== */ 508