util.c (9814B)
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, uz size) 5 { 6 u8 *p = p_; 7 while (size) 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 u8 * 19 arena_aligned_start(Arena a, uz alignment) 20 { 21 uz padding = -(uintptr_t)a.beg & (alignment - 1); 22 u8 *result = a.beg + padding; 23 return result; 24 } 25 26 function u8 * 27 arena_commit(Arena *a, sz size) 28 { 29 assert(a->end - a->beg >= size); 30 u8 *result = a->beg; 31 a->beg += size; 32 return result; 33 } 34 35 function void 36 arena_pop(Arena *a, sz length) 37 { 38 a->beg -= length; 39 } 40 41 #define push_array(a, t, n) (t *)arena_alloc(a, sizeof(t), _Alignof(t), n) 42 #define push_struct(a, t) (t *)arena_alloc(a, sizeof(t), _Alignof(t), 1) 43 function void * 44 arena_alloc(Arena *a, uz len, uz align, uz count) 45 { 46 void *result = 0; 47 if (a->beg) { 48 u8 *start = arena_aligned_start(*a, align); 49 sz available = a->end - start; 50 assert((available >= 0 && count <= (uz)available / len)); 51 a->beg = start + count * len; 52 /* TODO: Performance? */ 53 result = mem_clear(start, 0, count * len); 54 } 55 return result; 56 } 57 58 enum { DA_INITIAL_CAP = 4 }; 59 #define da_reserve(a, s, n) \ 60 (s)->data = da_reserve_((a), (s)->data, &(s)->capacity, (s)->count + n, \ 61 _Alignof(typeof(*(s)->data)), sizeof(*(s)->data)) 62 63 #define da_push(a, s) \ 64 ((s)->count == (s)->capacity \ 65 ? da_reserve(a, s, 1u), \ 66 (s)->data + (s)->count++ \ 67 : (s)->data + (s)->count++) 68 69 function void * 70 da_reserve_(Arena *a, void *data, sz *capacity, sz needed, uz align, uz size) 71 { 72 uz cap = (uz)*capacity; 73 74 /* NOTE(rnp): handle both 0 initialized DAs and DAs that need to be moved (they started 75 * on the stack or someone allocated something in the middle of the arena during usage) */ 76 if (!data || a->beg != (u8 *)data + cap * size) { 77 void *copy = arena_alloc(a, size, align, cap); 78 if (data) mem_copy(copy, data, cap * size); 79 data = copy; 80 } 81 82 if (!cap) cap = DA_INITIAL_CAP; 83 while (cap < needed) cap *= 2; 84 arena_alloc(a, size, align, cap - (uz)*capacity); 85 *capacity = (sz)cap; 86 return data; 87 } 88 89 function u32 90 utf8_encode(u8 *out, u32 cp) 91 { 92 u32 result = 1; 93 if (cp <= 0x7F) { 94 out[0] = cp & 0x7F; 95 } else if (cp <= 0x7FF) { 96 result = 2; 97 out[0] = ((cp >> 6) & 0x1F) | 0xC0; 98 out[1] = ((cp >> 0) & 0x3F) | 0x80; 99 } else if (cp <= 0xFFFF) { 100 result = 3; 101 out[0] = ((cp >> 12) & 0x0F) | 0xE0; 102 out[1] = ((cp >> 6) & 0x3F) | 0x80; 103 out[2] = ((cp >> 0) & 0x3F) | 0x80; 104 } else if (cp <= 0x10FFFF) { 105 result = 4; 106 out[0] = ((cp >> 18) & 0x07) | 0xF0; 107 out[1] = ((cp >> 12) & 0x3F) | 0x80; 108 out[2] = ((cp >> 6) & 0x3F) | 0x80; 109 out[3] = ((cp >> 0) & 0x3F) | 0x80; 110 } else { 111 out[0] = '?'; 112 } 113 return result; 114 } 115 116 function UnicodeDecode 117 utf16_decode(u16 *data, sz length) 118 { 119 UnicodeDecode result = {.cp = U32_MAX}; 120 if (length) { 121 result.consumed = 1; 122 result.cp = data[0]; 123 if (length > 1 && BETWEEN(data[0], 0xD800, 0xDBFF) 124 && BETWEEN(data[1], 0xDC00, 0xDFFF)) 125 { 126 result.consumed = 2; 127 result.cp = ((data[0] - 0xD800u) << 10) | ((data[1] - 0xDC00u) + 0x10000u); 128 } 129 } 130 return result; 131 } 132 133 function u32 134 utf16_encode(u16 *out, u32 cp) 135 { 136 u32 result = 1; 137 if (cp == U32_MAX) { 138 out[0] = '?'; 139 } else if (cp < 0x10000) { 140 out[0] = (u16)cp; 141 } else { 142 u32 value = cp - 0x10000; 143 out[0] = (u16)(0xD800u + (value >> 10u)); 144 out[1] = (u16)(0xDC00u + (value & 0x3FFu)); 145 result = 2; 146 } 147 return result; 148 } 149 150 function Stream 151 stream_alloc(Arena *a, u32 cap) 152 { 153 Stream result = {.cap = cap}; 154 result.data = push_array(a, u8, cap); 155 return result; 156 } 157 158 function str8 159 stream_to_str8(Stream *s) 160 { 161 str8 result = str8(""); 162 if (!s->errors) result = (str8){.len = s->widx, .data = s->data}; 163 return result; 164 } 165 166 function void 167 stream_reset(Stream *s, u32 index) 168 { 169 s->errors = s->cap <= index; 170 if (!s->errors) 171 s->widx = index; 172 } 173 174 function void 175 stream_commit(Stream *s, sz count) 176 { 177 s->errors |= !BETWEEN(s->widx + count, 0, s->cap); 178 if (!s->errors) 179 s->widx += count; 180 } 181 182 function void 183 stream_append(Stream *s, void *data, sz count) 184 { 185 s->errors |= (s->cap - s->widx) < count; 186 if (!s->errors) { 187 mem_copy(s->data + s->widx, data, (u32)count); 188 s->widx += count; 189 } 190 } 191 192 function void 193 stream_append_byte(Stream *s, u8 b) 194 { 195 stream_append(s, &b, 1); 196 } 197 198 function void 199 stream_append_str8(Stream *s, str8 str) 200 { 201 stream_append(s, str.data, (u32)str.len); 202 } 203 204 #define stream_append_str8s(s, ...) stream_append_str8s_(s, (str8 []){__VA_ARGS__}, \ 205 sizeof((str8 []){__VA_ARGS__}) / sizeof(str8)) 206 function void 207 stream_append_str8s_(Stream *s, str8 *strs, sz count) 208 { 209 for (sz i = 0; i < count; i++) 210 stream_append(s, strs[i].data, (u32)strs[i].len); 211 } 212 213 function void 214 stream_append_u64_width(Stream *s, u64 n, u64 min_width) 215 { 216 u8 tmp[64]; 217 u8 *end = tmp + sizeof(tmp); 218 u8 *beg = end; 219 min_width = MIN(sizeof(tmp), min_width); 220 221 do { *--beg = '0' + (n % 10); } while (n /= 10); 222 while (end - beg > 0 && end - beg < min_width) 223 *--beg = '0'; 224 225 stream_append(s, beg, end - beg); 226 } 227 228 function void 229 stream_append_u64(Stream *s, u64 n) 230 { 231 stream_append_u64_width(s, n, 0); 232 } 233 234 function void 235 stream_append_s64(Stream *s, s64 n) 236 { 237 if (n < 0) { 238 stream_append_byte(s, '-'); 239 n *= -1; 240 } 241 stream_append_u64(s, (u64)n); 242 } 243 244 function void 245 stream_append_f64(Stream *s, f64 f, s64 prec) 246 { 247 if (f < 0) { 248 stream_append_byte(s, '-'); 249 f *= -1; 250 } 251 252 /* NOTE: round last digit */ 253 f += 0.5f / (float)prec; 254 255 if (f >= (f64)(-1UL >> 1)) { 256 stream_append_str8(s, str8("inf")); 257 } else { 258 u64 integral = (u64)f; 259 u64 fraction = (u64)((f - (f64)integral) * (f64)prec); 260 stream_append_u64(s, integral); 261 stream_append_byte(s, '.'); 262 for (s64 i = prec / 10; i > 1; i /= 10) { 263 if (i > fraction) 264 stream_append_byte(s, '0'); 265 } 266 stream_append_u64(s, fraction); 267 } 268 } 269 270 function Stream 271 arena_stream(Arena a) 272 { 273 Stream result = {0}; 274 result.data = a.beg; 275 result.cap = (u32)(a.end - a.beg); 276 return result; 277 } 278 279 function str8 280 arena_stream_commit_zero(Arena *a, Stream *s) 281 { 282 b32 error = s->errors || s->widx == s->cap; 283 if (!error) 284 s->data[s->widx] = 0; 285 str8 result = stream_to_str8(s); 286 arena_commit(a, result.len + 1); 287 return result; 288 } 289 290 function str8 291 arena_stream_commit(Arena *a, Stream *s) 292 { 293 assert(s->data == a->beg); 294 str8 result = stream_to_str8(s); 295 arena_commit(a, result.len); 296 return result; 297 } 298 299 /* NOTE(rnp): FNV-1a hash */ 300 function u64 301 str8_hash(str8 v) 302 { 303 u64 h = 0x3243f6a8885a308d; /* digits of pi */ 304 for (; v.len; v.len--) { 305 h ^= v.data[v.len - 1] & 0xFF; 306 h *= 1111111111111111111; /* random prime */ 307 } 308 return h; 309 } 310 311 function b32 312 c_str_equal(const char *a, const char *b) 313 { 314 b32 result = 0; 315 if (a && b) { 316 while (*a && *b && *a == *b) { a++, b++; } 317 result = *a == 0 && *b == 0; 318 } 319 return result; 320 } 321 322 323 function str8 324 c_str_to_str8(char *cstr) 325 { 326 str8 result = {.data = (u8 *)cstr}; 327 if (cstr) while (*cstr) { cstr++; } 328 result.len = (u8 *)cstr - result.data; 329 return result; 330 } 331 332 /* NOTE(rnp): returns < 0 if byte is not found */ 333 function sz 334 str8_scan_backwards(str8 s, u8 byte) 335 { 336 sz result = s.len; 337 while (result && s.data[result - 1] != byte) result--; 338 result--; 339 return result; 340 } 341 342 function str8 343 str8_trim_trailing(str8 s, u8 byte) 344 { 345 str8 result = s; 346 while (result.len >= 1 && result.data[result.len - 1] == byte) result.len--; 347 return result; 348 } 349 350 function str8 351 str8_cut_head(str8 s, sz cut) 352 { 353 str8 result = s; 354 if (cut > 0) { 355 result.data += cut; 356 result.len -= cut; 357 } 358 return result; 359 } 360 361 function str8 362 str8_alloc(Arena *a, sz len) 363 { 364 str8 result = {.data = push_array(a, u8, (uz)len), .len = len}; 365 return result; 366 } 367 368 function str8 369 str16_to_str8(Arena *a, str16 in) 370 { 371 str8 result = str8(""); 372 if (in.len) { 373 sz commit = in.len * 4; 374 sz length = 0; 375 u8 *data = arena_commit(a, commit + 1); 376 u16 *beg = in.data; 377 u16 *end = in.data + in.len; 378 while (beg < end) { 379 UnicodeDecode decode = utf16_decode(beg, end - beg); 380 length += utf8_encode(data + length, decode.cp); 381 beg += decode.consumed; 382 } 383 data[length] = 0; 384 result = (str8){.len = length, .data = data}; 385 arena_pop(a, commit - length); 386 } 387 return result; 388 } 389 390 function str16 391 str8_to_str16(Arena *a, str8 in) 392 { 393 str16 result = {0}; 394 if (in.len) { 395 sz required = 2 * in.len + 1; 396 u16 *data = push_array(a, u16, (uz)required); 397 sz length = 0; 398 /* TODO(rnp): utf8_decode */ 399 for (sz i = 0; i < in.len; i++) { 400 u32 cp = in.data[i]; 401 length += utf16_encode(data + length, cp); 402 } 403 result = (str16){.len = length, .data = data}; 404 arena_pop(a, required - length); 405 } 406 return result; 407 } 408 409 function str8 410 push_str8(Arena *a, str8 str) 411 { 412 str8 result = str8_alloc(a, str.len); 413 mem_copy(result.data, str.data, (uz)result.len); 414 return result; 415 } 416 417 function str8 418 push_str8_zero(Arena *a, str8 str) 419 { 420 str8 result = str8_alloc(a, str.len + 1); 421 result.len -= 1; 422 mem_copy(result.data, str.data, (uz)result.len); 423 return result; 424 } 425 426 function f64 427 parse_f64(str8 s) 428 { 429 f64 integral = 0, fractional = 0, sign = 1; 430 431 if (s.len && *s.data == '-') { 432 sign = -1; 433 s.data++; 434 s.len--; 435 } 436 437 while (s.len && *s.data != '.') { 438 integral *= 10; 439 integral += *s.data - '0'; 440 s.data++; 441 s.len--; 442 } 443 444 if (*s.data == '.') { s.data++; s.len--; } 445 446 while (s.len) { 447 assert(s.data[s.len - 1] != '.'); 448 fractional /= 10; 449 fractional += (f64)(s.data[--s.len] - '0') / 10.0; 450 } 451 f64 result = sign * (integral + fractional); 452 return result; 453 } 454 455 function FileWatchDirectory * 456 lookup_file_watch_directory(FileWatchContext *ctx, u64 hash) 457 { 458 FileWatchDirectory *result = 0; 459 for (u32 i = 0; i < ctx->count; i++) { 460 FileWatchDirectory *test = ctx->data + i; 461 if (test->hash == hash) { 462 result = test; 463 break; 464 } 465 } 466 return result; 467 }