util.c (12858B)
1 /* See LICENSE for copyright details */ 2 3 /* NOTE: avoids braindead standards committee UB when n is negative or shift is > 31 */ 4 static u32 5 safe_left_shift(u32 n, u32 shift) 6 { 7 u64 result = (u64)n << (shift & 63); 8 return result & 0xFFFFFFFF; 9 } 10 11 static u32 12 round_down_power_of_2(u32 a) 13 { 14 u32 result = 0x80000000UL >> clz_u32(a); 15 return result; 16 } 17 18 static v2 19 sub_v2(v2 a, v2 b) 20 { 21 v2 result; 22 result.x = a.x - b.x; 23 result.y = a.y - b.y; 24 return result; 25 } 26 27 static f32 28 length_v2(v2 a) 29 { 30 f32 result = a.x * a.x + a.y * a.y; 31 return result; 32 } 33 34 static b32 35 equal_iv2(iv2 a, iv2 b) 36 { 37 b32 result = a.x == b.x && a.y == b.y; 38 return result; 39 } 40 41 static b32 42 equal_uv2(uv2 a, uv2 b) 43 { 44 b32 result = a.x == b.x && a.y == b.y; 45 return result; 46 } 47 48 static v2 49 v2_from_iv2(iv2 a) 50 { 51 v2 result = {.x = a.x, .y = a.y}; 52 return result; 53 } 54 55 static b32 56 is_valid_range(Range r) 57 { 58 b32 result = !equal_iv2(r.end, INVALID_RANGE_END); 59 return result; 60 } 61 62 static b32 63 equal_range(Range a, Range b) 64 { 65 b32 result = equal_iv2(a.start, b.start) && equal_iv2(a.end, b.end); 66 return result; 67 } 68 69 static b32 70 point_in_rect(v2 point, Rect rect) 71 { 72 v2 max = {.x = rect.pos.x + rect.size.w, .y = rect.pos.y + rect.size.h}; 73 b32 result = BETWEEN(point.x, rect.pos.x, max.x) && BETWEEN(point.y, rect.pos.y, max.y); 74 return result; 75 } 76 77 static Range 78 normalize_range(Range r) 79 { 80 Range result; 81 if (!is_valid_range(r) || r.start.y < r.end.y) { 82 result = r; 83 } else if (r.end.y < r.start.y) { 84 result = (Range){.start = r.end, .end = r.start}; 85 } else { 86 result.start.y = result.end.y = r.start.y; 87 result.start.x = MIN(r.start.x, r.end.x); 88 result.end.x = MAX(r.start.x, r.end.x); 89 } 90 return result; 91 } 92 93 /* NOTE(rnp): based on nullprogram's lock-free, concurrent, 94 * generic queue in 32 bits */ 95 static i32 96 work_queue_push(u32 *q, u32 capacity) 97 { 98 ASSERT(ISPOWEROFTWO(capacity)); 99 u32 r = atomic_load(q); 100 i32 mask = capacity - 1; 101 i32 head = r & mask; 102 i32 tail = (r >> 16) & mask; 103 i32 next = (head + 1) & mask; 104 /* NOTE(rnp): prevent an overflow into the tail on commit */ 105 if (r & 0x8000) atomic_and(q, ~0x8000u); 106 return next == tail ? -1 : head; 107 } 108 109 static void 110 work_queue_push_commit(u32 *q) 111 { 112 atomic_fetch_add(q, 1); 113 } 114 115 static i32 116 work_queue_pop(u32 *q, u32 capacity) 117 { 118 ASSERT(ISPOWEROFTWO(capacity)); 119 u32 r = atomic_load(q); 120 i32 mask = capacity - 1; 121 i32 head = r & mask; 122 i32 tail = (r >> 16) & mask; 123 return head == tail ? -1 : tail; 124 } 125 126 static void 127 work_queue_pop_commit(u32 *q) 128 { 129 atomic_fetch_add(q, 0x10000u); 130 } 131 132 static b32 133 work_queue_empty(u32 *q, u32 capacity) 134 { 135 ASSERT(ISPOWEROFTWO(capacity)); 136 u32 r = atomic_load(q); 137 i32 mask = capacity - 1; 138 i32 head = r & mask; 139 i32 tail = (r >> 16) & mask; 140 return head == tail; 141 } 142 143 static void 144 work_queue_insert(Term *t, u32 type, void *ctx) 145 { 146 i32 index = work_queue_push(&t->work_queue, t->work_queue_capacity); 147 /* NOTE(rnp): if we ever fill this up we need to resize the queue */ 148 ASSERT(index != -1); 149 work_queue_push_commit(&t->work_queue); 150 t->work_queue_items[index].type = type; 151 t->work_queue_items[index].ctx = ctx; 152 } 153 154 static void 155 mem_copy(void *restrict src, void *restrict dest, size len) 156 { 157 ASSERT(len >= 0); 158 u8 *s = src, *d = dest; 159 for (; len; len--) *d++ = *s++; 160 } 161 162 #define zero_struct(s) mem_clear(s, 0, sizeof(typeof(*s))) 163 static void * 164 mem_clear(void *p_, u8 c, size len) 165 { 166 u8 *p = p_; 167 while (len) p[--len] = c; 168 return p; 169 } 170 171 #define push_struct(a, t) alloc(a, t, 1) 172 #define alloc(a, t, n) (t *)alloc_(a, sizeof(t), _Alignof(t), n) 173 static void * 174 alloc_(Arena *a, size len, size align, size count) 175 { 176 size padding = -(uintptr_t)a->beg & (align - 1); 177 size available = a->end - a->beg - padding; 178 if (available <= 0 || available / len < count) { 179 ASSERT(0); 180 } 181 182 void *p = a->beg + padding; 183 a->beg += padding + count * len; 184 return mem_clear(p, 0, count * len); 185 } 186 187 static Arena 188 arena_from_memory_block(MemoryBlock memory) 189 { 190 Arena result; 191 result.beg = memory.memory; 192 result.end = memory.memory + memory.size; 193 return result; 194 } 195 196 static MemoryBlock 197 memory_block_from_arena(Arena *a, size requested_size) 198 { 199 MemoryBlock result; 200 result.memory = alloc_(a, requested_size, 64, 1); 201 result.size = requested_size; 202 return result; 203 } 204 205 static Arena 206 sub_arena(Arena *a, size size) 207 { 208 Arena result = {0}; 209 result.beg = alloc_(a, size, 64, 1); 210 result.end = result.beg + size; 211 return result; 212 } 213 214 static TempArena 215 begin_temp_arena(Arena *a) 216 { 217 TempArena result; 218 result.arena = a; 219 result.old_beg = a->beg; 220 return result; 221 } 222 223 static void 224 end_temp_arena(TempArena ta) 225 { 226 Arena *a = ta.arena; 227 ASSERT(a->beg >= ta.old_beg); 228 a->beg = ta.old_beg; 229 } 230 231 /* NOTE: This performs wrapping of the ring buffer as needed; since a line could be in 232 * progress this must also adjust the start and end of the current line */ 233 static void 234 commit_to_rb(TermView *tv, size len) 235 { 236 ASSERT(ABS(len) <= tv->log.cap); 237 238 tv->log.widx += len; 239 tv->log.filled += len; 240 241 CLAMP(tv->log.filled, 0, tv->log.cap); 242 if (tv->log.widx >= tv->log.cap) { 243 tv->log.widx -= tv->log.cap; 244 245 size line = tv->lines.widx; 246 tv->lines.buf[line].start -= tv->log.cap; 247 tv->lines.buf[line].end -= tv->log.cap; 248 } 249 250 ASSERT(tv->log.filled >= 0); 251 ASSERT(tv->log.widx >= 0 && tv->log.widx < tv->log.cap); 252 } 253 254 static void 255 line_buf_alloc(LineBuf *lb, Arena *a, u8 *start_position, CellStyle state, size capacity) 256 { 257 lb->cap = capacity; 258 lb->filled = 0; 259 lb->widx = 0; 260 lb->buf = alloc(a, Line, capacity); 261 lb->buf[0].start = start_position; 262 lb->buf[0].end = start_position; 263 lb->buf[0].cursor_state = state; 264 } 265 266 static s8 267 s8alloc(Arena *a, size len) 268 { 269 return (s8){.len = len, .data = alloc(a, u8, len)}; 270 } 271 272 static s8 273 c_str_to_s8(char *s) 274 { 275 s8 result = {.data = (u8 *)s}; 276 while (*s) { result.len++; s++; } 277 return result; 278 } 279 280 static b32 281 s8_equal(s8 a, s8 b) 282 { 283 b32 result = a.len == b.len; 284 for (size i = 0; result && i < a.len; i++) 285 result = a.data[i] == b.data[i]; 286 return result; 287 } 288 289 static b32 290 s8_prefix_of(s8 s, s8 match) 291 { 292 b32 result = 0; 293 if (s.len <= match.len) { 294 s8 head = {.data = match.data, .len = s.len}; 295 result = s8_equal(s, head); 296 } 297 return result; 298 } 299 300 static s8 301 s8_chop_at(s8 raw, u8 delim) 302 { 303 size i; 304 for (i = 0; i < raw.len; i++) { 305 if (raw.data[i] == delim) 306 break; 307 } 308 s8 result = {.len = i, .data = raw.data}; 309 return result; 310 } 311 312 static void 313 s8_parse_i32_accum(struct conversion_result *result, s8 raw) 314 { 315 result->status = CR_SUCCESS; 316 317 size i = 0; 318 i32 scale = 1; 319 if (raw.len && raw.data[0] == '-') { 320 scale = -1; 321 i = 1; 322 } 323 324 for (; i < raw.len; i++) { 325 i32 digit = (i32)raw.data[i] - '0'; 326 if (BETWEEN(digit, 0, 9)) { 327 if (result->i > (I32_MAX - digit) / 10) { 328 result->status = CR_OUT_OF_RANGE; 329 result->i = I32_MAX; 330 } else { 331 result->i = 10 * result->i + digit; 332 } 333 } else { 334 break; 335 } 336 } 337 338 if (i == 0 || (i == 1 && raw.data[0] == '-')) 339 result->status = CR_FAILURE; 340 341 result->unparsed = (s8){.len = raw.len - i, .data = raw.data + i}; 342 result->i *= scale; 343 } 344 345 static struct conversion_result 346 s8_parse_i32(s8 raw) 347 { 348 struct conversion_result result = {0}; 349 s8_parse_i32_accum(&result, raw); 350 return result; 351 } 352 353 static struct conversion_result 354 s8_parse_i32_until(s8 raw, u8 delim) 355 { 356 s8 chopped = s8_chop_at(raw, delim); 357 struct conversion_result result = s8_parse_i32(chopped); 358 result.unparsed = (s8){.data = raw.data + chopped.len, .len = raw.len - chopped.len}; 359 return result; 360 } 361 362 static Stream 363 arena_stream(Arena a) 364 { 365 Stream result = {0}; 366 result.cap = a.end - a.beg; 367 result.buf = (typeof(result.buf))a.beg; 368 return result; 369 } 370 371 static Stream 372 stream_alloc(Arena *a, u32 cap) 373 { 374 Stream result = {0}; 375 result.cap = cap; 376 result.buf = alloc(a, typeof(*result.buf), cap); 377 return result; 378 } 379 380 static s8 381 stream_to_s8(Stream *s) 382 { 383 s8 result = {.len = s->widx, .data = s->buf}; 384 return result; 385 } 386 387 static void 388 stream_push_byte(Stream *s, u8 cp) 389 { 390 s->errors |= !(s->cap - s->widx); 391 if (!s->errors) 392 s->buf[s->widx++] = cp; 393 } 394 395 static void 396 stream_push_s8(Stream *s, s8 str) 397 { 398 s->errors |= (s->cap - s->widx) < str.len; 399 if (!s->errors) { 400 for (size i = 0; i < str.len; i++) 401 s->buf[s->widx++] = str.data[i]; 402 } 403 } 404 405 /* NOTE: how can he get away with not using a library for this */ 406 static void 407 stream_push_s8_left_padded(Stream *s, s8 str, u32 width) 408 { 409 for (u32 i = str.len; i < width; i++) 410 stream_push_byte(s, ' '); 411 stream_push_s8(s, str); 412 } 413 414 static void 415 stream_push_s8s(Stream *s, u32 count, s8 *strs) 416 { 417 while (count) { stream_push_s8(s, *strs++); count--; } 418 } 419 420 static void 421 stream_push_hex_u64(Stream *s, u64 n) 422 { 423 if (!s->errors) { 424 static u8 hex[16] = {"0123456789abcdef"}; 425 u8 buf[16]; 426 u8 *end = buf + sizeof(buf); 427 u8 *beg = end; 428 while (n) { 429 *--beg = hex[n & 0x0F]; 430 n >>= 4; 431 } 432 while (end - beg < 2) 433 *--beg = '0'; 434 stream_push_s8(s, (s8){.len = end - beg, .data = beg}); 435 } 436 } 437 438 static void 439 stream_push_u64_padded(Stream *s, u64 n, u32 width) 440 { 441 if (!s->errors) { 442 u8 tmp[64]; 443 u8 *end = tmp + sizeof(tmp); 444 u8 *beg = end; 445 do { *--beg = '0' + (n % 10); } while (n /= 10); 446 447 s8 str = {.len = end - beg, .data = beg}; 448 for (u32 i = str.len; i < width; i++) 449 stream_push_byte(s, ' '); 450 stream_push_s8(s, str); 451 } 452 } 453 454 static void 455 stream_push_u64(Stream *s, u64 n) 456 { 457 stream_push_u64_padded(s, n, 0); 458 } 459 460 static void 461 stream_push_i64(Stream *s, i64 n) 462 { 463 s->errors |= n == I64_MIN; 464 if (!s->errors) { 465 if (n < 0) { 466 stream_push_byte(s, '-'); 467 n = -n; 468 } 469 stream_push_u64_padded(s, n, 0); 470 } 471 } 472 473 static void 474 stream_push_iv2(Stream *s, iv2 v) 475 { 476 stream_push_byte(s, '{'); 477 stream_push_i64(s, v.x); 478 stream_push_byte(s, ','); 479 stream_push_i64(s, v.y); 480 stream_push_byte(s, '}'); 481 } 482 483 static void 484 stream_push_f64(Stream *s, f64 f, i64 prec) 485 { 486 if (f < 0) { 487 stream_push_byte(s, '-'); 488 f *= -1; 489 } 490 491 /* NOTE: round last digit */ 492 f += 0.5f / prec; 493 494 if (f >= (f64)(-1UL >> 1)) { 495 stream_push_s8(s, s8("inf")); 496 } else { 497 u64 integral = f; 498 u64 fraction = (f - integral) * prec; 499 stream_push_u64(s, integral); 500 stream_push_byte(s, '.'); 501 for (u64 i = prec / 10; i > 1; i /= 10) { 502 if (i > fraction) 503 stream_push_byte(s, '0'); 504 } 505 stream_push_u64(s, fraction); 506 } 507 } 508 509 static SelectionIterator 510 selection_iterator(Range s, Row *rows, u32 term_width) 511 { 512 SelectionIterator result; 513 result.rows = rows; 514 result.cursor = (iv2){.x = -1, .y = -1}; 515 result.next = s.start; 516 result.end = s.end; 517 result.term_width = term_width; 518 return result; 519 } 520 521 static Cell * 522 selection_next(SelectionIterator *s) 523 { 524 Cell *result = 0; 525 526 if (!equal_iv2(s->cursor, s->end)) { 527 s->cursor = s->next; 528 result = s->rows[s->next.y] + s->next.x++; 529 if (s->next.x == s->term_width) { 530 s->next.x = 0; 531 s->next.y++; 532 } 533 } 534 535 return result; 536 } 537 538 static s8 539 envp_lookup(s8 name, char **e) 540 { 541 ASSERT(name.data[name.len - 1] == '='); 542 543 s8 s, result = {0}; 544 for (; *e; e++) { 545 s = c_str_to_s8(*e); 546 if (s8_prefix_of(name, s)) 547 break; 548 } 549 550 if (*e) { 551 result = s; 552 result.len -= name.len; 553 result.data += name.len; 554 } 555 556 return result; 557 } 558 559 static c8 ** 560 construct_c_str_array(Arena *a, SLLVariableVector vars) 561 { 562 c8 **result = alloc(a, c8 *, vars.count + 1); 563 564 VariableLink *chain = vars.next; 565 for (u32 i = 0; i < vars.count; i++) { 566 ASSERT(chain->var->type == VT_S8); 567 result[i] = (c8 *)chain->var->s8.data; 568 chain = chain->next; 569 } 570 571 return result; 572 } 573 574 static b32 575 any_mouse_down(TerminalInput *input) 576 { 577 b32 result = input->keys[MOUSE_LEFT].ended_down || 578 input->keys[MOUSE_RIGHT].ended_down || 579 input->keys[MOUSE_MIDDLE].ended_down; 580 return result; 581 } 582 583 static b32 584 all_mouse_up(TerminalInput *input) 585 { 586 b32 result = !input->keys[MOUSE_LEFT].ended_down && 587 !input->keys[MOUSE_RIGHT].ended_down && 588 !input->keys[MOUSE_MIDDLE].ended_down; 589 return result; 590 } 591 592 static void 593 button_action(ButtonState *button, b32 pressed) 594 { 595 if (pressed != button->ended_down) 596 button->transitions++; 597 button->ended_down = pressed; 598 } 599 600 static b32 601 pressed_last_frame(ButtonState *button) 602 { 603 b32 result = (button->transitions > 1) || (button->ended_down && button->transitions == 1); 604 return result; 605 } 606 607 static s8 608 utf8_encode(u32 cp) 609 { 610 static u8 buf[4]; 611 s8 result = {.data = buf}; 612 if (cp <= 0x7F) { 613 result.len = 1; 614 buf[0] = cp & 0x7F; 615 } else if (cp <= 0x7FF) { 616 result.len = 2; 617 buf[0] = ((cp >> 6) & 0x1F) | 0xC0; 618 buf[1] = ((cp >> 0) & 0x3F) | 0x80; 619 } else if (cp <= 0xFFFF) { 620 result.len = 3; 621 buf[0] = ((cp >> 12) & 0x0F) | 0xE0; 622 buf[1] = ((cp >> 6) & 0x3F) | 0x80; 623 buf[2] = ((cp >> 0) & 0x3F) | 0x80; 624 } else if (cp <= 0x10FFFF) { 625 result.len = 4; 626 buf[0] = ((cp >> 18) & 0x07) | 0xF0; 627 buf[1] = ((cp >> 12) & 0x3F) | 0x80; 628 buf[2] = ((cp >> 6) & 0x3F) | 0x80; 629 buf[3] = ((cp >> 0) & 0x3F) | 0x80; 630 } else { 631 buf[0] = '?'; 632 result.len = 1; 633 } 634 return result; 635 } 636 637 #include "extern/utf8_decode.c"