util.c (18960B)
1 /* See LICENSE for license details. */ 2 #if COMPILER_CLANG 3 #pragma GCC diagnostic ignored "-Winitializer-overrides" 4 #elif COMPILER_GCC 5 #pragma GCC diagnostic ignored "-Woverride-init" 6 #endif 7 8 #define zero_struct(s) mem_clear(s, 0, sizeof(*s)) 9 function void * 10 mem_clear(void *restrict p_, u8 c, iz size) 11 { 12 u8 *p = p_; 13 while (size > 0) p[--size] = c; 14 return p; 15 } 16 17 function b32 18 memory_equal(void *restrict left, void *restrict right, uz n) 19 { 20 u8 *a = left, *b = right; 21 b32 result = 1; 22 for (; result && n; n--) 23 result &= *a++ == *b++; 24 return result; 25 } 26 27 function void 28 mem_copy(void *restrict dest, void *restrict src, uz n) 29 { 30 u8 *s = src, *d = dest; 31 #ifdef __AVX512BW__ 32 { 33 for (; n >= 64; n -= 64, s += 64, d += 64) 34 _mm512_storeu_epi8(d, _mm512_loadu_epi8(s)); 35 __mmask64 k = _cvtu64_mask64(_bzhi_u64(-1ULL, n)); 36 _mm512_mask_storeu_epi8(d, k, _mm512_maskz_loadu_epi8(k, s)); 37 } 38 #else 39 for (; n; n--) *d++ = *s++; 40 #endif 41 } 42 43 /* IMPORTANT: this function may fault if dest, src, and n are not multiples of 64 */ 44 function void 45 memory_copy_non_temporal(void *restrict dest, void *restrict src, uz n) 46 { 47 assume(((u64)dest & 63) == 0); 48 assume(((u64)src & 63) == 0); 49 assume(((u64)n & 63) == 0); 50 u8 *s = src, *d = dest; 51 52 #if defined(__AVX512BW__) 53 { 54 for (; n >= 64; n -= 64, s += 64, d += 64) 55 _mm512_stream_si512((__m512i *)d, _mm512_stream_load_si512((__m512i *)s)); 56 } 57 #elif defined(__AVX2__) 58 { 59 for (; n >= 32; n -= 32, s += 32, d += 32) 60 _mm256_stream_si256((__m256i *)d, _mm256_stream_load_si256((__m256i *)s)); 61 } 62 #elif ARCH_ARM64 && !COMPILER_MSVC 63 { 64 asm volatile ( 65 "cbz %2, 2f\n" 66 "1: ldnp q0, q1, [%1]\n" 67 "subs %2, %2, #32\n" 68 "add %1, %1, #32\n" 69 "stnp q0, q1, [%0]\n" 70 "add %0, %0, #32\n" 71 "b.ne 1b\n" 72 "2:" 73 : "+r"(d), "+r"(s), "+r"(n) 74 :: "memory", "v0", "v1" 75 ); 76 } 77 #else 78 mem_copy(d, s, n); 79 #endif 80 } 81 82 function void 83 mem_move(u8 *dest, u8 *src, uz n) 84 { 85 if (dest < src) mem_copy(dest, src, n); 86 else while (n) { n--; dest[n] = src[n]; } 87 } 88 89 function void * 90 memory_scan_backwards(void *memory, u8 byte, iz n) 91 { 92 void *result = 0; 93 u8 *s = memory; 94 while (n > 0) if (s[--n] == byte) { result = s + n; break; } 95 return result; 96 } 97 98 function Arena 99 arena_from_memory(void *memory, u64 size) 100 { 101 Arena result; 102 result.beg = memory; 103 result.end = result.beg + size; 104 return result; 105 } 106 107 function void * 108 align_pointer_up(void *p, uz alignment) 109 { 110 uz padding = -(u64)p & (alignment - 1); 111 void *result = (u8 *)p + padding; 112 return result; 113 } 114 115 function void * 116 arena_aligned_start(Arena a, uz alignment) 117 { 118 return align_pointer_up(a.beg, alignment); 119 } 120 121 #define arena_capacity(a, t) arena_capacity_(a, sizeof(t), alignof(t)) 122 function iz 123 arena_capacity_(Arena *a, iz size, uz alignment) 124 { 125 iz available = a->end - (u8 *)arena_aligned_start(*a, alignment); 126 iz result = available / size; 127 return result; 128 } 129 130 function u8 * 131 arena_commit(Arena *a, iz size) 132 { 133 assert(a->end - a->beg >= size); 134 u8 *result = a->beg; 135 a->beg += size; 136 return result; 137 } 138 139 function void 140 arena_pop(Arena *a, iz length) 141 { 142 a->beg -= length; 143 } 144 145 typedef enum { 146 ArenaAllocateFlags_NoZero = 1 << 0, 147 } ArenaAllocateFlags; 148 149 typedef struct { 150 iz size; 151 uz align; 152 iz count; 153 ArenaAllocateFlags flags; 154 } ArenaAllocateInfo; 155 156 #define arena_alloc(a, ...) arena_alloc_(a, (ArenaAllocateInfo){.align = 8, .count = 1, ##__VA_ARGS__}) 157 #define push_array(a, t, n) (t *)arena_alloc(a, .size = sizeof(t), .align = alignof(t), .count = n) 158 #define push_array_no_zero(a, t, n) (t *)arena_alloc(a, .size = sizeof(t), .align = alignof(t), .count = n, .flags = ArenaAllocateFlags_NoZero) 159 #define push_struct(a, t) push_array(a, t, 1) 160 #define push_struct_no_zero(a, t) push_array_no_zero(a, t, 1) 161 162 function void * 163 arena_alloc_(Arena *a, ArenaAllocateInfo info) 164 { 165 void *result = 0; 166 if (a->beg) { 167 u8 *start = arena_aligned_start(*a, info.align); 168 iz available = a->end - start; 169 assert((available >= 0 && info.count <= available / info.size)); 170 asan_unpoison_region(start, info.count * info.size); 171 a->beg = start + info.count * info.size; 172 result = start; 173 if ((info.flags & ArenaAllocateFlags_NoZero) == 0) 174 result = mem_clear(start, 0, info.count * info.size); 175 } 176 return result; 177 } 178 179 function Arena 180 sub_arena(Arena *a, iz size, uz align) 181 { 182 Arena result = {.beg = arena_alloc(a, .size = size, .align = align, .flags = ArenaAllocateFlags_NoZero)}; 183 result.end = result.beg + size; 184 return result; 185 } 186 187 function Arena 188 sub_arena_end(Arena *a, iz len, uz align) 189 { 190 Arena result; 191 result.beg = (u8 *)((u64)(a->end - len) & ~(align - 1)), 192 result.end = a->end, 193 194 a->end = result.beg; 195 assert(a->end >= a->beg); 196 197 return result; 198 } 199 200 function TempArena 201 begin_temp_arena(Arena *a) 202 { 203 TempArena result = {.arena = a, .original_arena = *a}; 204 return result; 205 } 206 207 function void 208 end_temp_arena(TempArena ta) 209 { 210 Arena *a = ta.arena; 211 if (a) { 212 assert(a->beg >= ta.original_arena.beg); 213 *a = ta.original_arena; 214 } 215 } 216 217 218 enum { DA_INITIAL_CAP = 16 }; 219 220 #define da_index(it, s) ((it) - (s)->data) 221 #define da_reserve(a, s, n) \ 222 (s)->data = da_reserve_((a), (s)->data, &(s)->capacity, (s)->count + n, \ 223 _Alignof(typeof(*(s)->data)), sizeof(*(s)->data)) 224 225 #define da_append_count(a, s, items, item_count) do { \ 226 da_reserve((a), (s), (item_count)); \ 227 mem_copy((s)->data + (s)->count, (items), sizeof(*(items)) * (uz)(item_count)); \ 228 (s)->count += (item_count); \ 229 } while (0) 230 231 #define da_push(a, s) \ 232 ((s)->count == (s)->capacity \ 233 ? da_reserve(a, s, 1), \ 234 (s)->data + (s)->count++ \ 235 : (s)->data + (s)->count++) 236 237 function void * 238 da_reserve_(Arena *a, void *data, iz *capacity, iz needed, uz align, iz size) 239 { 240 iz cap = *capacity; 241 242 /* NOTE(rnp): handle both 0 initialized DAs and DAs that need to be moved (they started 243 * on the stack or someone allocated something in the middle of the arena during usage) */ 244 if (!data || a->beg != (u8 *)data + cap * size) { 245 void *copy = arena_alloc(a, .size = size, .align = align, .count = cap); 246 if (data) mem_copy(copy, data, (uz)(cap * size)); 247 data = copy; 248 } 249 250 if (!cap) cap = DA_INITIAL_CAP; 251 while (cap < needed) cap *= 2; 252 arena_alloc(a, .size = size, .align = align, .count = cap - *capacity); 253 *capacity = cap; 254 return data; 255 } 256 257 function u32 258 utf8_encode(u8 *out, u32 cp) 259 { 260 u32 result = 1; 261 if (cp <= 0x7F) { 262 out[0] = cp & 0x7F; 263 } else if (cp <= 0x7FF) { 264 result = 2; 265 out[0] = ((cp >> 6) & 0x1F) | 0xC0; 266 out[1] = ((cp >> 0) & 0x3F) | 0x80; 267 } else if (cp <= 0xFFFF) { 268 result = 3; 269 out[0] = ((cp >> 12) & 0x0F) | 0xE0; 270 out[1] = ((cp >> 6) & 0x3F) | 0x80; 271 out[2] = ((cp >> 0) & 0x3F) | 0x80; 272 } else if (cp <= 0x10FFFF) { 273 result = 4; 274 out[0] = ((cp >> 18) & 0x07) | 0xF0; 275 out[1] = ((cp >> 12) & 0x3F) | 0x80; 276 out[2] = ((cp >> 6) & 0x3F) | 0x80; 277 out[3] = ((cp >> 0) & 0x3F) | 0x80; 278 } else { 279 out[0] = '?'; 280 } 281 return result; 282 } 283 284 function UnicodeDecode 285 utf16_decode(u16 *data, iz length) 286 { 287 UnicodeDecode result = {.cp = U32_MAX}; 288 if (length) { 289 result.consumed = 1; 290 result.cp = data[0]; 291 if (length > 1 && BETWEEN(data[0], 0xD800u, 0xDBFFu) 292 && BETWEEN(data[1], 0xDC00u, 0xDFFFu)) 293 { 294 result.consumed = 2; 295 result.cp = ((data[0] - 0xD800u) << 10u) | ((data[1] - 0xDC00u) + 0x10000u); 296 } 297 } 298 return result; 299 } 300 301 function u32 302 utf16_encode(u16 *out, u32 cp) 303 { 304 u32 result = 1; 305 if (cp == U32_MAX) { 306 out[0] = '?'; 307 } else if (cp < 0x10000u) { 308 out[0] = (u16)cp; 309 } else { 310 u32 value = cp - 0x10000u; 311 out[0] = (u16)(0xD800u + (value >> 10u)); 312 out[1] = (u16)(0xDC00u + (value & 0x3FFu)); 313 result = 2; 314 } 315 return result; 316 } 317 318 function Stream 319 stream_from_buffer(u8 *buffer, u32 capacity) 320 { 321 Stream result = {.data = buffer, .cap = (i32)capacity}; 322 return result; 323 } 324 325 function Stream 326 stream_alloc(Arena *a, i32 cap) 327 { 328 Stream result = stream_from_buffer(arena_commit(a, cap), (u32)cap); 329 return result; 330 } 331 332 function s8 333 stream_to_s8(Stream *s) 334 { 335 s8 result = s8(""); 336 if (!s->errors) result = (s8){.len = s->widx, .data = s->data}; 337 return result; 338 } 339 340 function void 341 stream_reset(Stream *s, i32 index) 342 { 343 s->errors = s->cap <= index; 344 if (!s->errors) 345 s->widx = index; 346 } 347 348 function void 349 stream_commit(Stream *s, i32 count) 350 { 351 s->errors |= !BETWEEN(s->widx + count, 0, s->cap); 352 if (!s->errors) 353 s->widx += count; 354 } 355 356 function void 357 stream_append(Stream *s, void *data, iz count) 358 { 359 s->errors |= (s->cap - s->widx) < count; 360 if (!s->errors) { 361 mem_copy(s->data + s->widx, data, (uz)count); 362 s->widx += (i32)count; 363 } 364 } 365 366 function void 367 stream_append_byte(Stream *s, u8 b) 368 { 369 stream_append(s, &b, 1); 370 } 371 372 function void 373 stream_pad(Stream *s, u8 b, i32 n) 374 { 375 while (n > 0) stream_append_byte(s, b), n--; 376 } 377 378 function void 379 stream_append_s8(Stream *s, s8 str) 380 { 381 stream_append(s, str.data, str.len); 382 } 383 384 #define stream_append_s8s(s, ...) stream_append_s8s_(s, arg_list(s8, ##__VA_ARGS__)) 385 function void 386 stream_append_s8s_(Stream *s, s8 *strs, iz count) 387 { 388 for (iz i = 0; i < count; i++) 389 stream_append(s, strs[i].data, strs[i].len); 390 } 391 392 function void 393 stream_append_u64_width(Stream *s, u64 n, u64 min_width) 394 { 395 u8 tmp[64]; 396 u8 *end = tmp + sizeof(tmp); 397 u8 *beg = end; 398 min_width = MIN(sizeof(tmp), min_width); 399 400 do { *--beg = (u8)('0' + (n % 10)); } while (n /= 10); 401 while (end - beg > 0 && (uz)(end - beg) < min_width) 402 *--beg = '0'; 403 404 stream_append(s, beg, end - beg); 405 } 406 407 function void 408 stream_append_u64(Stream *s, u64 n) 409 { 410 stream_append_u64_width(s, n, 0); 411 } 412 413 function void 414 stream_append_hex_u64_width(Stream *s, u64 n, iz width) 415 { 416 assert(width <= 16); 417 if (!s->errors) { 418 u8 buf[16]; 419 u8 *end = buf + sizeof(buf); 420 u8 *beg = end; 421 while (n) { 422 *--beg = (u8)"0123456789abcdef"[n & 0x0F]; 423 n >>= 4; 424 } 425 while (end - beg < width) 426 *--beg = '0'; 427 stream_append(s, beg, end - beg); 428 } 429 } 430 431 function void 432 stream_append_hex_u64(Stream *s, u64 n) 433 { 434 stream_append_hex_u64_width(s, n, 2); 435 } 436 437 function void 438 stream_append_i64(Stream *s, i64 n) 439 { 440 if (n < 0) { 441 stream_append_byte(s, '-'); 442 n *= -1; 443 } 444 stream_append_u64(s, (u64)n); 445 } 446 447 function void 448 stream_append_f64(Stream *s, f64 f, u64 prec) 449 { 450 if (f < 0) { 451 stream_append_byte(s, '-'); 452 f *= -1; 453 } 454 455 /* NOTE: round last digit */ 456 f += 0.5f / (f64)prec; 457 458 if (f >= (f64)(-1UL >> 1)) { 459 stream_append_s8(s, s8("inf")); 460 } else { 461 u64 integral = (u64)f; 462 u64 fraction = (u64)((f - (f64)integral) * (f64)prec); 463 stream_append_u64(s, integral); 464 stream_append_byte(s, '.'); 465 for (u64 i = prec / 10; i > 1; i /= 10) { 466 if (i > fraction) 467 stream_append_byte(s, '0'); 468 } 469 stream_append_u64(s, fraction); 470 } 471 } 472 473 function void 474 stream_append_f64_e(Stream *s, f64 f) 475 { 476 /* TODO: there should be a better way of doing this */ 477 #if 0 478 /* NOTE: we ignore subnormal numbers for now */ 479 union { f64 f; u64 u; } u = {.f = f}; 480 i32 exponent = ((u.u >> 52) & 0x7ff) - 1023; 481 f32 log_10_of_2 = 0.301f; 482 i32 scale = (exponent * log_10_of_2); 483 /* NOTE: normalize f */ 484 for (i32 i = ABS(scale); i > 0; i--) 485 f *= (scale > 0)? 0.1f : 10.0f; 486 #else 487 i32 scale = 0; 488 if (f != 0) { 489 while (f > 1) { 490 f *= 0.1f; 491 scale++; 492 } 493 while (f < 1) { 494 f *= 10.0f; 495 scale--; 496 } 497 } 498 #endif 499 500 u32 prec = 100; 501 stream_append_f64(s, f, prec); 502 stream_append_byte(s, 'e'); 503 stream_append_byte(s, scale >= 0? '+' : '-'); 504 for (u32 i = prec / 10; i > 1; i /= 10) 505 stream_append_byte(s, '0'); 506 stream_append_u64(s, (u64)Abs(scale)); 507 } 508 509 function void 510 stream_append_v2(Stream *s, v2 v) 511 { 512 stream_append_byte(s, '{'); 513 stream_append_f64(s, v.x, 100); 514 stream_append_s8(s, s8(", ")); 515 stream_append_f64(s, v.y, 100); 516 stream_append_byte(s, '}'); 517 } 518 519 function Stream 520 arena_stream(Arena a) 521 { 522 Stream result = {0}; 523 result.data = a.beg; 524 result.cap = (i32)(a.end - a.beg); 525 526 /* TODO(rnp): no idea what to do here if we want to maintain the ergonomics */ 527 asan_unpoison_region(result.data, result.cap); 528 529 return result; 530 } 531 532 function s8 533 arena_stream_commit(Arena *a, Stream *s) 534 { 535 ASSERT(s->data == a->beg); 536 s8 result = stream_to_s8(s); 537 arena_commit(a, result.len); 538 return result; 539 } 540 541 function s8 542 arena_stream_commit_zero(Arena *a, Stream *s) 543 { 544 b32 error = s->errors || s->widx == s->cap; 545 if (!error) 546 s->data[s->widx] = 0; 547 s8 result = stream_to_s8(s); 548 arena_commit(a, result.len + 1); 549 return result; 550 } 551 552 function s8 553 arena_stream_commit_and_reset(Arena *arena, Stream *s) 554 { 555 s8 result = arena_stream_commit_zero(arena, s); 556 *s = arena_stream(*arena); 557 return result; 558 } 559 560 #if !defined(XXH_IMPLEMENTATION) 561 # define XXH_INLINE_ALL 562 # define XXH_IMPLEMENTATION 563 # define XXH_STATIC_LINKING_ONLY 564 # include "external/xxhash.h" 565 #endif 566 567 function u128 568 u128_hash_from_data(void *data, uz size) 569 { 570 u128 result = {0}; 571 XXH128_hash_t hash = XXH3_128bits_withSeed(data, size, 4969); 572 mem_copy(&result, &hash, sizeof(result)); 573 return result; 574 } 575 576 function u64 577 u64_hash_from_s8(s8 v) 578 { 579 u64 result = XXH3_64bits_withSeed(v.data, (uz)v.len, 4969); 580 return result; 581 } 582 583 function s8 584 c_str_to_s8(char *cstr) 585 { 586 s8 result = {.data = (u8 *)cstr}; 587 if (cstr) { while (*cstr) { result.len++; cstr++; } } 588 return result; 589 } 590 591 function b32 592 s8_equal(s8 a, s8 b) 593 { 594 b32 result = a.len == b.len; 595 for (iz i = 0; result && i < a.len; i++) 596 result = a.data[i] == b.data[i]; 597 return result; 598 } 599 600 /* NOTE(rnp): returns < 0 if byte is not found */ 601 function iz 602 s8_scan_backwards(s8 s, u8 byte) 603 { 604 iz result = (u8 *)memory_scan_backwards(s.data, byte, s.len) - s.data; 605 return result; 606 } 607 608 function s8 609 s8_cut_head(s8 s, iz cut) 610 { 611 s8 result = s; 612 if (cut > 0) { 613 result.data += cut; 614 result.len -= cut; 615 } 616 return result; 617 } 618 619 function s8 620 s8_alloc(Arena *a, iz len) 621 { 622 s8 result = {.data = push_array(a, u8, len), .len = len}; 623 return result; 624 } 625 626 function s8 627 s16_to_s8(Arena *a, s16 in) 628 { 629 s8 result = s8(""); 630 if (in.len) { 631 iz commit = in.len * 4; 632 iz length = 0; 633 u8 *data = arena_commit(a, commit + 1); 634 u16 *beg = in.data; 635 u16 *end = in.data + in.len; 636 while (beg < end) { 637 UnicodeDecode decode = utf16_decode(beg, end - beg); 638 length += utf8_encode(data + length, decode.cp); 639 beg += decode.consumed; 640 } 641 data[length] = 0; 642 result = (s8){.len = length, .data = data}; 643 arena_pop(a, commit - length); 644 } 645 return result; 646 } 647 648 function s16 649 s8_to_s16(Arena *a, s8 in) 650 { 651 s16 result = {0}; 652 if (in.len) { 653 iz required = 2 * in.len + 1; 654 u16 *data = push_array(a, u16, required); 655 iz length = 0; 656 /* TODO(rnp): utf8_decode */ 657 for (iz i = 0; i < in.len; i++) { 658 u32 cp = in.data[i]; 659 length += utf16_encode(data + length, cp); 660 } 661 result = (s16){.len = length, .data = data}; 662 arena_pop(a, required - length); 663 } 664 return result; 665 } 666 667 #define push_s8_from_parts(a, j, ...) push_s8_from_parts_((a), (j), arg_list(s8, __VA_ARGS__)) 668 function s8 669 push_s8_from_parts_(Arena *arena, s8 joiner, s8 *parts, iz count) 670 { 671 iz length = joiner.len * (count - 1); 672 for (iz i = 0; i < count; i++) 673 length += parts[i].len; 674 675 s8 result = {.len = length, .data = arena_commit(arena, length + 1)}; 676 677 iz offset = 0; 678 for (iz i = 0; i < count; i++) { 679 if (i != 0) { 680 mem_copy(result.data + offset, joiner.data, (uz)joiner.len); 681 offset += joiner.len; 682 } 683 mem_copy(result.data + offset, parts[i].data, (uz)parts[i].len); 684 offset += parts[i].len; 685 } 686 result.data[result.len] = 0; 687 688 return result; 689 } 690 691 function s8 692 push_s8(Arena *a, s8 str) 693 { 694 s8 result = s8_alloc(a, str.len + 1); 695 result.len -= 1; 696 mem_copy(result.data, str.data, (uz)result.len); 697 return result; 698 } 699 700 function force_inline u32 701 round_down_power_of_2(u32 a) 702 { 703 u32 result = 0x80000000UL >> clz_u32(a); 704 return result; 705 } 706 707 function force_inline u32 708 round_up_power_of_2(u32 a) 709 { 710 u32 result = 0x80000000UL >> (clz_u32(a - 1) - 1); 711 return result; 712 } 713 714 function force_inline iz 715 round_up_to(iz value, iz multiple) 716 { 717 iz result = value; 718 if (value % multiple != 0) 719 result += multiple - value % multiple; 720 return result; 721 } 722 723 function void 724 split_rect_horizontal(Rect rect, f32 fraction, Rect *left, Rect *right) 725 { 726 if (left) { 727 left->pos = rect.pos; 728 left->size.h = rect.size.h; 729 left->size.w = rect.size.w * fraction; 730 } 731 if (right) { 732 right->pos = rect.pos; 733 right->pos.x += rect.size.w * fraction; 734 right->size.h = rect.size.h; 735 right->size.w = rect.size.w * (1.0f - fraction); 736 } 737 } 738 739 function void 740 split_rect_vertical(Rect rect, f32 fraction, Rect *top, Rect *bot) 741 { 742 if (top) { 743 top->pos = rect.pos; 744 top->size.w = rect.size.w; 745 top->size.h = rect.size.h * fraction; 746 } 747 if (bot) { 748 bot->pos = rect.pos; 749 bot->pos.y += rect.size.h * fraction; 750 bot->size.w = rect.size.w; 751 bot->size.h = rect.size.h * (1.0f - fraction); 752 } 753 } 754 755 function void 756 cut_rect_horizontal(Rect rect, f32 at, Rect *left, Rect *right) 757 { 758 at = MIN(at, rect.size.w); 759 if (left) { 760 *left = rect; 761 left->size.w = at; 762 } 763 if (right) { 764 *right = rect; 765 right->pos.x += at; 766 right->size.w -= at; 767 } 768 } 769 770 function void 771 cut_rect_vertical(Rect rect, f32 at, Rect *top, Rect *bot) 772 { 773 at = MIN(at, rect.size.h); 774 if (top) { 775 *top = rect; 776 top->size.h = at; 777 } 778 if (bot) { 779 *bot = rect; 780 bot->pos.y += at; 781 bot->size.h -= at; 782 } 783 } 784 785 function IntegerConversion 786 integer_from_s8(s8 raw) 787 { 788 read_only local_persist alignas(64) i8 lut[64] = { 789 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, 790 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, 791 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 792 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, 793 }; 794 795 IntegerConversion result = {.unparsed = raw}; 796 797 iz i = 0; 798 i64 scale = 1; 799 if (raw.len > 0 && raw.data[0] == '-') { 800 scale = -1; 801 i = 1; 802 } 803 804 b32 hex = 0; 805 if (raw.len - i > 2 && raw.data[i] == '0' && (raw.data[1] == 'x' || raw.data[1] == 'X')) { 806 hex = 1; 807 i += 2; 808 } 809 810 #define integer_conversion_body(radix, clamp) do {\ 811 for (; i < raw.len; i++) {\ 812 i64 value = lut[Min((u8)(raw.data[i] - (u8)'0'), clamp)];\ 813 if (value >= 0) {\ 814 if (result.U64 > (U64_MAX - (u64)value) / radix) {\ 815 result.result = IntegerConversionResult_OutOfRange;\ 816 result.U64 = U64_MAX;\ 817 return result;\ 818 } else {\ 819 result.U64 = radix * result.U64 + (u64)value;\ 820 }\ 821 } else {\ 822 break;\ 823 }\ 824 }\ 825 } while (0) 826 827 if (hex) integer_conversion_body(16u, 63u); 828 else integer_conversion_body(10u, 15u); 829 830 #undef integer_conversion_body 831 832 result.unparsed = (s8){.len = raw.len - i, .data = raw.data + i}; 833 result.result = IntegerConversionResult_Success; 834 if (scale < 0) result.U64 = 0 - result.U64; 835 836 return result; 837 } 838 839 function f64 840 parse_f64(s8 s) 841 { 842 IntegerConversion integral = integer_from_s8(s); 843 844 s = integral.unparsed; 845 if (*s.data == '.') { s.data++; s.len--; } 846 while (s.len > 0 && s.data[s.len - 1] == '0') s.len--; 847 848 IntegerConversion fractional = integer_from_s8(s); 849 850 u64 power = (u64)(fractional.unparsed.data - s.data); 851 f64 frac = (f64)fractional.U64; 852 while (power > 0) { frac /= 10.0; power--; } 853 854 f64 result = (f64)integral.S64 + frac; 855 return result; 856 }