util.c (11978B)
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 >> _lzcnt_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(*s0))) 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 struct conversion_result 301 i32_from_cstr(char *s, char delim) 302 { 303 struct conversion_result ret = {.status = CR_FAILURE}; 304 i32 scale = 1; 305 306 if (!s || !s[0]) 307 return ret; 308 309 if (s[0] == '-') { 310 s++; 311 scale = -1; 312 } 313 314 for (; *s && *s != delim; s++) { 315 if (!BETWEEN(s[0], '0', '9')) 316 return ret; 317 ret.i *= 10; 318 ret.i += s[0] - '0'; 319 } 320 321 ret.i *= scale; 322 ret.status = CR_SUCCESS; 323 ret.unparsed = (*s == delim) ? s + 1 : s; 324 325 return ret; 326 } 327 328 static Stream 329 arena_stream(Arena a) 330 { 331 Stream result = {0}; 332 result.cap = a.end - a.beg; 333 result.buf = (typeof(result.buf))a.beg; 334 return result; 335 } 336 337 static Stream 338 stream_alloc(Arena *a, u32 cap) 339 { 340 Stream result = {0}; 341 result.cap = cap; 342 result.buf = alloc(a, typeof(*result.buf), cap); 343 return result; 344 } 345 346 static s8 347 stream_to_s8(Stream *s) 348 { 349 s8 result = {.len = s->widx, .data = s->buf}; 350 return result; 351 } 352 353 static void 354 stream_push_byte(Stream *s, u8 cp) 355 { 356 s->errors |= !(s->cap - s->widx); 357 if (!s->errors) 358 s->buf[s->widx++] = cp; 359 } 360 361 static void 362 stream_push_s8(Stream *s, s8 str) 363 { 364 s->errors |= (s->cap - s->widx) < str.len; 365 if (!s->errors) { 366 for (size i = 0; i < str.len; i++) 367 s->buf[s->widx++] = str.data[i]; 368 } 369 } 370 371 /* NOTE: how can he get away with not using a library for this */ 372 static void 373 stream_push_s8_left_padded(Stream *s, s8 str, u32 width) 374 { 375 for (u32 i = str.len; i < width; i++) 376 stream_push_byte(s, ' '); 377 stream_push_s8(s, str); 378 } 379 380 static void 381 stream_push_s8s(Stream *s, u32 count, s8 *strs) 382 { 383 while (count) { stream_push_s8(s, *strs++); count--; } 384 } 385 386 static void 387 stream_push_hex_u64(Stream *s, u64 n) 388 { 389 if (!s->errors) { 390 static u8 hex[16] = {"0123456789abcdef"}; 391 u8 buf[16]; 392 u8 *end = buf + sizeof(buf); 393 u8 *beg = end; 394 while (n) { 395 *--beg = hex[n & 0x0F]; 396 n >>= 4; 397 } 398 while (end - beg < 2) 399 *--beg = '0'; 400 stream_push_s8(s, (s8){.len = end - beg, .data = beg}); 401 } 402 } 403 404 static void 405 stream_push_u64_padded(Stream *s, u64 n, u32 width) 406 { 407 if (!s->errors) { 408 u8 tmp[64]; 409 u8 *end = tmp + sizeof(tmp); 410 u8 *beg = end; 411 do { *--beg = '0' + (n % 10); } while (n /= 10); 412 413 s8 str = {.len = end - beg, .data = beg}; 414 for (u32 i = str.len; i < width; i++) 415 stream_push_byte(s, ' '); 416 stream_push_s8(s, str); 417 } 418 } 419 420 static void 421 stream_push_u64(Stream *s, u64 n) 422 { 423 stream_push_u64_padded(s, n, 0); 424 } 425 426 static void 427 stream_push_i64(Stream *s, i64 n) 428 { 429 s->errors |= n == I64_MIN; 430 if (!s->errors) { 431 if (n < 0) { 432 stream_push_byte(s, '-'); 433 n = -n; 434 } 435 stream_push_u64_padded(s, n, 0); 436 } 437 } 438 439 static void 440 stream_push_iv2(Stream *s, iv2 v) 441 { 442 stream_push_byte(s, '{'); 443 stream_push_i64(s, v.x); 444 stream_push_byte(s, ','); 445 stream_push_i64(s, v.y); 446 stream_push_byte(s, '}'); 447 } 448 449 static void 450 stream_push_f64(Stream *s, f64 f, i64 prec) 451 { 452 if (f < 0) { 453 stream_push_byte(s, '-'); 454 f *= -1; 455 } 456 457 /* NOTE: round last digit */ 458 f += 0.5f / prec; 459 460 if (f >= (f64)(-1UL >> 1)) { 461 stream_push_s8(s, s8("inf")); 462 } else { 463 u64 integral = f; 464 u64 fraction = (f - integral) * prec; 465 stream_push_u64(s, integral); 466 stream_push_byte(s, '.'); 467 for (u64 i = prec / 10; i > 1; i /= 10) { 468 if (i > fraction) 469 stream_push_byte(s, '0'); 470 } 471 stream_push_u64(s, fraction); 472 } 473 } 474 475 static SelectionIterator 476 selection_iterator(Range s, Row *rows, u32 term_width) 477 { 478 SelectionIterator result; 479 result.rows = rows; 480 result.cursor = (iv2){.x = -1, .y = -1}; 481 result.next = s.start; 482 result.end = s.end; 483 result.term_width = term_width; 484 return result; 485 } 486 487 static Cell * 488 selection_next(SelectionIterator *s) 489 { 490 Cell *result = 0; 491 492 if (!equal_iv2(s->cursor, s->end)) { 493 s->cursor = s->next; 494 result = s->rows[s->next.y] + s->next.x++; 495 if (s->next.x == s->term_width) { 496 s->next.x = 0; 497 s->next.y++; 498 } 499 } 500 501 return result; 502 } 503 504 static s8 505 envp_lookup(s8 name, char **e) 506 { 507 ASSERT(name.data[name.len - 1] == '='); 508 509 s8 s, result = {0}; 510 for (; *e; e++) { 511 s = c_str_to_s8(*e); 512 if (s8_prefix_of(name, s)) 513 break; 514 } 515 516 if (*e) { 517 result = s; 518 result.len -= name.len; 519 result.data += name.len; 520 } 521 522 return result; 523 } 524 525 static c8 ** 526 construct_c_str_array(Arena *a, SLLVariableVector vars) 527 { 528 c8 **result = alloc(a, c8 *, vars.count + 1); 529 530 VariableLink *chain = vars.next; 531 for (u32 i = 0; i < vars.count; i++) { 532 ASSERT(chain->var->type == VT_S8); 533 result[i] = (c8 *)chain->var->s8.data; 534 chain = chain->next; 535 } 536 537 return result; 538 } 539 540 static b32 541 any_mouse_down(TerminalInput *input) 542 { 543 b32 result = input->keys[MOUSE_LEFT].ended_down || 544 input->keys[MOUSE_RIGHT].ended_down || 545 input->keys[MOUSE_MIDDLE].ended_down; 546 return result; 547 } 548 549 static b32 550 all_mouse_up(TerminalInput *input) 551 { 552 b32 result = !input->keys[MOUSE_LEFT].ended_down && 553 !input->keys[MOUSE_RIGHT].ended_down && 554 !input->keys[MOUSE_MIDDLE].ended_down; 555 return result; 556 } 557 558 static void 559 button_action(ButtonState *button, b32 pressed) 560 { 561 if (pressed != button->ended_down) 562 button->transitions++; 563 button->ended_down = pressed; 564 } 565 566 static b32 567 pressed_last_frame(ButtonState *button) 568 { 569 b32 result = (button->transitions > 1) || (button->ended_down && button->transitions == 1); 570 return result; 571 } 572 573 static s8 574 utf8_encode(u32 cp) 575 { 576 static u8 buf[4]; 577 s8 ret = { .data = buf, .len = -1 }; 578 if (cp < 0x80) { 579 ret.len = 1; 580 buf[0] = cp & 0x7F; 581 } else if (cp < 0x800) { 582 ret.len = 2; 583 buf[0] = ((cp >> 6) & 0x1F) | 0xC0; 584 buf[1] = ((cp >> 0) & 0x3F) | 0x80; 585 } else if (cp < 0x10000) { 586 ret.len = 3; 587 buf[0] = ((cp >> 12) & 0x0F) | 0xE0; 588 buf[1] = ((cp >> 6) & 0x3F) | 0x80; 589 buf[2] = ((cp >> 0) & 0x3F) | 0x80; 590 } else if (cp < 0x200000) { 591 ret.len = 4; 592 buf[0] = ((cp >> 18) & 0x07) | 0xF0; 593 buf[1] = ((cp >> 12) & 0x3F) | 0x80; 594 buf[2] = ((cp >> 6) & 0x3F) | 0x80; 595 buf[3] = ((cp >> 0) & 0x3F) | 0x80; 596 } 597 return ret; 598 } 599 600 #include "extern/utf8_decode.c"