util.c (13286B)
1 /* See LICENSE for license details. */ 2 #define zero_struct(s) mem_clear(s, 0, sizeof(*s)) 3 function void * 4 mem_clear(void *restrict p_, u8 c, iz size) 5 { 6 u8 *p = p_; 7 while (size > 0) p[--size] = c; 8 return p; 9 } 10 11 function void 12 mem_copy(void *restrict dest, void *restrict src, uz n) 13 { 14 u8 *s = src, *d = dest; 15 for (; n; n--) *d++ = *s++; 16 } 17 18 function void 19 mem_move(u8 *dest, u8 *src, uz n) 20 { 21 if (dest < src) mem_copy(dest, src, n); 22 else while (n) { n--; dest[n] = src[n]; } 23 } 24 25 function u8 * 26 arena_aligned_start(Arena a, uz alignment) 27 { 28 uz padding = -(uintptr_t)a.beg & (alignment - 1); 29 u8 *result = a.beg + padding; 30 return result; 31 } 32 33 #define arena_capacity(a, t) arena_capacity_(a, sizeof(t), alignof(t)) 34 function iz 35 arena_capacity_(Arena *a, iz size, uz alignment) 36 { 37 iz available = a->end - arena_aligned_start(*a, alignment); 38 iz result = available / size; 39 return result; 40 } 41 42 function u8 * 43 arena_commit(Arena *a, iz size) 44 { 45 assert(a->end - a->beg >= size); 46 u8 *result = a->beg; 47 a->beg += size; 48 return result; 49 } 50 51 function void 52 arena_pop(Arena *a, iz length) 53 { 54 a->beg -= length; 55 } 56 57 #define push_array(a, t, n) (t *)arena_alloc(a, sizeof(t), _Alignof(t), n) 58 #define push_struct(a, t) (t *)arena_alloc(a, sizeof(t), _Alignof(t), 1) 59 function void * 60 arena_alloc(Arena *a, iz len, uz align, iz count) 61 { 62 void *result = 0; 63 if (a->beg) { 64 u8 *start = arena_aligned_start(*a, align); 65 iz available = a->end - start; 66 assert((available >= 0 && count <= available / len)); 67 asan_unpoison_region(start, count * len); 68 a->beg = start + count * len; 69 /* TODO: Performance? */ 70 result = mem_clear(start, 0, count * len); 71 } 72 return result; 73 } 74 75 enum { DA_INITIAL_CAP = 4 }; 76 #define da_reserve(a, s, n) \ 77 (s)->data = da_reserve_((a), (s)->data, &(s)->capacity, (s)->count + n, \ 78 _Alignof(typeof(*(s)->data)), sizeof(*(s)->data)) 79 80 #define da_push(a, s) \ 81 ((s)->count == (s)->capacity \ 82 ? da_reserve(a, s, 1), \ 83 (s)->data + (s)->count++ \ 84 : (s)->data + (s)->count++) 85 86 function void * 87 da_reserve_(Arena *a, void *data, iz *capacity, iz needed, uz align, iz size) 88 { 89 iz cap = *capacity; 90 91 /* NOTE(rnp): handle both 0 initialized DAs and DAs that need to be moved (they started 92 * on the stack or someone allocated something in the middle of the arena during usage) */ 93 if (!data || a->beg != (u8 *)data + cap * size) { 94 void *copy = arena_alloc(a, size, align, cap); 95 if (data) mem_copy(copy, data, (uz)(cap * size)); 96 data = copy; 97 } 98 99 if (!cap) cap = DA_INITIAL_CAP; 100 while (cap < needed) cap *= 2; 101 arena_alloc(a, size, align, cap - *capacity); 102 *capacity = cap; 103 return data; 104 } 105 106 function Arena 107 sub_arena(Arena *a, iz len, uz align) 108 { 109 Arena result = {0}; 110 111 uz padding = -(uintptr_t)a->beg & (align - 1); 112 result.beg = a->beg + padding; 113 result.end = result.beg + len; 114 arena_commit(a, len + (iz)padding); 115 116 return result; 117 } 118 119 function TempArena 120 begin_temp_arena(Arena *a) 121 { 122 TempArena result = {.arena = a, .old_beg = a->beg}; 123 return result; 124 } 125 126 function void 127 end_temp_arena(TempArena ta) 128 { 129 Arena *a = ta.arena; 130 if (a) { 131 assert(a->beg >= ta.old_beg); 132 a->beg = ta.old_beg; 133 } 134 } 135 136 function u32 137 utf8_encode(u8 *out, u32 cp) 138 { 139 u32 result = 1; 140 if (cp <= 0x7F) { 141 out[0] = cp & 0x7F; 142 } else if (cp <= 0x7FF) { 143 result = 2; 144 out[0] = ((cp >> 6) & 0x1F) | 0xC0; 145 out[1] = ((cp >> 0) & 0x3F) | 0x80; 146 } else if (cp <= 0xFFFF) { 147 result = 3; 148 out[0] = ((cp >> 12) & 0x0F) | 0xE0; 149 out[1] = ((cp >> 6) & 0x3F) | 0x80; 150 out[2] = ((cp >> 0) & 0x3F) | 0x80; 151 } else if (cp <= 0x10FFFF) { 152 result = 4; 153 out[0] = ((cp >> 18) & 0x07) | 0xF0; 154 out[1] = ((cp >> 12) & 0x3F) | 0x80; 155 out[2] = ((cp >> 6) & 0x3F) | 0x80; 156 out[3] = ((cp >> 0) & 0x3F) | 0x80; 157 } else { 158 out[0] = '?'; 159 } 160 return result; 161 } 162 163 function UnicodeDecode 164 utf16_decode(u16 *data, iz length) 165 { 166 UnicodeDecode result = {.cp = U32_MAX}; 167 if (length) { 168 result.consumed = 1; 169 result.cp = data[0]; 170 if (length > 1 && BETWEEN(data[0], 0xD800u, 0xDBFFu) 171 && BETWEEN(data[1], 0xDC00u, 0xDFFFu)) 172 { 173 result.consumed = 2; 174 result.cp = ((data[0] - 0xD800u) << 10u) | ((data[1] - 0xDC00u) + 0x10000u); 175 } 176 } 177 return result; 178 } 179 180 function u32 181 utf16_encode(u16 *out, u32 cp) 182 { 183 u32 result = 1; 184 if (cp == U32_MAX) { 185 out[0] = '?'; 186 } else if (cp < 0x10000u) { 187 out[0] = (u16)cp; 188 } else { 189 u32 value = cp - 0x10000u; 190 out[0] = (u16)(0xD800u + (value >> 10u)); 191 out[1] = (u16)(0xDC00u + (value & 0x3FFu)); 192 result = 2; 193 } 194 return result; 195 } 196 197 function Stream 198 stream_alloc(Arena *a, i32 cap) 199 { 200 Stream result = {.cap = cap}; 201 result.data = push_array(a, u8, cap); 202 return result; 203 } 204 205 function s8 206 stream_to_s8(Stream *s) 207 { 208 s8 result = s8(""); 209 if (!s->errors) result = (s8){.len = s->widx, .data = s->data}; 210 return result; 211 } 212 213 function void 214 stream_reset(Stream *s, i32 index) 215 { 216 s->errors = s->cap <= index; 217 if (!s->errors) 218 s->widx = index; 219 } 220 221 function void 222 stream_commit(Stream *s, i32 count) 223 { 224 s->errors |= !BETWEEN(s->widx + count, 0, s->cap); 225 if (!s->errors) 226 s->widx += count; 227 } 228 229 function void 230 stream_append(Stream *s, void *data, iz count) 231 { 232 s->errors |= (s->cap - s->widx) < count; 233 if (!s->errors) { 234 mem_copy(s->data + s->widx, data, (uz)count); 235 s->widx += (i32)count; 236 } 237 } 238 239 function void 240 stream_append_byte(Stream *s, u8 b) 241 { 242 stream_append(s, &b, 1); 243 } 244 245 function void 246 stream_pad(Stream *s, u8 b, i32 n) 247 { 248 while (n > 0) stream_append_byte(s, b), n--; 249 } 250 251 function void 252 stream_append_s8(Stream *s, s8 str) 253 { 254 stream_append(s, str.data, str.len); 255 } 256 257 #define stream_append_s8s(s, ...) stream_append_s8s_(s, arg_list(s8, ##__VA_ARGS__)) 258 function void 259 stream_append_s8s_(Stream *s, s8 *strs, iz count) 260 { 261 for (iz i = 0; i < count; i++) 262 stream_append(s, strs[i].data, strs[i].len); 263 } 264 265 function void 266 stream_append_u64_width(Stream *s, u64 n, u64 min_width) 267 { 268 u8 tmp[64]; 269 u8 *end = tmp + sizeof(tmp); 270 u8 *beg = end; 271 min_width = MIN(sizeof(tmp), min_width); 272 273 do { *--beg = (u8)('0' + (n % 10)); } while (n /= 10); 274 while (end - beg > 0 && (uz)(end - beg) < min_width) 275 *--beg = '0'; 276 277 stream_append(s, beg, end - beg); 278 } 279 280 function void 281 stream_append_u64(Stream *s, u64 n) 282 { 283 stream_append_u64_width(s, n, 0); 284 } 285 286 function void 287 stream_append_hex_u64(Stream *s, u64 n) 288 { 289 if (!s->errors) { 290 u8 buf[16]; 291 u8 *end = buf + sizeof(buf); 292 u8 *beg = end; 293 while (n) { 294 *--beg = (u8)"0123456789abcdef"[n & 0x0F]; 295 n >>= 4; 296 } 297 while (end - beg < 2) 298 *--beg = '0'; 299 stream_append(s, beg, end - beg); 300 } 301 } 302 303 function void 304 stream_append_i64(Stream *s, i64 n) 305 { 306 if (n < 0) { 307 stream_append_byte(s, '-'); 308 n *= -1; 309 } 310 stream_append_u64(s, (u64)n); 311 } 312 313 function void 314 stream_append_f64(Stream *s, f64 f, u64 prec) 315 { 316 if (f < 0) { 317 stream_append_byte(s, '-'); 318 f *= -1; 319 } 320 321 /* NOTE: round last digit */ 322 f += 0.5f / (f64)prec; 323 324 if (f >= (f64)(-1UL >> 1)) { 325 stream_append_s8(s, s8("inf")); 326 } else { 327 u64 integral = (u64)f; 328 u64 fraction = (u64)((f - (f64)integral) * (f64)prec); 329 stream_append_u64(s, integral); 330 stream_append_byte(s, '.'); 331 for (u64 i = prec / 10; i > 1; i /= 10) { 332 if (i > fraction) 333 stream_append_byte(s, '0'); 334 } 335 stream_append_u64(s, fraction); 336 } 337 } 338 339 function void 340 stream_append_f64_e(Stream *s, f64 f) 341 { 342 /* TODO: there should be a better way of doing this */ 343 #if 0 344 /* NOTE: we ignore subnormal numbers for now */ 345 union { f64 f; u64 u; } u = {.f = f}; 346 i32 exponent = ((u.u >> 52) & 0x7ff) - 1023; 347 f32 log_10_of_2 = 0.301f; 348 i32 scale = (exponent * log_10_of_2); 349 /* NOTE: normalize f */ 350 for (i32 i = ABS(scale); i > 0; i--) 351 f *= (scale > 0)? 0.1f : 10.0f; 352 #else 353 i32 scale = 0; 354 if (f != 0) { 355 while (f > 1) { 356 f *= 0.1f; 357 scale++; 358 } 359 while (f < 1) { 360 f *= 10.0f; 361 scale--; 362 } 363 } 364 #endif 365 366 u32 prec = 100; 367 stream_append_f64(s, f, prec); 368 stream_append_byte(s, 'e'); 369 stream_append_byte(s, scale >= 0? '+' : '-'); 370 for (u32 i = prec / 10; i > 1; i /= 10) 371 stream_append_byte(s, '0'); 372 stream_append_u64(s, (u64)ABS(scale)); 373 } 374 375 function void 376 stream_append_v2(Stream *s, v2 v) 377 { 378 stream_append_byte(s, '{'); 379 stream_append_f64(s, v.x, 100); 380 stream_append_s8(s, s8(", ")); 381 stream_append_f64(s, v.y, 100); 382 stream_append_byte(s, '}'); 383 } 384 385 function Stream 386 arena_stream(Arena a) 387 { 388 Stream result = {0}; 389 result.data = a.beg; 390 result.cap = (i32)(a.end - a.beg); 391 392 /* TODO(rnp): no idea what to do here if we want to maintain the ergonomics */ 393 asan_unpoison_region(result.data, result.cap); 394 395 return result; 396 } 397 398 function s8 399 arena_stream_commit(Arena *a, Stream *s) 400 { 401 ASSERT(s->data == a->beg); 402 s8 result = stream_to_s8(s); 403 arena_commit(a, result.len); 404 return result; 405 } 406 407 function s8 408 arena_stream_commit_zero(Arena *a, Stream *s) 409 { 410 b32 error = s->errors || s->widx == s->cap; 411 if (!error) 412 s->data[s->widx] = 0; 413 s8 result = stream_to_s8(s); 414 arena_commit(a, result.len + 1); 415 return result; 416 } 417 418 /* NOTE(rnp): FNV-1a hash */ 419 function u64 420 s8_hash(s8 v) 421 { 422 u64 h = 0x3243f6a8885a308d; /* digits of pi */ 423 for (; v.len; v.len--) { 424 h ^= v.data[v.len - 1] & 0xFF; 425 h *= 1111111111111111111; /* random prime */ 426 } 427 return h; 428 } 429 430 function s8 431 c_str_to_s8(char *cstr) 432 { 433 s8 result = {.data = (u8 *)cstr}; 434 if (cstr) { while (*cstr) { result.len++; cstr++; } } 435 return result; 436 } 437 438 /* NOTE(rnp): returns < 0 if byte is not found */ 439 function iz 440 s8_scan_backwards(s8 s, u8 byte) 441 { 442 iz result = s.len; 443 while (result && s.data[result - 1] != byte) result--; 444 result--; 445 return result; 446 } 447 448 function s8 449 s8_cut_head(s8 s, iz cut) 450 { 451 s8 result = s; 452 if (cut > 0) { 453 result.data += cut; 454 result.len -= cut; 455 } 456 return result; 457 } 458 459 function s8 460 s8_alloc(Arena *a, iz len) 461 { 462 s8 result = {.data = push_array(a, u8, len), .len = len}; 463 return result; 464 } 465 466 function s8 467 s16_to_s8(Arena *a, s16 in) 468 { 469 s8 result = s8(""); 470 if (in.len) { 471 iz commit = in.len * 4; 472 iz length = 0; 473 u8 *data = arena_commit(a, commit + 1); 474 u16 *beg = in.data; 475 u16 *end = in.data + in.len; 476 while (beg < end) { 477 UnicodeDecode decode = utf16_decode(beg, end - beg); 478 length += utf8_encode(data + length, decode.cp); 479 beg += decode.consumed; 480 } 481 data[length] = 0; 482 result = (s8){.len = length, .data = data}; 483 arena_pop(a, commit - length); 484 } 485 return result; 486 } 487 488 function s16 489 s8_to_s16(Arena *a, s8 in) 490 { 491 s16 result = {0}; 492 if (in.len) { 493 iz required = 2 * in.len + 1; 494 u16 *data = push_array(a, u16, required); 495 iz length = 0; 496 /* TODO(rnp): utf8_decode */ 497 for (iz i = 0; i < in.len; i++) { 498 u32 cp = in.data[i]; 499 length += utf16_encode(data + length, cp); 500 } 501 result = (s16){.len = length, .data = data}; 502 arena_pop(a, required - length); 503 } 504 return result; 505 } 506 507 function s8 508 push_s8(Arena *a, s8 str) 509 { 510 s8 result = s8_alloc(a, str.len); 511 mem_copy(result.data, str.data, (uz)result.len); 512 return result; 513 } 514 515 function s8 516 push_s8_zero(Arena *a, s8 str) 517 { 518 s8 result = s8_alloc(a, str.len + 1); 519 result.len -= 1; 520 mem_copy(result.data, str.data, (uz)result.len); 521 return result; 522 } 523 524 function u32 525 round_down_power_of_2(u32 a) 526 { 527 u32 result = 0x80000000UL >> clz_u32(a); 528 return result; 529 } 530 531 function u32 532 round_up_power_of_2(u32 a) 533 { 534 u32 result = 0x80000000UL >> (clz_u32(a - 1) - 1); 535 return result; 536 } 537 538 function iz 539 round_up_to(iz value, iz multiple) 540 { 541 iz result = value; 542 if (value % multiple != 0) 543 result += multiple - value % multiple; 544 return result; 545 } 546 547 function void 548 split_rect_horizontal(Rect rect, f32 fraction, Rect *left, Rect *right) 549 { 550 if (left) { 551 left->pos = rect.pos; 552 left->size.h = rect.size.h; 553 left->size.w = rect.size.w * fraction; 554 } 555 if (right) { 556 right->pos = rect.pos; 557 right->pos.x += rect.size.w * fraction; 558 right->size.h = rect.size.h; 559 right->size.w = rect.size.w * (1.0f - fraction); 560 } 561 } 562 563 function void 564 split_rect_vertical(Rect rect, f32 fraction, Rect *top, Rect *bot) 565 { 566 if (top) { 567 top->pos = rect.pos; 568 top->size.w = rect.size.w; 569 top->size.h = rect.size.h * fraction; 570 } 571 if (bot) { 572 bot->pos = rect.pos; 573 bot->pos.y += rect.size.h * fraction; 574 bot->size.w = rect.size.w; 575 bot->size.h = rect.size.h * (1.0f - fraction); 576 } 577 } 578 579 function void 580 cut_rect_horizontal(Rect rect, f32 at, Rect *left, Rect *right) 581 { 582 at = MIN(at, rect.size.w); 583 if (left) { 584 *left = rect; 585 left->size.w = at; 586 } 587 if (right) { 588 *right = rect; 589 right->pos.x += at; 590 right->size.w -= at; 591 } 592 } 593 594 function void 595 cut_rect_vertical(Rect rect, f32 at, Rect *top, Rect *bot) 596 { 597 at = MIN(at, rect.size.h); 598 if (top) { 599 *top = rect; 600 top->size.h = at; 601 } 602 if (bot) { 603 *bot = rect; 604 bot->pos.y += at; 605 bot->size.h -= at; 606 } 607 } 608 609 function f64 610 parse_f64(s8 s) 611 { 612 f64 integral = 0, fractional = 0, sign = 1; 613 614 if (s.len && *s.data == '-') { 615 sign = -1; 616 s.data++; 617 s.len--; 618 } 619 620 while (s.len && *s.data != '.') { 621 integral *= 10; 622 integral += *s.data - '0'; 623 s.data++; 624 s.len--; 625 } 626 627 if (*s.data == '.') { s.data++; s.len--; } 628 629 while (s.len) { 630 ASSERT(s.data[s.len - 1] != '.'); 631 fractional /= 10; 632 fractional += (f64)(s.data[--s.len] - '0') / 10.0; 633 } 634 f64 result = sign * (integral + fractional); 635 return result; 636 } 637 638 function FileWatchDirectory * 639 lookup_file_watch_directory(FileWatchContext *ctx, u64 hash) 640 { 641 FileWatchDirectory *result = 0; 642 for (u32 i = 0; i < ctx->count; i++) { 643 FileWatchDirectory *test = ctx->data + i; 644 if (test->hash == hash) { 645 result = test; 646 break; 647 } 648 } 649 return result; 650 }