From 850ab378946e8d6f0818b4ccf8eee413f68dcb95 Mon Sep 17 00:00:00 2001 From: pommicket Date: Sun, 1 Jan 2023 23:11:55 -0500 Subject: start os.h merging --- filesystem-posix.c | 111 -------------------------------------------- filesystem-win.c | 106 ------------------------------------------ filesystem.h | 51 -------------------- main.c | 12 ++--- os-posix.c | 134 +++++++++++++++++++++++++++++++++++++++++++++++++++++ os-win.c | 128 ++++++++++++++++++++++++++++++++++++++++++++++++++ os.h | 79 +++++++++++++++++++++++++++++++ time.c | 92 ------------------------------------ util.c | 22 +++++++++ util.h | 4 ++ 10 files changed, 370 insertions(+), 369 deletions(-) delete mode 100644 filesystem-posix.c delete mode 100644 filesystem-win.c delete mode 100644 filesystem.h create mode 100644 os-posix.c create mode 100644 os-win.c create mode 100644 os.h delete mode 100644 time.c diff --git a/filesystem-posix.c b/filesystem-posix.c deleted file mode 100644 index 5506278..0000000 --- a/filesystem-posix.c +++ /dev/null @@ -1,111 +0,0 @@ -#include "filesystem.h" -#include -#include -#include -#include -#include -#include - -static FsType statbuf_path_type(const struct stat *statbuf) { - if (S_ISREG(statbuf->st_mode)) - return FS_FILE; - if (S_ISDIR(statbuf->st_mode)) - return FS_DIRECTORY; - return FS_OTHER; -} - -FsType fs_path_type(char const *path) { - struct stat statbuf = {0}; - if (stat(path, &statbuf) != 0) - return FS_NON_EXISTENT; - return statbuf_path_type(&statbuf); -} - -FsPermission fs_path_permission(char const *path) { - FsPermission perm = 0; - if (access(path, R_OK) == 0) perm |= FS_PERMISSION_READ; - if (access(path, W_OK) == 0) perm |= FS_PERMISSION_WRITE; - return perm; -} - -bool fs_file_exists(char const *path) { - return fs_path_type(path) == FS_FILE; -} - -FsDirectoryEntry **fs_list_directory(char const *dirname) { - FsDirectoryEntry **entries = NULL; - DIR *dir = opendir(dirname); - if (dir) { - struct dirent *ent; - size_t nentries = 0; - int fd = dirfd(dir); - if (fd != -1) { - while (readdir(dir)) ++nentries; - rewinddir(dir); - entries = (FsDirectoryEntry **)calloc(nentries+1, sizeof *entries); - if (entries) { - size_t idx = 0; - while ((ent = readdir(dir))) { - char const *filename = ent->d_name; - size_t len = strlen(filename); - FsDirectoryEntry *entry = (FsDirectoryEntry *)calloc(1, sizeof *entry + len + 1); - if (!entry) break; - memcpy(entry->name, filename, len); - switch (ent->d_type) { - case DT_REG: - entry->type = FS_FILE; - break; - case DT_DIR: - entry->type = FS_DIRECTORY; - break; - case DT_LNK: // we need to dereference the link - case DT_UNKNOWN: { // information not available directly from dirent, we need to get it ourselves - struct stat statbuf = {0}; - fstatat(fd, filename, &statbuf, 0); - entry->type = statbuf_path_type(&statbuf); - } break; - default: - entry->type = FS_OTHER; - } - if (idx < nentries) // this could actually fail if someone creates files between calculating nentries and here. - entries[idx++] = entry; - } - } - } - closedir(dir); - } - return entries; -} - -int fs_mkdir(char const *path) { - if (mkdir(path, 0755) == 0) { - // directory created successfully - return 1; - } else if (errno == EEXIST) { - struct stat statbuf = {0}; - if (stat(path, &statbuf) == 0) { - if (S_ISDIR(statbuf.st_mode)) { - // already exists, and it's a directory - return 0; - } else { - // already exists, but not a directory - return -1; - } - } else { - return -1; - } - } else { - return -1; - } -} - -int fs_get_cwd(char *buf, size_t buflen) { - assert(buf && buflen); - if (getcwd(buf, buflen)) { - return 1; - } else if (errno == ERANGE) { - return 0; - } else { - return -1; - } -} diff --git a/filesystem-win.c b/filesystem-win.c deleted file mode 100644 index dc17eaf..0000000 --- a/filesystem-win.c +++ /dev/null @@ -1,106 +0,0 @@ -#include "filesystem.h" -#include -#include -#include - -static FsType windows_file_attributes_to_type(DWORD attrs) { - if (attrs == INVALID_FILE_ATTRIBUTES) - return FS_NON_EXISTENT; - else if (attrs & FILE_ATTRIBUTE_DIRECTORY) - return FS_DIRECTORY; - else - return FS_FILE; -} - -FsType fs_path_type(char const *path) { - WCHAR wide_path[4100]; - if (MultiByteToWideChar(CP_UTF8, 0, path, -1, wide_path, sizeof wide_path) == 0) { - return FS_NON_EXISTENT; - } - return windows_file_attributes_to_type(GetFileAttributesW(wide_path)); -} - -FsPermission fs_path_permission(char const *path) { - FsPermission permission = 0; - if (_access(path, 04) == 0) permission |= FS_PERMISSION_READ; - if (_access(path, 02) == 0) permission |= FS_PERMISSION_WRITE; - return permission; -} - -bool fs_file_exists(char const *path) { - return fs_path_type(path) == FS_FILE; -} - -FsDirectoryEntry **fs_list_directory(char const *dirname) { - char file_pattern[1000] = {0}; - FsDirectoryEntry **files = NULL; - WIN32_FIND_DATAW find_data; - HANDLE fhandle; - assert(*dirname); - sprintf_s(file_pattern, sizeof file_pattern, "%s%s*", dirname, - dirname[strlen(dirname) - 1] == PATH_SEPARATOR ? "" : PATH_SEPARATOR_STR); - wchar_t wide_pattern[1024] = {0}; - MultiByteToWideChar(CP_UTF8, 0, file_pattern, -1, wide_pattern, sizeof wide_pattern - 1); - - fhandle = FindFirstFileW(wide_pattern, &find_data); - if (fhandle != INVALID_HANDLE_VALUE) { - // first, figure out number of files - int nfiles = 1, idx = 0; - while (FindNextFileW(fhandle, &find_data)) { - ++nfiles; - } - FindClose(fhandle); - // now, fill out files array - files = calloc(nfiles + 1, sizeof *files); - if (files) { - fhandle = FindFirstFileW(wide_pattern, &find_data); - if (fhandle != INVALID_HANDLE_VALUE) { - do { - if (idx < nfiles) { - LPWSTR wide_filename = find_data.cFileName; - size_t wide_len = wcslen(wide_filename); - size_t cap = 4 * wide_len + 4; - FsDirectoryEntry *entry = calloc(1, sizeof *entry + cap); - - - if (entry) { - if (WideCharToMultiByte(CP_UTF8, 0, wide_filename, (int)wide_len, entry->name, (int)cap - 1, NULL, NULL) == 0) - break; - DWORD attrs = find_data.dwFileAttributes; - entry->type = windows_file_attributes_to_type(attrs); - files[idx++] = entry; - } else break; // stop now - } - } while (FindNextFileW(fhandle, &find_data)); - FindClose(fhandle); - } - } - } - return files; -} - -int fs_mkdir(char const *path) { - WCHAR wide_path[4100]; - if (MultiByteToWideChar(CP_UTF8, 0, path, -1, wide_path, sizeof wide_path) == 0) - return -1; - - if (CreateDirectoryW(wide_path, NULL)) { - // directory created successfully - return 1; - } else { - if (GetLastError() == ERROR_ALREADY_EXISTS) // directory already exists - return 0; - else - return -1; // some other error - } -} - -int fs_get_cwd(char *buf, size_t buflen) { - assert(buf && buflen); - wchar_t wide_path[4100]; - DWORD wide_pathlen = GetCurrentDirectoryW(sizeof wide_path - 1, wide_path); - if (wide_pathlen == 0) return -1; - if (WideCharToMultiByte(CP_UTF8, 0, wide_path, (int)wide_pathlen, buf, (int)buflen, NULL, NULL) == 0) - return 0; - return 1; -} diff --git a/filesystem.h b/filesystem.h deleted file mode 100644 index 03ae04a..0000000 --- a/filesystem.h +++ /dev/null @@ -1,51 +0,0 @@ -#ifndef FILESYSTEM_H_ -#define FILESYSTEM_H_ - -typedef enum { - FS_NON_EXISTENT, - FS_FILE, - FS_DIRECTORY, - FS_OTHER -} FsType; - -enum { - FS_PERMISSION_READ = 0x01, - FS_PERMISSION_WRITE = 0x02, -}; -typedef u8 FsPermission; - -typedef struct { - FsType type; - char name[]; -} FsDirectoryEntry; - -// returns what kind of thing this is. -FsType fs_path_type(char const *path); -FsPermission fs_path_permission(char const *path); -// Does this file exist? Returns false for directories. -bool fs_file_exists(char const *path); -// Returns a NULL-terminated array of the files/directories in this directory, or NULL if the directory does not exist/out of memory. -// When you're done with the entries, call fs_dir_entries_free (or call free on each entry, then on the whole array). -// NOTE: The files/directories aren't returned in any particular order! -FsDirectoryEntry **fs_list_directory(char const *dirname); -// Create the directory specified by `path` -// Returns: -// 1 if the directory was created successfully -// 0 if the directory already exists -// -1 if the path already exists, but it's not a directory, or if there's another error (e.g. don't have permission to create directory). -int fs_mkdir(char const *path); -// Puts the current working directory into buf, including a null-terminator, writing at most buflen bytes. -// Returns: -// 1 if the working directory was inserted into buf successfully -// 0 if buf is too short to hold the cwd -// -1 if we can't get the cwd for whatever reason. -int fs_get_cwd(char *buf, size_t buflen); -// free the entries generated by fs_list_directory.s -static void fs_dir_entries_free(FsDirectoryEntry **entries) { - for (int i = 0; entries[i]; ++i) - free(entries[i]); - free(entries); -} - -#endif // FILESYSTEM_H_ - diff --git a/main.c b/main.c index 9ae5b68..72a9ff7 100644 --- a/main.c +++ b/main.c @@ -7,6 +7,7 @@ - check LSP process status (TEST: what happens if LSP server is not installed) - make tags_dir the root folder - check that tags still works +- do we need higher than 1-second resolution in time_last_modified on windows? - TESTING: make rust-analyzer-slow (waits 10s before sending response) - TESTING: check all IDE features with different servers - run everything through valgrind ideally with leak checking @@ -89,22 +90,15 @@ no_warn_end #include "util.c" #if _WIN32 -#include "filesystem-win.c" -#elif __unix__ -#include "filesystem-posix.c" -#else -#error "Unrecognized operating system." -#endif - -#if _WIN32 +#include "os-win.c" #include "process-win.c" #elif __unix__ +#include "os-posix.c" #include "process-posix.c" #else #error "Unrecognized operating system." #endif -#include "time.c" #include "ted.h" #include "gl.c" #include "text.c" diff --git a/os-posix.c b/os-posix.c new file mode 100644 index 0000000..68e1d4f --- /dev/null +++ b/os-posix.c @@ -0,0 +1,134 @@ +#include "os.h" +#include +#include +#include +#include +#include +#include +#include + +static FsType statbuf_path_type(const struct stat *statbuf) { + if (S_ISREG(statbuf->st_mode)) + return FS_FILE; + if (S_ISDIR(statbuf->st_mode)) + return FS_DIRECTORY; + return FS_OTHER; +} + +FsType fs_path_type(char const *path) { + struct stat statbuf = {0}; + if (stat(path, &statbuf) != 0) + return FS_NON_EXISTENT; + return statbuf_path_type(&statbuf); +} + +FsPermission fs_path_permission(char const *path) { + FsPermission perm = 0; + if (access(path, R_OK) == 0) perm |= FS_PERMISSION_READ; + if (access(path, W_OK) == 0) perm |= FS_PERMISSION_WRITE; + return perm; +} + +bool fs_file_exists(char const *path) { + return fs_path_type(path) == FS_FILE; +} + +FsDirectoryEntry **fs_list_directory(char const *dirname) { + FsDirectoryEntry **entries = NULL; + DIR *dir = opendir(dirname); + if (dir) { + struct dirent *ent; + size_t nentries = 0; + int fd = dirfd(dir); + if (fd != -1) { + while (readdir(dir)) ++nentries; + rewinddir(dir); + entries = (FsDirectoryEntry **)calloc(nentries+1, sizeof *entries); + if (entries) { + size_t idx = 0; + while ((ent = readdir(dir))) { + char const *filename = ent->d_name; + size_t len = strlen(filename); + FsDirectoryEntry *entry = (FsDirectoryEntry *)calloc(1, sizeof *entry + len + 1); + if (!entry) break; + memcpy(entry->name, filename, len); + switch (ent->d_type) { + case DT_REG: + entry->type = FS_FILE; + break; + case DT_DIR: + entry->type = FS_DIRECTORY; + break; + case DT_LNK: // we need to dereference the link + case DT_UNKNOWN: { // information not available directly from dirent, we need to get it ourselves + struct stat statbuf = {0}; + fstatat(fd, filename, &statbuf, 0); + entry->type = statbuf_path_type(&statbuf); + } break; + default: + entry->type = FS_OTHER; + } + if (idx < nentries) // this could actually fail if someone creates files between calculating nentries and here. + entries[idx++] = entry; + } + } + } + closedir(dir); + } + return entries; +} + +int fs_mkdir(char const *path) { + if (mkdir(path, 0755) == 0) { + // directory created successfully + return 1; + } else if (errno == EEXIST) { + struct stat statbuf = {0}; + if (stat(path, &statbuf) == 0) { + if (S_ISDIR(statbuf.st_mode)) { + // already exists, and it's a directory + return 0; + } else { + // already exists, but not a directory + return -1; + } + } else { + return -1; + } + } else { + return -1; + } +} + +int fs_get_cwd(char *buf, size_t buflen) { + assert(buf && buflen); + if (getcwd(buf, buflen)) { + return 1; + } else if (errno == ERANGE) { + return 0; + } else { + return -1; + } +} + +struct timespec time_last_modified(char const *filename) { + struct stat statbuf = {0}; + stat(filename, &statbuf); + return statbuf.st_mtim; +} + +struct timespec time_get(void) { + struct timespec ts = {0}; + clock_gettime(CLOCK_REALTIME, &ts); + return ts; +} + +void time_sleep_ns(u64 ns) { + struct timespec rem = {0}, req = { + (time_t)(ns / 1000000000), + (long)(ns % 1000000000) + }; + + while (nanosleep(&req, &rem) == EINTR) // sleep interrupted by signal + req = rem; +} diff --git a/os-win.c b/os-win.c new file mode 100644 index 0000000..df32cdf --- /dev/null +++ b/os-win.c @@ -0,0 +1,128 @@ +#include "filesystem.h" +#include +#include +#include +#include + +static FsType windows_file_attributes_to_type(DWORD attrs) { + if (attrs == INVALID_FILE_ATTRIBUTES) + return FS_NON_EXISTENT; + else if (attrs & FILE_ATTRIBUTE_DIRECTORY) + return FS_DIRECTORY; + else + return FS_FILE; +} + +FsType fs_path_type(char const *path) { + WCHAR wide_path[4100]; + if (MultiByteToWideChar(CP_UTF8, 0, path, -1, wide_path, sizeof wide_path) == 0) { + return FS_NON_EXISTENT; + } + return windows_file_attributes_to_type(GetFileAttributesW(wide_path)); +} + +FsPermission fs_path_permission(char const *path) { + FsPermission permission = 0; + if (_access(path, 04) == 0) permission |= FS_PERMISSION_READ; + if (_access(path, 02) == 0) permission |= FS_PERMISSION_WRITE; + return permission; +} + +bool fs_file_exists(char const *path) { + return fs_path_type(path) == FS_FILE; +} + +FsDirectoryEntry **fs_list_directory(char const *dirname) { + char file_pattern[1000] = {0}; + FsDirectoryEntry **files = NULL; + WIN32_FIND_DATAW find_data; + HANDLE fhandle; + assert(*dirname); + sprintf_s(file_pattern, sizeof file_pattern, "%s%s*", dirname, + dirname[strlen(dirname) - 1] == PATH_SEPARATOR ? "" : PATH_SEPARATOR_STR); + wchar_t wide_pattern[1024] = {0}; + MultiByteToWideChar(CP_UTF8, 0, file_pattern, -1, wide_pattern, sizeof wide_pattern - 1); + + fhandle = FindFirstFileW(wide_pattern, &find_data); + if (fhandle != INVALID_HANDLE_VALUE) { + // first, figure out number of files + int nfiles = 1, idx = 0; + while (FindNextFileW(fhandle, &find_data)) { + ++nfiles; + } + FindClose(fhandle); + // now, fill out files array + files = calloc(nfiles + 1, sizeof *files); + if (files) { + fhandle = FindFirstFileW(wide_pattern, &find_data); + if (fhandle != INVALID_HANDLE_VALUE) { + do { + if (idx < nfiles) { + LPWSTR wide_filename = find_data.cFileName; + size_t wide_len = wcslen(wide_filename); + size_t cap = 4 * wide_len + 4; + FsDirectoryEntry *entry = calloc(1, sizeof *entry + cap); + + + if (entry) { + if (WideCharToMultiByte(CP_UTF8, 0, wide_filename, (int)wide_len, entry->name, (int)cap - 1, NULL, NULL) == 0) + break; + DWORD attrs = find_data.dwFileAttributes; + entry->type = windows_file_attributes_to_type(attrs); + files[idx++] = entry; + } else break; // stop now + } + } while (FindNextFileW(fhandle, &find_data)); + FindClose(fhandle); + } + } + } + return files; +} + +int fs_mkdir(char const *path) { + WCHAR wide_path[4100]; + if (MultiByteToWideChar(CP_UTF8, 0, path, -1, wide_path, sizeof wide_path) == 0) + return -1; + + if (CreateDirectoryW(wide_path, NULL)) { + // directory created successfully + return 1; + } else { + if (GetLastError() == ERROR_ALREADY_EXISTS) // directory already exists + return 0; + else + return -1; // some other error + } +} + +int fs_get_cwd(char *buf, size_t buflen) { + assert(buf && buflen); + wchar_t wide_path[4100]; + DWORD wide_pathlen = GetCurrentDirectoryW(sizeof wide_path - 1, wide_path); + if (wide_pathlen == 0) return -1; + if (WideCharToMultiByte(CP_UTF8, 0, wide_path, (int)wide_pathlen, buf, (int)buflen, NULL, NULL) == 0) + return 0; + return 1; +} + +struct timespec time_last_modified(char const *filename) { + // windows' _stat does not have st_mtim + struct _stat statbuf = {0}; + struct timespec ts = {0}; + _stat(filename, &statbuf); + ts.tv_sec = statbuf.st_mtime; + return ts; +} + + +struct timespec time_get(void) { + struct timespec ts = {0}; + timespec_get(&ts, TIME_UTC); + return ts; +} + +void time_sleep_ns(u64 ns) { + // windows.... + Sleep((DWORD)(ns / 1000000)); +} diff --git a/os.h b/os.h new file mode 100644 index 0000000..ebf0317 --- /dev/null +++ b/os.h @@ -0,0 +1,79 @@ +#ifndef OS_H_ +#define OS_H_ + +typedef enum { + FS_NON_EXISTENT, + FS_FILE, + FS_DIRECTORY, + FS_OTHER +} FsType; + +enum { + FS_PERMISSION_READ = 0x01, + FS_PERMISSION_WRITE = 0x02, +}; +typedef u8 FsPermission; + +typedef struct { + FsType type; + char name[]; +} FsDirectoryEntry; + +// returns what kind of thing this is. +FsType fs_path_type(char const *path); +FsPermission fs_path_permission(char const *path); +// Does this file exist? Returns false for directories. +bool fs_file_exists(char const *path); +// Returns a NULL-terminated array of the files/directories in this directory, or NULL if the directory does not exist/out of memory. +// When you're done with the entries, call fs_dir_entries_free (or call free on each entry, then on the whole array). +// NOTE: The files/directories aren't returned in any particular order! +FsDirectoryEntry **fs_list_directory(char const *dirname); +// Create the directory specified by `path` +// Returns: +// 1 if the directory was created successfully +// 0 if the directory already exists +// -1 if the path already exists, but it's not a directory, or if there's another error (e.g. don't have permission to create directory). +int fs_mkdir(char const *path); +// Puts the current working directory into buf, including a null-terminator, writing at most buflen bytes. +// Returns: +// 1 if the working directory was inserted into buf successfully +// 0 if buf is too short to hold the cwd +// -1 if we can't get the cwd for whatever reason. +int fs_get_cwd(char *buf, size_t buflen); +struct timespec time_last_modified(char const *filename); +struct timespec time_get(void); +// sleep for a certain number of nanoseconds +void time_sleep_ns(u64 ns); + + +// free the entries generated by fs_list_directory. +static void fs_dir_entries_free(FsDirectoryEntry **entries) { + for (int i = 0; entries[i]; ++i) + free(entries[i]); + free(entries); +} + +static double time_get_seconds(void) { + return timespec_to_seconds(time_get()); +} + + + +// sleep for microseconds +static void time_sleep_us(u64 us) { + time_sleep_ns(us * 1000); +} + +// sleep for milliseconds +static void time_sleep_ms(u64 ms) { + time_sleep_ns(ms * 1000000); +} + +// sleep for seconds +static void time_sleep_s(u64 s) { + time_sleep_ns(s * 1000000000); +} + + +#endif // OS_H_ + diff --git a/time.c b/time.c deleted file mode 100644 index 7e82f0d..0000000 --- a/time.c +++ /dev/null @@ -1,92 +0,0 @@ -#include -#include -#include -#if _WIN32 -#include -#endif - -static struct timespec time_last_modified(char const *filename) { -#if __unix__ - struct stat statbuf = {0}; - stat(filename, &statbuf); - return statbuf.st_mtim; -#else - // windows' _stat does not have st_mtim - struct _stat statbuf = {0}; - struct timespec ts = {0}; - _stat(filename, &statbuf); - ts.tv_sec = statbuf.st_mtime; - return ts; -#endif -} - -static int timespec_cmp(struct timespec a, struct timespec b) { - if (a.tv_sec > b.tv_sec) return 1; - if (a.tv_sec < b.tv_sec) return -1; - if (a.tv_nsec > b.tv_nsec) return 1; - if (a.tv_nsec < b.tv_nsec) return -1; - return 0; -} - -static bool timespec_eq(struct timespec a, struct timespec b) { - return timespec_cmp(a, b) == 0; -} - -static struct timespec timespec_max(struct timespec a, struct timespec b) { - return timespec_cmp(a, b) < 0 ? b : a; -} - -static double timespec_to_seconds(struct timespec ts) { - return (double)ts.tv_sec - + (double)ts.tv_nsec * 1e-9; -} - -static double timespec_sub(struct timespec a, struct timespec b) { - return (double)(a.tv_sec - b.tv_sec) - + (double)(a.tv_nsec - b.tv_nsec) * 1e-9; -} - -static struct timespec time_get(void) { - struct timespec ts = {0}; -#if _WIN32 - timespec_get(&ts, TIME_UTC); -#else - clock_gettime(CLOCK_REALTIME, &ts); -#endif - return ts; -} - -static double time_get_seconds(void) { - return timespec_to_seconds(time_get()); -} - -// sleep for a certain number of nanoseconds -static void time_sleep_ns(u64 ns) { -#if __unix__ - struct timespec rem = {0}, req = { - (time_t)(ns / 1000000000), - (long)(ns % 1000000000) - }; - - while (nanosleep(&req, &rem) == EINTR) // sleep interrupted by signal - req = rem; -#else - // windows.... - Sleep((DWORD)(ns / 1000000)); -#endif -} - -// sleep for microseconds -static void time_sleep_us(u64 us) { - time_sleep_ns(us * 1000); -} - -// sleep for milliseconds -static void time_sleep_ms(u64 ms) { - time_sleep_ns(ms * 1000000); -} - -// sleep for seconds -static void time_sleep_s(u64 s) { - time_sleep_ns(s * 1000000000); -} diff --git a/util.c b/util.c index 39d1e39..f8bdab5 100644 --- a/util.c +++ b/util.c @@ -1228,3 +1228,25 @@ u32 color_interpolate(float x, u32 color1, u32 color2) { c_out = color_hsva_to_rgba(c_out); return rgba_v4_to_u32(c_out); } + + +int timespec_cmp(struct timespec a, struct timespec b) { + if (a.tv_sec > b.tv_sec) return 1; + if (a.tv_sec < b.tv_sec) return -1; + if (a.tv_nsec > b.tv_nsec) return 1; + if (a.tv_nsec < b.tv_nsec) return -1; + return 0; +} + +bool timespec_eq(struct timespec a, struct timespec b) { + return timespec_cmp(a, b) == 0; +} + +struct timespec timespec_max(struct timespec a, struct timespec b) { + return timespec_cmp(a, b) < 0 ? b : a; +} + +double timespec_to_seconds(struct timespec ts) { + return (double)ts.tv_sec + + (double)ts.tv_nsec * 1e-9; +} diff --git a/util.h b/util.h index cbde09c..5645341 100644 --- a/util.h +++ b/util.h @@ -249,5 +249,9 @@ Rect rect_grow(Rect r, float amount); v4 color_rgba_to_hsva(v4 rgba); v4 color_hsva_to_rgba(v4 hsva); u32 color_interpolate(float x, u32 color1, u32 color2); +int timespec_cmp(struct timespec a, struct timespec b); +bool timespec_eq(struct timespec a, struct timespec b); +struct timespec timespec_max(struct timespec a, struct timespec b); +double timespec_to_seconds(struct timespec ts); #endif // UTIL_H_ -- cgit v1.2.3