vtgl

terminal emulator implemented in OpenGL
git clone anongit@rnpnr.xyz:vtgl.git
Log | Files | Refs | Feed | LICENSE

util.c (13869B)


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