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