ogl_beamforming

Ultrasound Beamforming Implemented with OpenGL
git clone anongit@rnpnr.xyz:ogl_beamforming.git
Log | Files | Refs | Feed | Submodules | README | LICENSE

util.c (18620B)


      1 /* See LICENSE for license details. */
      2 #if   COMPILER_CLANG
      3   #pragma GCC diagnostic ignored "-Winitializer-overrides"
      4 #elif COMPILER_GCC
      5   #pragma GCC diagnostic ignored "-Woverride-init"
      6 #endif
      7 
      8 #define zero_struct(s) mem_clear(s, 0, sizeof(*s))
      9 function void *
     10 mem_clear(void *restrict p_, u8 c, iz size)
     11 {
     12 	u8 *p = p_;
     13 	while (size > 0) p[--size] = c;
     14 	return p;
     15 }
     16 
     17 function void
     18 mem_copy(void *restrict dest, void *restrict src, uz n)
     19 {
     20 	u8 *s = src, *d = dest;
     21 	#ifdef __AVX512BW__
     22 	{
     23 		for (; n >= 64; n -= 64, s += 64, d += 64)
     24 			_mm512_storeu_epi8(d, _mm512_loadu_epi8(s));
     25 		__mmask64 k = _cvtu64_mask64(_bzhi_u64(-1, n));
     26 		_mm512_mask_storeu_epi8(d, k, _mm512_maskz_loadu_epi8(k, s));
     27 	}
     28 	#else
     29 		for (; n; n--) *d++ = *s++;
     30 	#endif
     31 }
     32 
     33 /* IMPORTANT: this function may fault if dest, src, and n are not multiples of 64 */
     34 function void
     35 memory_copy_non_temporal(void *restrict dest, void *restrict src, uz n)
     36 {
     37 	assume(((u64)dest & 63) == 0);
     38 	assume(((u64)src  & 63) == 0);
     39 	assume(((u64)n    & 63) == 0);
     40 	uint8_t *s = src, *d = dest;
     41 
     42 	#if defined(__AVX512BW__)
     43 	{
     44 		for (; n >= 64; n -= 64, s += 64, d += 64)
     45 			_mm512_stream_si512((__m512i *)d, _mm512_stream_load_si512((__m512i *)s));
     46 	}
     47 	#elif defined(__AVX2__)
     48 	{
     49 		for (; n >= 32; n -= 32, s += 32, d += 32)
     50 			_mm256_stream_si256((__m256i *)d, _mm256_stream_load_si256((__m256i *)s));
     51 	}
     52 	#elif ARCH_ARM64 && !COMPILER_MSVC
     53 	{
     54 		asm volatile (
     55 			"cbz  %2, 2f\n"
     56 			"1: ldnp q0, q1, [%1]\n"
     57 			"subs %2, %2, #32\n"
     58 			"add  %1, %1, #32\n"
     59 			"stnp q0, q1, [%0]\n"
     60 			"add  %0, %0, #32\n"
     61 			"b.ne 1b\n"
     62 			"2:"
     63 			:  "+r"(d), "+r"(s), "+r"(n)
     64 			:: "memory", "v0", "v1"
     65 		);
     66 	}
     67 	#else
     68 		mem_copy(d, s, n);
     69 	#endif
     70 }
     71 
     72 function void
     73 mem_move(u8 *dest, u8 *src, uz n)
     74 {
     75 	if (dest < src) mem_copy(dest, src, n);
     76 	else            while (n) { n--; dest[n] = src[n]; }
     77 }
     78 
     79 function void *
     80 memory_scan_backwards(void *memory, u8 byte, iz n)
     81 {
     82 	void *result = 0;
     83 	u8   *s      = memory;
     84 	while (n > 0) if (s[--n] == byte) { result = s + n; break; }
     85 	return result;
     86 }
     87 
     88 function Arena
     89 arena_from_memory(void *memory, u64 size)
     90 {
     91 	Arena result;
     92 	result.beg = memory;
     93 	result.end = result.beg + size;
     94 	return result;
     95 }
     96 
     97 function void *
     98 align_pointer_up(void *p, uz alignment)
     99 {
    100 	uz padding = -(uintptr_t)p & (alignment - 1);
    101 	void *result = (u8 *)p + padding;
    102 	return result;
    103 }
    104 
    105 function void *
    106 arena_aligned_start(Arena a, uz alignment)
    107 {
    108 	return align_pointer_up(a.beg, alignment);
    109 }
    110 
    111 #define arena_capacity(a, t) arena_capacity_(a, sizeof(t), alignof(t))
    112 function iz
    113 arena_capacity_(Arena *a, iz size, uz alignment)
    114 {
    115 	iz available = a->end - (u8 *)arena_aligned_start(*a, alignment);
    116 	iz result    = available / size;
    117 	return result;
    118 }
    119 
    120 function u8 *
    121 arena_commit(Arena *a, iz size)
    122 {
    123 	assert(a->end - a->beg >= size);
    124 	u8 *result = a->beg;
    125 	a->beg += size;
    126 	return result;
    127 }
    128 
    129 function void
    130 arena_pop(Arena *a, iz length)
    131 {
    132 	a->beg -= length;
    133 }
    134 
    135 typedef enum {
    136 	ArenaAllocateFlags_NoZero = 1 << 0,
    137 } ArenaAllocateFlags;
    138 
    139 typedef struct {
    140 	iz size;
    141 	uz align;
    142 	iz count;
    143 	ArenaAllocateFlags flags;
    144 } ArenaAllocateInfo;
    145 
    146 #define arena_alloc(a, ...)         arena_alloc_(a, (ArenaAllocateInfo){.align = 8, .count = 1, ##__VA_ARGS__})
    147 #define push_array(a, t, n)         (t *)arena_alloc(a, .size = sizeof(t), .align = alignof(t), .count = n)
    148 #define push_array_no_zero(a, t, n) (t *)arena_alloc(a, .size = sizeof(t), .align = alignof(t), .count = n, .flags = ArenaAllocateFlags_NoZero)
    149 #define push_struct(a, t)           push_array(a, t, 1)
    150 #define push_struct_no_zero(a, t)   push_array_no_zero(a, t, 1)
    151 
    152 function void *
    153 arena_alloc_(Arena *a, ArenaAllocateInfo info)
    154 {
    155 	void *result = 0;
    156 	if (a->beg) {
    157 		u8 *start = arena_aligned_start(*a, info.align);
    158 		iz available = a->end - start;
    159 		assert((available >= 0 && info.count <= available / info.size));
    160 		asan_unpoison_region(start, info.count * info.size);
    161 		a->beg = start + info.count * info.size;
    162 		result = start;
    163 		if ((info.flags & ArenaAllocateFlags_NoZero) == 0)
    164 			result = mem_clear(start, 0, info.count * info.size);
    165 	}
    166 	return result;
    167 }
    168 
    169 function Arena
    170 sub_arena(Arena *a, iz size, uz align)
    171 {
    172 	Arena result = {.beg = arena_alloc(a, .size = size, .align = align, .flags = ArenaAllocateFlags_NoZero)};
    173 	result.end   = result.beg + size;
    174 	return result;
    175 }
    176 
    177 function Arena
    178 sub_arena_end(Arena *a, iz len, uz align)
    179 {
    180 	Arena result;
    181 	result.beg = (u8 *)((uintptr_t)(a->end - len) & ~(align - 1)),
    182 	result.end = a->end,
    183 
    184 	a->end = result.beg;
    185 	assert(a->end >= a->beg);
    186 
    187 	return result;
    188 }
    189 
    190 function TempArena
    191 begin_temp_arena(Arena *a)
    192 {
    193 	TempArena result = {.arena = a, .original_arena = *a};
    194 	return result;
    195 }
    196 
    197 function void
    198 end_temp_arena(TempArena ta)
    199 {
    200 	Arena *a = ta.arena;
    201 	if (a) {
    202 		assert(a->beg >= ta.original_arena.beg);
    203 		*a = ta.original_arena;
    204 	}
    205 }
    206 
    207 
    208 enum { DA_INITIAL_CAP = 16 };
    209 
    210 #define da_index(it, s) ((it) - (s)->data)
    211 #define da_reserve(a, s, n) \
    212   (s)->data = da_reserve_((a), (s)->data, &(s)->capacity, (s)->count + n, \
    213                           _Alignof(typeof(*(s)->data)), sizeof(*(s)->data))
    214 
    215 #define da_append_count(a, s, items, item_count) do { \
    216 	da_reserve((a), (s), (item_count));                                             \
    217 	mem_copy((s)->data + (s)->count, (items), sizeof(*(items)) * (uz)(item_count)); \
    218 	(s)->count += (item_count);                                                     \
    219 } while (0)
    220 
    221 #define da_push(a, s) \
    222   ((s)->count == (s)->capacity  \
    223     ? da_reserve(a, s, 1),      \
    224       (s)->data + (s)->count++  \
    225     : (s)->data + (s)->count++)
    226 
    227 function void *
    228 da_reserve_(Arena *a, void *data, iz *capacity, iz needed, uz align, iz size)
    229 {
    230 	iz cap = *capacity;
    231 
    232 	/* NOTE(rnp): handle both 0 initialized DAs and DAs that need to be moved (they started
    233 	 * on the stack or someone allocated something in the middle of the arena during usage) */
    234 	if (!data || a->beg != (u8 *)data + cap * size) {
    235 		void *copy = arena_alloc(a, .size = size, .align = align, .count = cap);
    236 		if (data) mem_copy(copy, data, (uz)(cap * size));
    237 		data = copy;
    238 	}
    239 
    240 	if (!cap) cap = DA_INITIAL_CAP;
    241 	while (cap < needed) cap *= 2;
    242 	arena_alloc(a, .size = size, .align = align, .count = cap - *capacity);
    243 	*capacity = cap;
    244 	return data;
    245 }
    246 
    247 function u32
    248 utf8_encode(u8 *out, u32 cp)
    249 {
    250 	u32 result = 1;
    251 	if (cp <= 0x7F) {
    252 		out[0] = cp & 0x7F;
    253 	} else if (cp <= 0x7FF) {
    254 		result = 2;
    255 		out[0] = ((cp >>  6) & 0x1F) | 0xC0;
    256 		out[1] = ((cp >>  0) & 0x3F) | 0x80;
    257 	} else if (cp <= 0xFFFF) {
    258 		result = 3;
    259 		out[0] = ((cp >> 12) & 0x0F) | 0xE0;
    260 		out[1] = ((cp >>  6) & 0x3F) | 0x80;
    261 		out[2] = ((cp >>  0) & 0x3F) | 0x80;
    262 	} else if (cp <= 0x10FFFF) {
    263 		result = 4;
    264 		out[0] = ((cp >> 18) & 0x07) | 0xF0;
    265 		out[1] = ((cp >> 12) & 0x3F) | 0x80;
    266 		out[2] = ((cp >>  6) & 0x3F) | 0x80;
    267 		out[3] = ((cp >>  0) & 0x3F) | 0x80;
    268 	} else {
    269 		out[0] = '?';
    270 	}
    271 	return result;
    272 }
    273 
    274 function UnicodeDecode
    275 utf16_decode(u16 *data, iz length)
    276 {
    277 	UnicodeDecode result = {.cp = U32_MAX};
    278 	if (length) {
    279 		result.consumed = 1;
    280 		result.cp = data[0];
    281 		if (length > 1 && BETWEEN(data[0], 0xD800u, 0xDBFFu)
    282 		               && BETWEEN(data[1], 0xDC00u, 0xDFFFu))
    283 		{
    284 			result.consumed = 2;
    285 			result.cp = ((data[0] - 0xD800u) << 10u) | ((data[1] - 0xDC00u) + 0x10000u);
    286 		}
    287 	}
    288 	return result;
    289 }
    290 
    291 function u32
    292 utf16_encode(u16 *out, u32 cp)
    293 {
    294 	u32 result = 1;
    295 	if (cp == U32_MAX) {
    296 		out[0] = '?';
    297 	} else if (cp < 0x10000u) {
    298 		out[0] = (u16)cp;
    299 	} else {
    300 		u32 value = cp - 0x10000u;
    301 		out[0] = (u16)(0xD800u + (value >> 10u));
    302 		out[1] = (u16)(0xDC00u + (value & 0x3FFu));
    303 		result = 2;
    304 	}
    305 	return result;
    306 }
    307 
    308 function Stream
    309 stream_from_buffer(u8 *buffer, u32 capacity)
    310 {
    311 	Stream result = {.data = buffer, .cap = (i32)capacity};
    312 	return result;
    313 }
    314 
    315 function Stream
    316 stream_alloc(Arena *a, i32 cap)
    317 {
    318 	Stream result = stream_from_buffer(arena_commit(a, cap), (u32)cap);
    319 	return result;
    320 }
    321 
    322 function s8
    323 stream_to_s8(Stream *s)
    324 {
    325 	s8 result = s8("");
    326 	if (!s->errors) result = (s8){.len = s->widx, .data = s->data};
    327 	return result;
    328 }
    329 
    330 function void
    331 stream_reset(Stream *s, i32 index)
    332 {
    333 	s->errors = s->cap <= index;
    334 	if (!s->errors)
    335 		s->widx = index;
    336 }
    337 
    338 function void
    339 stream_commit(Stream *s, i32 count)
    340 {
    341 	s->errors |= !BETWEEN(s->widx + count, 0, s->cap);
    342 	if (!s->errors)
    343 		s->widx += count;
    344 }
    345 
    346 function void
    347 stream_append(Stream *s, void *data, iz count)
    348 {
    349 	s->errors |= (s->cap - s->widx) < count;
    350 	if (!s->errors) {
    351 		mem_copy(s->data + s->widx, data, (uz)count);
    352 		s->widx += (i32)count;
    353 	}
    354 }
    355 
    356 function void
    357 stream_append_byte(Stream *s, u8 b)
    358 {
    359 	stream_append(s, &b, 1);
    360 }
    361 
    362 function void
    363 stream_pad(Stream *s, u8 b, i32 n)
    364 {
    365 	while (n > 0) stream_append_byte(s, b), n--;
    366 }
    367 
    368 function void
    369 stream_append_s8(Stream *s, s8 str)
    370 {
    371 	stream_append(s, str.data, str.len);
    372 }
    373 
    374 #define stream_append_s8s(s, ...) stream_append_s8s_(s, arg_list(s8, ##__VA_ARGS__))
    375 function void
    376 stream_append_s8s_(Stream *s, s8 *strs, iz count)
    377 {
    378 	for (iz i = 0; i < count; i++)
    379 		stream_append(s, strs[i].data, strs[i].len);
    380 }
    381 
    382 function void
    383 stream_append_u64_width(Stream *s, u64 n, u64 min_width)
    384 {
    385 	u8 tmp[64];
    386 	u8 *end = tmp + sizeof(tmp);
    387 	u8 *beg = end;
    388 	min_width = MIN(sizeof(tmp), min_width);
    389 
    390 	do { *--beg = (u8)('0' + (n % 10)); } while (n /= 10);
    391 	while (end - beg > 0 && (uz)(end - beg) < min_width)
    392 		*--beg = '0';
    393 
    394 	stream_append(s, beg, end - beg);
    395 }
    396 
    397 function void
    398 stream_append_u64(Stream *s, u64 n)
    399 {
    400 	stream_append_u64_width(s, n, 0);
    401 }
    402 
    403 function void
    404 stream_append_hex_u64_width(Stream *s, u64 n, iz width)
    405 {
    406 	assert(width <= 16);
    407 	if (!s->errors) {
    408 		u8  buf[16];
    409 		u8 *end = buf + sizeof(buf);
    410 		u8 *beg = end;
    411 		while (n) {
    412 			*--beg = (u8)"0123456789abcdef"[n & 0x0F];
    413 			n >>= 4;
    414 		}
    415 		while (end - beg < width)
    416 			*--beg = '0';
    417 		stream_append(s, beg, end - beg);
    418 	}
    419 }
    420 
    421 function void
    422 stream_append_hex_u64(Stream *s, u64 n)
    423 {
    424 	stream_append_hex_u64_width(s, n, 2);
    425 }
    426 
    427 function void
    428 stream_append_i64(Stream *s, i64 n)
    429 {
    430 	if (n < 0) {
    431 		stream_append_byte(s, '-');
    432 		n *= -1;
    433 	}
    434 	stream_append_u64(s, (u64)n);
    435 }
    436 
    437 function void
    438 stream_append_f64(Stream *s, f64 f, u64 prec)
    439 {
    440 	if (f < 0) {
    441 		stream_append_byte(s, '-');
    442 		f *= -1;
    443 	}
    444 
    445 	/* NOTE: round last digit */
    446 	f += 0.5f / (f64)prec;
    447 
    448 	if (f >= (f64)(-1UL >> 1)) {
    449 		stream_append_s8(s, s8("inf"));
    450 	} else {
    451 		u64 integral = (u64)f;
    452 		u64 fraction = (u64)((f - (f64)integral) * (f64)prec);
    453 		stream_append_u64(s, integral);
    454 		stream_append_byte(s, '.');
    455 		for (u64 i = prec / 10; i > 1; i /= 10) {
    456 			if (i > fraction)
    457 				stream_append_byte(s, '0');
    458 		}
    459 		stream_append_u64(s, fraction);
    460 	}
    461 }
    462 
    463 function void
    464 stream_append_f64_e(Stream *s, f64 f)
    465 {
    466 	/* TODO: there should be a better way of doing this */
    467 	#if 0
    468 	/* NOTE: we ignore subnormal numbers for now */
    469 	union { f64 f; u64 u; } u = {.f = f};
    470 	i32 exponent = ((u.u >> 52) & 0x7ff) - 1023;
    471 	f32 log_10_of_2 = 0.301f;
    472 	i32 scale       = (exponent * log_10_of_2);
    473 	/* NOTE: normalize f */
    474 	for (i32 i = ABS(scale); i > 0; i--)
    475 		f *= (scale > 0)? 0.1f : 10.0f;
    476 	#else
    477 	i32 scale = 0;
    478 	if (f != 0) {
    479 		while (f > 1) {
    480 			f *= 0.1f;
    481 			scale++;
    482 		}
    483 		while (f < 1) {
    484 			f *= 10.0f;
    485 			scale--;
    486 		}
    487 	}
    488 	#endif
    489 
    490 	u32 prec = 100;
    491 	stream_append_f64(s, f, prec);
    492 	stream_append_byte(s, 'e');
    493 	stream_append_byte(s, scale >= 0? '+' : '-');
    494 	for (u32 i = prec / 10; i > 1; i /= 10)
    495 		stream_append_byte(s, '0');
    496 	stream_append_u64(s, (u64)ABS(scale));
    497 }
    498 
    499 function void
    500 stream_append_v2(Stream *s, v2 v)
    501 {
    502 	stream_append_byte(s, '{');
    503 	stream_append_f64(s, v.x, 100);
    504 	stream_append_s8(s, s8(", "));
    505 	stream_append_f64(s, v.y, 100);
    506 	stream_append_byte(s, '}');
    507 }
    508 
    509 function Stream
    510 arena_stream(Arena a)
    511 {
    512 	Stream result = {0};
    513 	result.data   = a.beg;
    514 	result.cap    = (i32)(a.end - a.beg);
    515 
    516 	/* TODO(rnp): no idea what to do here if we want to maintain the ergonomics */
    517 	asan_unpoison_region(result.data, result.cap);
    518 
    519 	return result;
    520 }
    521 
    522 function s8
    523 arena_stream_commit(Arena *a, Stream *s)
    524 {
    525 	ASSERT(s->data == a->beg);
    526 	s8 result = stream_to_s8(s);
    527 	arena_commit(a, result.len);
    528 	return result;
    529 }
    530 
    531 function s8
    532 arena_stream_commit_zero(Arena *a, Stream *s)
    533 {
    534 	b32 error = s->errors || s->widx == s->cap;
    535 	if (!error)
    536 		s->data[s->widx] = 0;
    537 	s8 result = stream_to_s8(s);
    538 	arena_commit(a, result.len + 1);
    539 	return result;
    540 }
    541 
    542 function s8
    543 arena_stream_commit_and_reset(Arena *arena, Stream *s)
    544 {
    545 	s8 result = arena_stream_commit_zero(arena, s);
    546 	*s = arena_stream(*arena);
    547 	return result;
    548 }
    549 
    550 #if !defined(XXH_IMPLEMENTATION)
    551 # define XXH_INLINE_ALL
    552 # define XXH_IMPLEMENTATION
    553 # define XXH_STATIC_LINKING_ONLY
    554 # include "external/xxhash.h"
    555 #endif
    556 
    557 function u128
    558 u128_hash_from_data(void *data, uz size)
    559 {
    560 	u128 result = {0};
    561 	XXH128_hash_t hash = XXH3_128bits_withSeed(data, size, 4969);
    562 	mem_copy(&result, &hash, sizeof(result));
    563 	return result;
    564 }
    565 
    566 function u64
    567 u64_hash_from_s8(s8 v)
    568 {
    569 	u64 result = XXH3_64bits_withSeed(v.data, (uz)v.len, 4969);
    570 	return result;
    571 }
    572 
    573 function s8
    574 c_str_to_s8(char *cstr)
    575 {
    576 	s8 result = {.data = (u8 *)cstr};
    577 	if (cstr) { while (*cstr) { result.len++; cstr++; } }
    578 	return result;
    579 }
    580 
    581 /* NOTE(rnp): returns < 0 if byte is not found */
    582 function iz
    583 s8_scan_backwards(s8 s, u8 byte)
    584 {
    585 	iz result = (u8 *)memory_scan_backwards(s.data, byte, s.len) - s.data;
    586 	return result;
    587 }
    588 
    589 function s8
    590 s8_cut_head(s8 s, iz cut)
    591 {
    592 	s8 result = s;
    593 	if (cut > 0) {
    594 		result.data += cut;
    595 		result.len  -= cut;
    596 	}
    597 	return result;
    598 }
    599 
    600 function s8
    601 s8_alloc(Arena *a, iz len)
    602 {
    603 	s8 result = {.data = push_array(a, u8, len), .len = len};
    604 	return result;
    605 }
    606 
    607 function s8
    608 s16_to_s8(Arena *a, s16 in)
    609 {
    610 	s8 result = s8("");
    611 	if (in.len) {
    612 		iz commit = in.len * 4;
    613 		iz length = 0;
    614 		u8 *data = arena_commit(a, commit + 1);
    615 		u16 *beg = in.data;
    616 		u16 *end = in.data + in.len;
    617 		while (beg < end) {
    618 			UnicodeDecode decode = utf16_decode(beg, end - beg);
    619 			length += utf8_encode(data + length, decode.cp);
    620 			beg    += decode.consumed;
    621 		}
    622 		data[length] = 0;
    623 		result = (s8){.len = length, .data = data};
    624 		arena_pop(a, commit - length);
    625 	}
    626 	return result;
    627 }
    628 
    629 function s16
    630 s8_to_s16(Arena *a, s8 in)
    631 {
    632 	s16 result = {0};
    633 	if (in.len) {
    634 		iz required = 2 * in.len + 1;
    635 		u16 *data   = push_array(a, u16, required);
    636 		iz length   = 0;
    637 		/* TODO(rnp): utf8_decode */
    638 		for (iz i = 0; i < in.len; i++) {
    639 			u32 cp  = in.data[i];
    640 			length += utf16_encode(data + length, cp);
    641 		}
    642 		result = (s16){.len = length, .data = data};
    643 		arena_pop(a, required - length);
    644 	}
    645 	return result;
    646 }
    647 
    648 #define push_s8_from_parts(a, j, ...) push_s8_from_parts_((a), (j), arg_list(s8, __VA_ARGS__))
    649 function s8
    650 push_s8_from_parts_(Arena *arena, s8 joiner, s8 *parts, iz count)
    651 {
    652 	iz length = joiner.len * (count - 1);
    653 	for (iz i = 0; i < count; i++)
    654 		length += parts[i].len;
    655 
    656 	s8 result = {.len = length, .data = arena_commit(arena, length + 1)};
    657 
    658 	iz offset = 0;
    659 	for (iz i = 0; i < count; i++) {
    660 		if (i != 0) {
    661 			mem_copy(result.data + offset, joiner.data, (uz)joiner.len);
    662 			offset += joiner.len;
    663 		}
    664 		mem_copy(result.data + offset, parts[i].data, (uz)parts[i].len);
    665 		offset += parts[i].len;
    666 	}
    667 	result.data[result.len] = 0;
    668 
    669 	return result;
    670 }
    671 
    672 function s8
    673 push_s8(Arena *a, s8 str)
    674 {
    675 	s8 result   = s8_alloc(a, str.len + 1);
    676 	result.len -= 1;
    677 	mem_copy(result.data, str.data, (uz)result.len);
    678 	return result;
    679 }
    680 
    681 function force_inline u32
    682 round_down_power_of_2(u32 a)
    683 {
    684 	u32 result = 0x80000000UL >> clz_u32(a);
    685 	return result;
    686 }
    687 
    688 function force_inline u32
    689 round_up_power_of_2(u32 a)
    690 {
    691 	u32 result = 0x80000000UL >> (clz_u32(a - 1) - 1);
    692 	return result;
    693 }
    694 
    695 function force_inline iz
    696 round_up_to(iz value, iz multiple)
    697 {
    698 	iz result = value;
    699 	if (value % multiple != 0)
    700 		result += multiple - value % multiple;
    701 	return result;
    702 }
    703 
    704 function void
    705 split_rect_horizontal(Rect rect, f32 fraction, Rect *left, Rect *right)
    706 {
    707 	if (left) {
    708 		left->pos    = rect.pos;
    709 		left->size.h = rect.size.h;
    710 		left->size.w = rect.size.w * fraction;
    711 	}
    712 	if (right) {
    713 		right->pos    = rect.pos;
    714 		right->pos.x += rect.size.w * fraction;
    715 		right->size.h = rect.size.h;
    716 		right->size.w = rect.size.w * (1.0f - fraction);
    717 	}
    718 }
    719 
    720 function void
    721 split_rect_vertical(Rect rect, f32 fraction, Rect *top, Rect *bot)
    722 {
    723 	if (top) {
    724 		top->pos    = rect.pos;
    725 		top->size.w = rect.size.w;
    726 		top->size.h = rect.size.h * fraction;
    727 	}
    728 	if (bot) {
    729 		bot->pos    = rect.pos;
    730 		bot->pos.y += rect.size.h * fraction;
    731 		bot->size.w = rect.size.w;
    732 		bot->size.h = rect.size.h * (1.0f - fraction);
    733 	}
    734 }
    735 
    736 function void
    737 cut_rect_horizontal(Rect rect, f32 at, Rect *left, Rect *right)
    738 {
    739 	at = MIN(at, rect.size.w);
    740 	if (left) {
    741 		*left = rect;
    742 		left->size.w = at;
    743 	}
    744 	if (right) {
    745 		*right = rect;
    746 		right->pos.x  += at;
    747 		right->size.w -= at;
    748 	}
    749 }
    750 
    751 function void
    752 cut_rect_vertical(Rect rect, f32 at, Rect *top, Rect *bot)
    753 {
    754 	at = MIN(at, rect.size.h);
    755 	if (top) {
    756 		*top = rect;
    757 		top->size.h = at;
    758 	}
    759 	if (bot) {
    760 		*bot = rect;
    761 		bot->pos.y  += at;
    762 		bot->size.h -= at;
    763 	}
    764 }
    765 
    766 function IntegerConversion
    767 integer_from_s8(s8 raw)
    768 {
    769 	read_only local_persist alignas(64) i8 lut[64] = {
    770 		 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, -1, -1, -1, -1, -1, -1,
    771 		-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    772 		-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    773 		-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    774 	};
    775 
    776 	IntegerConversion result = {.unparsed = raw};
    777 
    778 	iz  i     = 0;
    779 	i64 scale = 1;
    780 	if (raw.len > 0 && raw.data[0] == '-') {
    781 		scale = -1;
    782 		i     =  1;
    783 	}
    784 
    785 	b32 hex = 0;
    786 	if (raw.len - i > 2 && raw.data[i] == '0' && (raw.data[1] == 'x' || raw.data[1] == 'X')) {
    787 		hex = 1;
    788 		i += 2;
    789 	}
    790 
    791 	#define integer_conversion_body(radix, clamp) do {\
    792 		for (; i < raw.len; i++) {\
    793 			i64 value = lut[Min((u8)(raw.data[i] - (u8)'0'), clamp)];\
    794 			if (value >= 0) {\
    795 				if (result.U64 > (U64_MAX - (u64)value) / radix) {\
    796 					result.result = IntegerConversionResult_OutOfRange;\
    797 					result.U64    = U64_MAX;\
    798 					return result;\
    799 				} else {\
    800 					result.U64 = radix * result.U64 + (u64)value;\
    801 				}\
    802 			} else {\
    803 				break;\
    804 			}\
    805 		}\
    806 	} while (0)
    807 
    808 	if (hex) integer_conversion_body(16u, 63u);
    809 	else     integer_conversion_body(10u, 15u);
    810 
    811 	#undef integer_conversion_body
    812 
    813 	result.unparsed = (s8){.len = raw.len - i, .data = raw.data + i};
    814 	result.result   = IntegerConversionResult_Success;
    815 	if (scale < 0) result.U64 = 0 - result.U64;
    816 
    817 	return result;
    818 }
    819 
    820 function f64
    821 parse_f64(s8 s)
    822 {
    823 	IntegerConversion integral = integer_from_s8(s);
    824 
    825 	s = integral.unparsed;
    826 	if (*s.data == '.') { s.data++; s.len--; }
    827 	while (s.len > 0 && s.data[s.len - 1] == '0') s.len--;
    828 
    829 	IntegerConversion fractional = integer_from_s8(s);
    830 
    831 	u64 power = (u64)(fractional.unparsed.data - s.data);
    832 	f64 frac  = (f64)fractional.U64;
    833 	while (power > 0) { frac /= 10.0; power--; }
    834 
    835 	f64 result = (f64)integral.S64 + frac;
    836 	return result;
    837 }