summaryrefslogtreecommitdiff
path: root/util.c
diff options
context:
space:
mode:
Diffstat (limited to 'util.c')
-rw-r--r--util.c263
1 files changed, 263 insertions, 0 deletions
diff --git a/util.c b/util.c
new file mode 100644
index 0000000..aa79ad5
--- /dev/null
+++ b/util.c
@@ -0,0 +1,263 @@
+#if _WIN32
+#include <intrin.h>
+#include <direct.h>
+#elif __unix__
+#include <unistd.h>
+#else
+#error "Unrecognized operating system."
+#endif
+
+static u8 util_popcount(u64 x) {
+#ifdef __GNUC__
+ return (u8)__builtin_popcountll(x);
+#else
+ u8 count = 0;
+ while (x) {
+ x &= x-1;
+ ++count;
+ }
+ return count;
+#endif
+}
+
+static u8 util_count_leading_zeroes(u64 x) {
+#if __GNUC__
+ return (u8)__builtin_clzll(x);
+#elif _WIN32
+ return (u8)__lzcnt64(x);
+#else
+ u8 count = 0;
+ for (int i = 63; i >= 0; --i)
+ if (x & ((u64)1<<i))
+ ++count;
+ return count;
+#endif
+}
+
+static bool util_is_power_of_2(u64 x) {
+ return util_popcount(x) == 1;
+}
+
+// for finding a character in a char32 string
+static char32_t *util_mem32chr(char32_t *s, char32_t c, size_t n) {
+ for (size_t i = 0; i < n; ++i) {
+ if (s[i] == c) {
+ return &s[i];
+ }
+ }
+ return NULL;
+}
+
+static char32_t const *util_mem32chr_const(char32_t const *s, char32_t c, size_t n) {
+ for (size_t i = 0; i < n; ++i) {
+ if (s[i] == c) {
+ return &s[i];
+ }
+ }
+ return NULL;
+}
+
+static bool str_is_prefix(char const *str, char const *prefix) {
+ return strncmp(str, prefix, strlen(prefix)) == 0;
+}
+
+static bool streq(char const *a, char const *b) {
+ return strcmp(a, b) == 0;
+}
+
+// duplicates at most n characters from src
+static char *strn_dup(char const *src, size_t n) {
+ size_t len = strlen(src);
+ if (n > len)
+ n = len;
+ char *ret = malloc(n + 1);
+ if (ret) {
+ memcpy(ret, src, n);
+ ret[n] = 0;
+ }
+ return ret;
+}
+
+// duplicates a null-terminated string. the returned string should be passed to free()
+static char *str_dup(char const *src) {
+ return strn_dup(src, SIZE_MAX);
+}
+
+// like snprintf, but not screwed up on windows
+#define str_printf(str, size, ...) (str)[(size) - 1] = '\0', snprintf((str), (size) - 1, __VA_ARGS__)
+// like snprintf, but the size is taken to be the length of the array str.
+// first, check that str is actually an array
+#define strbuf_printf(str, ...) assert(sizeof str != 4 && sizeof str != 8), \
+ str_printf(str, sizeof str, __VA_ARGS__)
+#define str_catf(str, size, ...) str_printf((str) + strlen(str), (size) - strlen(str), __VA_ARGS__)
+#define strbuf_catf(str, ...) assert(sizeof str != 4 && sizeof str != 8), \
+ str_catf(str, sizeof str, __VA_ARGS__)
+
+// on 16-bit systems, this is 16383. on 32/64-bit systems, this is 1073741823
+// it is unusual to have a string that long.
+#define STRLEN_SAFE_MAX (UINT_MAX >> 2)
+
+// safer version of strncat. dst_sz includes a null terminator.
+static void strn_cat(char *dst, size_t dst_sz, char const *src, size_t src_len) {
+ size_t dst_len = strlen(dst);
+
+ // make sure dst_len + src_len + 1 doesn't overflow
+ if (dst_len > STRLEN_SAFE_MAX || src_len > STRLEN_SAFE_MAX) {
+ assert(0);
+ return;
+ }
+
+ if (dst_len >= dst_sz) {
+ // dst doesn't actually contain a null-terminated string!
+ assert(0);
+ return;
+ }
+
+ if (dst_len + src_len + 1 > dst_sz) {
+ // number of bytes left in dst, not including null terminator
+ size_t n = dst_sz - dst_len - 1;
+ memcpy(dst + dst_len, src, n);
+ dst[dst_sz - 1] = 0; // dst_len + n == dst_sz - 1
+ } else {
+ memcpy(dst + dst_len, src, src_len);
+ dst[dst_len + src_len] = 0;
+ }
+}
+
+// safer version of strcat. dst_sz includes a null terminator.
+static void str_cat(char *dst, size_t dst_sz, char const *src) {
+ strn_cat(dst, dst_sz, src, strlen(src));
+}
+
+// safer version of strncpy. dst_sz includes a null terminator.
+static void str_cpy(char *dst, size_t dst_sz, char const *src) {
+ size_t srclen = strlen(src);
+ size_t n = srclen; // number of bytes to copy
+
+ if (dst_sz == 0) {
+ assert(0);
+ return;
+ }
+
+ if (dst_sz-1 < n)
+ n = dst_sz-1;
+ memcpy(dst, src, n);
+ dst[n] = 0;
+}
+
+#define strbuf_cpy(dst, src) str_cpy(dst, sizeof dst, src)
+
+// advances str to the start of the next UTF8 character
+static void utf8_next_char_const(char const **str) {
+ if (**str) {
+ do {
+ ++*str;
+ } while (((u8)(**str) & 0xC0) == 0x80); // while we are on a continuation byte
+ }
+}
+
+
+static void print_bytes(u8 *bytes, size_t n) {
+ u8 *b, *end;
+ for (b = bytes, end = bytes + n; b != end; ++b)
+ printf("%x ", *b);
+ printf("\n");
+}
+
+/*
+does this predicate hold for all the characters of s. predicate is int (*)(int) instead
+of bool (*)(char) so that you can pass isprint, etc. to it.
+*/
+static bool str_satisfies(char const *s, int (*predicate)(int)) {
+ char const *p;
+ for (p = s; *p; ++p)
+ if (!predicate(*p))
+ return false;
+ return true;
+}
+
+
+static int strcmp_case_insensitive(char const *a, char const *b) {
+#if _WIN32
+ return _stricmp(a, b);
+#else
+ return strcasecmp(a, b);
+#endif
+}
+
+// function to be passed into qsort for case insensitive sorting
+static int str_qsort_case_insensitive_cmp(const void *av, const void *bv) {
+ char const *const *a = av, *const *b = bv;
+ return strcmp_case_insensitive(*a, *b);
+}
+
+static void *qsort_ctx_arg;
+static int (*qsort_ctx_cmp)(void *, const void *, const void *);
+static int qsort_with_context_cmp(const void *a, const void *b) {
+ return qsort_ctx_cmp(qsort_ctx_arg, a, b);
+}
+
+static void qsort_with_context(void *base, size_t nmemb, size_t size, int (*compar)(void *, const void *, const void *), void *arg) {
+ // @TODO(eventually): write this yourself
+ // just use global variables. hopefully we don't try to run this in something multithreaded!
+ qsort_ctx_arg = arg;
+ qsort_ctx_cmp = compar;
+ qsort(base, nmemb, size, qsort_with_context_cmp);
+}
+
+// the actual file name part of the path; get rid of the containing directory.
+// NOTE: the returned string is part of path, so you don't need to free it or anything.
+static char const *path_filename(char const *path) {
+ char const *last_path_sep = strrchr(path, PATH_SEPARATOR);
+ if (last_path_sep)
+ return last_path_sep + 1;
+ // (a relative path with no path separators)
+ return path;
+}
+
+static bool path_is_absolute(char const *path) {
+ return path[0] == PATH_SEPARATOR
+ #if _WIN32
+ || path[1] == ':' && path[2] == PATH_SEPARATOR
+ #endif
+ ;
+}
+
+// assuming `dir` is an absolute path, returns the absolute path of `relpath`, relative to `dir`.
+static void path_full(char const *dir, char const *relpath, char *abspath, size_t abspath_size) {
+ str_cpy(abspath, abspath_size, dir);
+
+ while (1) {
+ size_t component_len = strcspn(relpath, ALL_PATH_SEPARATORS);
+ char const *component_end = relpath + component_len;
+
+ size_t len = strlen(abspath);
+ if (component_len == 1 && relpath[0] == '.') {
+ // ., do nothing
+ } else if (component_len == 2 && relpath[0] == '.' && relpath[1] == '.') {
+ // ..
+ char *lastsep = strrchr(abspath, PATH_SEPARATOR);
+ if (lastsep == abspath)
+ lastsep[1] = '\0';
+ else
+ lastsep[0] = '\0';
+ } else {
+ if (abspath[len - 1] != PATH_SEPARATOR)
+ str_cat(abspath, abspath_size, PATH_SEPARATOR_STR);
+ strn_cat(abspath, abspath_size, relpath, component_len);
+ }
+ if (*component_end == 0)
+ break;
+ else
+ relpath = component_end + 1;
+ }
+}
+
+static void change_directory(char const *path) {
+#if _WIN32
+ _chdir(path);
+#else
+ chdir(path);
+#endif
+}
+