Dqn/dqn_platform.h

492 lines
22 KiB
C

#if !defined(DQN_NO_FS)
#if defined(DQN_OS_WIN32) && defined(DQN_NO_WIN)
#error "Filesystem APIs requires Windows API, DQN_NO_WIN must not be defined"
#endif
// NOTE: [$FSYS] Dqn_Fs ============================================================================
// NOTE: FS Manipulation ===========================================================================
// TODO(dqn): We should have a Dqn_String8 interface and a CString interface
//
// NOTE: API =======================================================================================
// @proc Dqn_FsDelete
// @desc Delete the item specified at the path. This function *CAN* not delete directories unless
// the directory is empty.
// @return True if deletion was successful, false otherwise
enum Dqn_FsInfoType
{
Dqn_FsInfoType_Unknown,
Dqn_FsInfoType_Directory,
Dqn_FsInfoType_File,
};
struct Dqn_FsInfo
{
bool exists;
Dqn_FsInfoType type;
uint64_t create_time_in_s;
uint64_t last_write_time_in_s;
uint64_t last_access_time_in_s;
uint64_t size;
};
DQN_API bool Dqn_Fs_Exists (Dqn_String8 path);
DQN_API bool Dqn_Fs_DirExists(Dqn_String8 path);
DQN_API Dqn_FsInfo Dqn_Fs_GetInfo (Dqn_String8 path);
DQN_API bool Dqn_Fs_Copy (Dqn_String8 src, Dqn_String8 dest, bool overwrite);
DQN_API bool Dqn_Fs_MakeDir (Dqn_String8 path);
DQN_API bool Dqn_Fs_Move (Dqn_String8 src, Dqn_String8 dest, bool overwrite);
DQN_API bool Dqn_Fs_Delete (Dqn_String8 path);
// NOTE: R/W Entire File ===========================================================================
// NOTE: API =======================================================================================
// @proc Dqn_Fs_WriteString8, Dqn_Fs_WriteCString8
// @desc Write the string to a file at the path overwriting if necessary.
// @proc Dqn_Fs_ReadString8, Dqn_Fs_ReadCString8
// @desc Read the file at the path to a string.
DQN_API bool Dqn_Fs_WriteCString8(char const *file_path, Dqn_usize file_path_size, char const *buffer, Dqn_usize buffer_size);
DQN_API bool Dqn_Fs_Write (Dqn_String8 file_path, Dqn_String8 buffer);
DQN_API char *Dqn_Fs_ReadCString8 (char const *path, Dqn_usize path_size, Dqn_usize *file_size, Dqn_Allocator allocator);
DQN_API Dqn_String8 Dqn_Fs_Read (Dqn_String8 path, Dqn_Allocator allocator);
// NOTE: R/W Stream API ============================================================================
// NOTE: API =======================================================================================
// @proc Dqn_Fs_OpenFile
// @desc Open a handle to the file
// @proc Dqn_Fs_WriteFile
// @desc Append to the file specified by the handle with the given buffer.
// @proc Dqn_Fs_CloseFile
// @desc Close the file at specified by the handle
struct Dqn_FsFile
{
void *handle;
char error[512];
uint16_t error_size;
};
enum Dqn_FsFileOpen
{
Dqn_FsFileOpen_CreateAlways, // Create file if it does not exist, otherwise, zero out the file and open
Dqn_FsFileOpen_OpenIfExist, // Open file at path only if it exists
Dqn_FsFileOpen_OpenAlways, // Open file at path, create file if it does not exist
};
enum Dqn_FsFileAccess
{
Dqn_FsFileAccess_Read = 1 << 0,
Dqn_FsFileAccess_Write = 1 << 1,
Dqn_FsFileAccess_Execute = 1 << 2,
Dqn_FsFileAccess_AppendOnly = 1 << 3, // This flag cannot be combined with any other access mode
Dqn_FsFileAccess_ReadWrite = Dqn_FsFileAccess_Read | Dqn_FsFileAccess_Write,
Dqn_FsFileAccess_All = Dqn_FsFileAccess_ReadWrite | Dqn_FsFileAccess_Execute,
};
DQN_API Dqn_FsFile Dqn_Fs_OpenFile (Dqn_String8 path, Dqn_FsFileOpen open_mode, uint32_t access);
DQN_API bool Dqn_Fs_WriteFileBuffer(Dqn_FsFile *file, void const *data, Dqn_usize size);
DQN_API bool Dqn_Fs_WriteFile (Dqn_FsFile *file, Dqn_String8 buffer);
DQN_API bool Dqn_Fs_WriteFileFV (Dqn_FsFile *file, DQN_FMT_STRING_ANNOTATE char const *fmt, va_list args);
DQN_API bool Dqn_Fs_WriteFileF (Dqn_FsFile *file, DQN_FMT_STRING_ANNOTATE char const *fmt, ...);
DQN_API void Dqn_Fs_CloseFile (Dqn_FsFile *file);
#endif // !defined(DQN_NO_FS)
// NOTE: File system paths =========================================================================
// Helper data structure for building paths suitable for OS consumption.
//
// NOTE: API =======================================================================================
// @proc Dqn_FsPath_AddRef, Dqn_FsPath_Add
// @desc Append a path to the file path. The passed in path can be specify
// both a single level or multiple directories with different path separators.
// The path will be decomposed into individual sections in the function.
//
// For example passing
// - "path/to/your/desired/folder" is valid
// - "path" is valid
// - "path/to\your/desired\folder" is valid
// @proc Dqn_FsPath_Pop
// @desc Remove the last appended path level from the current path stored in
// the FsPath.
//
// For example "path/to/your/desired/folder" popped produces
// "path/to/your/desired"
// @proc Dqn_FsPath_Convert
// @desc Convert the path specified in the string to the OS native separated
// path.
#if !defined(Dqn_FsPathOSSeperator)
#if defined(DQN_OS_WIN32)
#define Dqn_FsPathOSSeperator "\\"
#else
#define Dqn_FsPathOSSeperator "/"
#endif
#define Dqn_FsPathOSSeperatorString DQN_STRING8(Dqn_FsPathOSSeperator)
#endif
struct Dqn_FsPathLink
{
Dqn_String8 string;
Dqn_FsPathLink *next;
Dqn_FsPathLink *prev;
};
struct Dqn_FsPath
{
Dqn_FsPathLink *head;
Dqn_FsPathLink *tail;
Dqn_usize string_size;
uint16_t links_size;
};
DQN_API bool Dqn_FsPath_AddRef (Dqn_Arena *arena, Dqn_FsPath *fs_path, Dqn_String8 path);
DQN_API bool Dqn_FsPath_Add (Dqn_Arena *arena, Dqn_FsPath *fs_path, Dqn_String8 path);
DQN_API bool Dqn_FsPath_AddF (Dqn_Arena *arena, Dqn_FsPath *fs_path, DQN_FMT_STRING_ANNOTATE char const *fmt, ...);
DQN_API bool Dqn_FsPath_Pop (Dqn_FsPath *fs_path);
DQN_API Dqn_String8 Dqn_FsPath_BuildWithSeparator(Dqn_Arena *arena, Dqn_FsPath const *fs_path, Dqn_String8 path_separator);
DQN_API Dqn_String8 Dqn_FsPath_Convert (Dqn_Arena *arena, Dqn_String8 path);
DQN_API Dqn_String8 Dqn_FsPath_ConvertF (Dqn_Arena *arena, DQN_FMT_STRING_ANNOTATE char const *fmt, ...);
#define Dqn_FsPath_BuildFwdSlash(arena, fs_path) Dqn_FsPath_BuildWithSeparator(arena, fs_path, DQN_STRING8("/"))
#define Dqn_FsPath_BuildBackSlash(arena, fs_path) Dqn_FsPath_BuildWithSeparator(arena, fs_path, DQN_STRING8("\\"))
#if !defined(Dqn_FsPath_Build)
#if defined(DQN_OS_WIN32)
#define Dqn_FsPath_Build(arena, fs_path) Dqn_FsPath_BuildBackSlash(arena, fs_path)
#else
#define Dqn_FsPath_Build(arena, fs_path) Dqn_FsPath_BuildFwdSlash(arena, fs_path)
#endif
#endif
// NOTE: [$DATE] Dqn_Date ==========================================================================
// NOTE: API =======================================================================================
// @proc Dqn_Date_EpochTime
// @desc Produce the time elapsed since the Unix epoch
// (e.g. 1970-01-01T00:00:00Z) in seconds
struct Dqn_DateHMSTimeString
{
char date[DQN_ARRAY_UCOUNT("YYYY-MM-SS")];
uint8_t date_size;
char hms[DQN_ARRAY_UCOUNT("HH:MM:SS")];
uint8_t hms_size;
};
struct Dqn_DateHMSTime
{
uint8_t day;
uint8_t month;
int16_t year;
uint8_t hour;
uint8_t minutes;
uint8_t seconds;
};
DQN_API Dqn_DateHMSTime Dqn_Date_HMSLocalTimeNow ();
DQN_API Dqn_DateHMSTimeString Dqn_Date_HMSLocalTimeStringNow(char date_separator = '-', char hms_separator = ':');
DQN_API Dqn_DateHMSTimeString Dqn_Date_HMSLocalTimeString (Dqn_DateHMSTime time, char date_separator = '-', char hms_separator = ':');
DQN_API uint64_t Dqn_Date_EpochTime ();
#if defined(DQN_OS_WIN32)
#if !defined(DQN_NO_WIN)
// NOTE: [$WIND] Dqn_Win ===========================================================================
// NOTE: API =======================================================================================
// @proc Dqn_Win_LastErrorToBuffer, Dqn_Win_LastError
// @desc Retrieve the latest error code and message Windows produced for the
// most recent Win32 API call.
// @proc Dqn_Win_MakeProcessDPIAware
// @desc Call once at application start-up to ensure that the application is
// DPI aware on Windows and ensure that application UI is scaled up
// appropriately for the monitor.
struct Dqn_WinError
{
unsigned long code;
Dqn_String8 msg;
};
DQN_API Dqn_WinError Dqn_Win_LastError(Dqn_Arena *arena);
DQN_API void Dqn_Win_MakeProcessDPIAware();
// NOTE: Windows String8 <-> String16 ===========================================
// Convert a UTF8 <-> UTF16 string.
//
// The exact size buffer required for this function can be determined by
// calling this function with the 'dest' set to null and 'dest_size' set to 0,
// the return size is the size required for conversion not-including space for
// the null-terminator. This function *always* null-terminates the input
// buffer.
//
// Returns the number of u8's (for UTF16->8) OR u16's (for UTF8->16)
// written/required for conversion. 0 if there was a conversion error and can be
// queried using 'Dqn_Win_LastError'
DQN_API Dqn_String16 Dqn_Win_String8ToString16(Dqn_Arena *arena, Dqn_String8 src);
DQN_API int Dqn_Win_String8ToString16Buffer(Dqn_String16 src, char *dest, int dest_size);
DQN_API Dqn_String8 Dqn_Win_String16ToString8(Dqn_Arena *arena, Dqn_String16 src);
DQN_API int Dqn_Win_String16ToString8Buffer(Dqn_String16 src, char *dest, int dest_size);
// NOTE: Path navigation ===========================================================================
// NOTE: API =======================================================================================
// @proc Dqn_Win_EXEDirW, Dqn_Win_EXEDirWArena
// @desc Evaluate the current executable's directory that is running when this
// function is called.
// @param[out] buffer The buffer to write the executable directory into. Set
// this to null to calculate the required buffer size for the directory.
// @param[in] size The size of the buffer given. Set this to 0 to calculate the
// required buffer size for the directory.
// @return The length of the executable directory string. If this return value
// exceeds the capacity of the 'buffer', the 'buffer' is untouched.
// @proc Dqn_Win_WorkingDir, Dqn_Win_WorkingDirW
// @param[in] suffix (Optional) A suffix to append to the current working directory
// @proc Dqn_Win_FolderIterate, Dqn_Win_FolderWIterate
// @desc Iterate the files in the specified folder at the path
#if 0
for (Dqn_WinFolderIterator it = {}; Dqn_Win_FolderIterate("C:/your/path/", &it); ) {
printf("%.*s\n", DQN_STRING_FMT(it.file_name));
}
#endif
struct Dqn_Win_FolderIteratorW
{
void *handle;
Dqn_String16 file_name;
wchar_t file_name_buf[512];
};
struct Dqn_Win_FolderIterator
{
void *handle;
Dqn_String8 file_name;
char file_name_buf[512];
};
DQN_API Dqn_String16 Dqn_Win_EXEDirW (Dqn_Arena *arena);
DQN_API Dqn_String8 Dqn_Win_WorkingDir (Dqn_Allocator allocator, Dqn_String8 suffix);
DQN_API Dqn_String16 Dqn_Win_WorkingDirW (Dqn_Allocator allocator, Dqn_String16 suffix);
DQN_API bool Dqn_Win_FolderIterate (Dqn_String8 path, Dqn_Win_FolderIterator *it);
DQN_API bool Dqn_Win_FolderWIterate(Dqn_String16 path, Dqn_Win_FolderIteratorW *it);
#endif // !defined(DQN_NO_WIN)
#if !defined(DQN_NO_WINNET)
// NOTE: [$WINN] Dqn_WinNet ========================================================================
// TODO(dqn): Useful options to expose in the handle
// https://docs.microsoft.com/en-us/windows/win32/wininet/option-flags
// INTERNET_OPTION_CONNECT_RETRIES -- default is 5 retries
// INTERNET_OPTION_CONNECT_TIMEOUT -- milliseconds
// INTERNET_OPTION_RECEIVE_TIMEOUT
// INTERNET_OPTION_SEND_TIMEOUT
//
// NOTE: API =======================================================================================
// @proc Dqn_Win_NetHandleInitHTTPMethod, Dqn_Win_NetHandleInitHTTPMethodCString
// @desc Setup a handle to the URL with the given HTTP verb.
//
// This function is the same as calling Dqn_Win_NetHandleInit() followed by
// Dqn_Win_NetHandleSetHTTPMethod().
//
// @param http_method The HTTP request type, e.g. "GET" or "POST" e.t.c
// @proc Dqn_Win_NetHandleSetHTTPMethod
// @desc Set the HTTP request method for the given handle. This function can
// be used on a pre-existing valid handle that has at the minimum been
// initialised.
enum Dqn_WinNetHandleState
{
Dqn_WinNetHandleState_Invalid,
Dqn_WinNetHandleState_Initialised,
Dqn_WinNetHandleState_HttpMethodReady,
Dqn_WinNetHandleState_RequestFailed,
Dqn_WinNetHandleState_RequestGood,
};
// The number of bytes each pump of the connection downloads at most. If this is
// zero we default to DQN_WIN_NET_HANDLE_DOWNLOAD_SIZE.
#if !defined(DQN_WIN_NET_HANDLE_DOWNLOAD_SIZE)
#define DQN_WIN_NET_HANDLE_DOWNLOAD_SIZE 4096
#endif
struct Dqn_WinNetHandle
{
// NOTE: We copy out the host name because it needs to be null-terminated.
// Luckily, we can assume a DNS domain won't exceed 256 characters so this
// will generally always work.
char host_name[256];
int host_name_size;
// NOTE: Everything after the domain/host name part of the string i.e. the
// '/test' part of the full url 'mywebsite.com/test'.
// TODO(dqn): I don't want to make our network API allocate here so we don't
// copy the string since we require that the string is null-terminated so
// then taking a pointer to the input string should work .. maybe this is
// ok?
char *url;
int url_size;
// NOTE: docs.microsoft.com/en-us/windows/win32/wininet/setting-and-retrieving-internet-options#scope-of-hinternet-handle
// These handles have three levels:
//
// The root HINTERNET handle (created by a call to InternetOpen) would contain all the Internet options that affect this instance of WinINet.
// HINTERNET handles that connect to a server (created by a call to InternetConnect)
// HINTERNET handles associated with a resource or enumeration of resources on a particular server.
//
// More detailed information about the HINTERNET dependency is listed here
// NOTE: https://docs.microsoft.com/en-us/windows/win32/wininet/appendix-a-hinternet-handles
void *internet_open_handle;
void *internet_connect_handle;
void *http_handle;
Dqn_WinNetHandleState state;
};
enum Dqn_WinNetHandleRequestHeaderFlag
{
Dqn_WinNetHandleRequestHeaderFlag_Add,
Dqn_WinNetHandleRequestHeaderFlag_AddIfNew,
Dqn_WinNetHandleRequestHeaderFlag_Replace,
Dqn_WinNetHandleRequestHeaderFlag_Count,
};
struct Dqn_WinNetHandleResponse
{
Dqn_String8 raw_headers;
Dqn_String8 *headers;
Dqn_usize headers_size;
// NOTE: Headers pulled from the 'raw_headers' for convenience
uint64_t content_length;
Dqn_String8 content_type;
};
DQN_API Dqn_WinNetHandle Dqn_Win_NetHandleInitCString (char const *url, int url_size);
DQN_API Dqn_WinNetHandle Dqn_Win_NetHandleInit (Dqn_String8 url);
DQN_API Dqn_WinNetHandle Dqn_Win_NetHandleInitHTTPMethodCString (char const *url, int url_size, char const *http_method);
DQN_API Dqn_WinNetHandle Dqn_Win_NetHandleInitHTTPMethod (Dqn_String8 url, Dqn_String8 http_method);
DQN_API void Dqn_Win_NetHandleClose (Dqn_WinNetHandle *handle);
DQN_API bool Dqn_Win_NetHandleIsValid (Dqn_WinNetHandle const *handle);
DQN_API void Dqn_Win_NetHandleSetUserAgentCString (Dqn_WinNetHandle *handle, char const *user_agent, int user_agent_size);
DQN_API bool Dqn_Win_NetHandleSetHTTPMethod (Dqn_WinNetHandle *handle, char const *method);
DQN_API bool Dqn_Win_NetHandleSetRequestHeaderCString8(Dqn_WinNetHandle *handle, char const *header, int header_size, uint32_t mode);
DQN_API bool Dqn_Win_NetHandleSetRequestHeaderString8 (Dqn_WinNetHandle *handle, Dqn_String8 header, uint32_t mode);
DQN_API Dqn_WinNetHandleResponse Dqn_Win_NetHandleSendRequest (Dqn_WinNetHandle *handle, Dqn_Allocator allocator, char const *post_data, unsigned long post_data_size);
DQN_API bool Dqn_Win_NetHandlePump (Dqn_WinNetHandle *handle, char *dest, int dest_size, size_t *download_size);
DQN_API char * Dqn_Win_NetHandlePumpCString8 (Dqn_WinNetHandle *handle, Dqn_Arena *arena, size_t *download_size);
DQN_API Dqn_String8 Dqn_Win_NetHandlePumpString8 (Dqn_WinNetHandle *handle, Dqn_Arena *arena);
DQN_API void Dqn_Win_NetHandlePumpToCRTFile (Dqn_WinNetHandle *handle, FILE *file);
DQN_API char * Dqn_Win_NetHandlePumpToAllocCString (Dqn_WinNetHandle *handle, size_t *download_size);
DQN_API Dqn_String8 Dqn_Win_NetHandlePumpToAllocString (Dqn_WinNetHandle *handle);
#endif // !defined(DQN_NO_WINNET)
#endif // defined(DQN_OS_WIN32)
// NOTE: [$OSYS] Dqn_OS ============================================================================
// NOTE: API =======================================================================================
// @proc Dqn_OS_SecureRNGBytes
// @desc Generate cryptographically secure bytes
// @proc Dqn_OS_EXEDir
// @desc Retrieve the executable directory without the trailing '/' or
// ('\' for windows). If this fails an empty string is returned.
// @proc Dqn_OS_PerfCounterFrequency
// @desc Get the number of ticks in the performance counter per second for the
// operating system you're running on. This value can be used to calculate
// duration from OS performance counter ticks.
// @proc Dqn_OS_EstimateTSCPerSecond
// @desc Estimate how many timestamp count's (TSC) there are per second. TSC
// is evaluated by calling __rdtsc() or the equivalent on the platform. This
// value can be used to convert TSC durations into seconds.
//
// @param duration_ms_to_gauge_tsc_frequency How many milliseconds to spend
// measuring the TSC rate of the current machine. 100ms is sufficient to
// produce a fairly accurate result with minimal blocking in applications.
/// Record time between two time-points using the OS's performance counter.
struct Dqn_OSTimer
{
uint64_t start;
uint64_t end;
};
DQN_API bool Dqn_OS_SecureRNGBytes (void *buffer, uint32_t size);
#if (defined(DQN_OS_WIN32) && !defined(DQN_NO_WIN)) || !defined(DQN_OS_WIN32)
DQN_API Dqn_String8 Dqn_OS_EXEDir (Dqn_Arena* arena);
#endif
DQN_API void Dqn_OS_SleepMs (Dqn_uint milliseconds);
DQN_API uint64_t Dqn_OS_PerfCounterNow ();
DQN_API uint64_t Dqn_OS_PerfCounterFrequency();
DQN_API Dqn_f64 Dqn_OS_PerfCounterS (uint64_t begin, uint64_t end);
DQN_API Dqn_f64 Dqn_OS_PerfCounterMs (uint64_t begin, uint64_t end);
DQN_API Dqn_f64 Dqn_OS_PerfCounterMicroS (uint64_t begin, uint64_t end);
DQN_API Dqn_f64 Dqn_OS_PerfCounterNs (uint64_t begin, uint64_t end);
DQN_API Dqn_OSTimer Dqn_OS_TimerBegin ();
DQN_API void Dqn_OS_TimerEnd (Dqn_OSTimer *timer);
DQN_API Dqn_f64 Dqn_OS_TimerS (Dqn_OSTimer timer);
DQN_API Dqn_f64 Dqn_OS_TimerMs (Dqn_OSTimer timer);
DQN_API Dqn_f64 Dqn_OS_TimerMicroS (Dqn_OSTimer timer);
DQN_API Dqn_f64 Dqn_OS_TimerNs (Dqn_OSTimer timer);
DQN_API uint64_t Dqn_OS_EstimateTSCPerSecond(uint64_t duration_ms_to_gauge_tsc_frequency);
// NOTE: [$TCTX] Dqn_ThreadContext =================================================================
// Each thread is assigned in their thread-local storage (TLS) scratch and
// permanent arena allocators. These can be used for allocations with a lifetime
// scoped to the lexical scope or for storing data permanently using the arena
// paradigm.
//
// TLS in this implementation is implemented using the `thread_local` C/C++
// keyword.
//
// NOTE: API
//
// @proc Dqn_Thread_GetContext
// @desc Get the current thread's context- this contains all the metadata for managing
// the thread scratch data. In general you probably want Dqn_Thread_GetScratch()
// which ensures you get a usable scratch arena for temporary allocations
// without having to worry about selecting the right arena from the state.
//
// @proc Dqn_Thread_GetScratch
// @desc Retrieve the per-thread temporary arena allocator that is reset on scope
// exit.
//
// The scratch arena must be deconflicted with any existing arenas in the
// function to avoid trampling over each other's memory. Consider the situation
// where the scratch arena is passed into the function. Inside the function, if
// the same arena is reused then, if both arenas allocate, when the inner arena
// is reset, this will undo the passed in arena's allocations in the function.
//
// @param[in] conflict_arena A pointer to the arena currently being used in the
// function
struct Dqn_ThreadContext
{
Dqn_b32 init;
// Scratch memory arena's for the calling thread
Dqn_Arena *scratch_arenas[2];
// Allocators that use the corresponding arena from the thread context.
// Provided for convenience when interfacing with allocator interfaces.
Dqn_Allocator scratch_allocators[2];
};
struct Dqn_ThreadScratch
{
Dqn_ThreadScratch(Dqn_ThreadContext *context, uint8_t context_index);
~Dqn_ThreadScratch();
Dqn_Allocator allocator;
Dqn_Arena *arena;
Dqn_b32 destructed;
Dqn_ArenaTempMemory temp_memory;
};
// NOTE: Context ===================================================================================
DQN_API uint32_t Dqn_Thread_GetID();
DQN_API Dqn_ThreadContext *Dqn_Thread_GetContext();
DQN_API Dqn_ThreadScratch Dqn_Thread_GetScratch(void const *conflict_arena);