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