#pragma once // Baselib FileIO // // This is a file reading abstraction api heavily influenced by next-gen async API's like io_uring, windows register I/O, etc. // This api allows for platform independent async file reading. #include "Baselib_ErrorState.h" #include "Baselib_Memory.h" #include "Internal/Baselib_EnumSizeCheck.h" #ifdef __cplusplus BASELIB_C_INTERFACE { #endif // Event queue handle. typedef struct Baselib_FileIO_EventQueue {void* handle;} Baselib_FileIO_EventQueue; // Async file handle. typedef struct Baselib_FileIO_AsyncFile {void* handle;} Baselib_FileIO_AsyncFile; // Sync file handle. typedef struct Baselib_FileIO_SyncFile {void* handle;} Baselib_FileIO_SyncFile; // Event queue handle invalid constant. static const Baselib_FileIO_EventQueue Baselib_FileIO_EventQueue_Invalid = { NULL }; // Async file handle invalid constant. static const Baselib_FileIO_AsyncFile Baselib_FileIO_AsyncFile_Invalid = { NULL }; // Sync file handle invalid constant. static const Baselib_FileIO_SyncFile Baselib_FileIO_SyncFile_Invalid = { (void*)-1 }; typedef enum Baselib_FileIO_OpenFlags_t { // Allows read access to the file. Baselib_FileIO_OpenFlags_Read = 0x01, // Allows write access to the file. Baselib_FileIO_OpenFlags_Write = 0x02, // Opens existing file without changes or creates 0 size file if file doesn't exist. // On some platforms open will implicitly add write flag if required by native API's. Baselib_FileIO_OpenFlags_OpenAlways = 0x04, // Always creates 0 size file. // On some platforms open will implicitly add write flag if required by native API's. Baselib_FileIO_OpenFlags_CreateAlways = 0x08, } Baselib_FileIO_OpenFlags_t; typedef uint32_t Baselib_FileIO_OpenFlags; // File IO read request. typedef struct Baselib_FileIO_ReadRequest { // Offset in a file to read from. // If offset+size is pointing pass EOF, will read up to EOF bytes. // If offset is pointing pass EOF, will read 0 bytes. uint64_t offset; // Buffer to read to, must be available for duration of operation. void* buffer; // Size of requested read. // If 0 is passed will read 0 bytes and raise no error. uint64_t size; } Baselib_FileIO_ReadRequest; // File IO priorities. // First we process all requests with high priority, then with normal priority. // There's no round-robin, and high priority can starve normal priority. typedef enum Baselib_FileIO_Priority { Baselib_FileIO_Priority_Normal = 0, Baselib_FileIO_Priority_High = 1 } Baselib_FileIO_Priority; BASELIB_ENUM_ENSURE_ABI_COMPATIBILITY(Baselib_FileIO_Priority); typedef enum Baselib_FileIO_EventQueue_ResultType { // Upon receiving this event, please call the provided callback with provided data argument. Baselib_FileIO_EventQueue_Callback = 1, // Result of open file operation. Baselib_FileIO_EventQueue_OpenFile = 2, // Result of read file operation. Baselib_FileIO_EventQueue_ReadFile = 3, // Result of close file operation. Baselib_FileIO_EventQueue_CloseFile = 4 } Baselib_FileIO_EventQueue_ResultType; BASELIB_ENUM_ENSURE_ABI_COMPATIBILITY(Baselib_FileIO_EventQueue_ResultType); typedef void (*EventQueueCallback)(uint64_t userdata); typedef struct Baselib_FileIO_EventQueue_Result_Callback { // Please invoke this callback with userdata from the event. EventQueueCallback callback; } Baselib_FileIO_EventQueue_Result_Callback; typedef struct Baselib_FileIO_EventQueue_Result_OpenFile { // Size of the file as seen on during open. uint64_t fileSize; } Baselib_FileIO_EventQueue_Result_OpenFile; typedef struct Baselib_FileIO_EventQueue_Result_ReadFile { // Bytes transferred during read. uint64_t bytesTransferred; } Baselib_FileIO_EventQueue_Result_ReadFile; // Event queue result. typedef struct Baselib_FileIO_EventQueue_Result { // Event type. Baselib_FileIO_EventQueue_ResultType type; // Userdata as provided to the request. uint64_t userdata; // Error state of the operation. Baselib_ErrorState errorState; union { Baselib_FileIO_EventQueue_Result_Callback callback; Baselib_FileIO_EventQueue_Result_OpenFile openFile; Baselib_FileIO_EventQueue_Result_ReadFile readFile; }; } Baselib_FileIO_EventQueue_Result; // Creates event queue. // // \returns Event queue. BASELIB_API Baselib_FileIO_EventQueue Baselib_FileIO_EventQueue_Create(void); // Frees event queue. // // \param eq event queue to free. BASELIB_API void Baselib_FileIO_EventQueue_Free( Baselib_FileIO_EventQueue eq ); // Dequeue events from event queue. // // \param eq Event queue to dequeue from. // \param results Results array to dequeue elements into. // If null will return 0. // \param count Amount of elements in results array. // If equals 0 will return 0. // \param timeoutInMilliseconds If no elements are present in the queue, // waits for any elements to be appear for specified amount of time. // If 0 is passed, wait is omitted. // If elements are present, dequeues up-to-count elements, and wait is omitted. // // File operations errors are reported via Baselib_FileIO_EventQueue_Result::errorState // Possible error codes: // - InvalidPathname: Requested pathname is invalid (not found, a directory, etc). // - RequestedAccessIsNotAllowed: Access to requested pathname is not allowed. // - IOError: IO error occured. // // \returns Amount of results filled. BASELIB_API uint64_t Baselib_FileIO_EventQueue_Dequeue( Baselib_FileIO_EventQueue eq, Baselib_FileIO_EventQueue_Result results[], uint64_t count, uint32_t timeoutInMilliseconds // 0 will return immediately ); // Request dequeue to shutdown // // \param eq Event queue to shutdown. // \param threadCount Number of threads to signal termination // // An empty queue will hang in Baselib_FileIO_EventQueue_Dequeue for as long as the timeout lasts. // This function can be used to exit such a condition BASELIB_API void Baselib_FileIO_EventQueue_Shutdown( Baselib_FileIO_EventQueue eq, uint32_t threadCount ); // Asynchronously opens a file. // // \param eq Event queue to associate file with. // File can only be associated with one event queue, // but one event queue can be associated with multiple files. // If invalid event queue is passed, will return invalid file handle. // \param pathname Platform defined pathname of a file. // Can be freed after this function returns. // If null is passed will return invalid file handle. // \param userdata Userdata to be set in the completion event. // \param priority Priority for file opening operation. // // Please note errors are reported via Baselib_FileIO_EventQueue_Result::errorState // Possible error codes: // - InvalidPathname: Requested pathname is invalid (not found, a directory, etc). // - RequestedAccessIsNotAllowed: Access to requested pathname is not allowed. // - IOError: IO error occured. // // \returns Async file handle, which can be used immediately for scheduling other operations. // In case if file opening fails, all scheduled operations will fail as well. // In case if invalid arguments are passed, might return invalid file handle (see args descriptions). BASELIB_API Baselib_FileIO_AsyncFile Baselib_FileIO_AsyncOpen( Baselib_FileIO_EventQueue eq, const char* pathname, uint64_t userdata, Baselib_FileIO_Priority priority ); // Asynchronously reads data from a file. // // Note scheduling reads on closed file is undefined. // // \param file Async file to read from. // If invalid file handle is passed, will no-op. // If file handle was already closed, behavior is undefined. // \param requests Requests to schedule. // If more than 1 provided, // will provide completion event per individual request in the array. // If null is passed, will no-op. // \param count Amount of requests in requests array. // If 0 is passed, will no-op. // \param userdata Userdata to be set in the completion event(s). // \param priority Priority for file reading operation(s). // // Please note errors are reported via Baselib_FileIO_EventQueue_Result::errorState // If file is invalid handle, error can not be reported because event queue is not known. // Possible error codes: // - IOError: IO error occured. BASELIB_API void Baselib_FileIO_AsyncRead( Baselib_FileIO_AsyncFile file, Baselib_FileIO_ReadRequest requests[], uint64_t count, uint64_t userdata, Baselib_FileIO_Priority priority ); // Asynchronously closes a file. // // Will wait for all pending operations to complete, // after that will close a file and put a completion event. // // \param file Async file to close. // If invalid file handle is passed, will no-op. // // Please note errors are reported via Baselib_FileIO_EventQueue_Result::errorState // If file is invalid handle, error can not be reported because event queue is not known. // Possible error codes: // - IOError: IO error occured. BASELIB_API void Baselib_FileIO_AsyncClose( Baselib_FileIO_AsyncFile file ); // Synchronously opens a file. // // Will try use the most open access permissions options that are available for each platform. // Meaning it might be possible for other process to write to file opened via this API. // On most platforms file can be simultaneously opened with different open flags. // If you require more strict options, or platform specific access configuration, please use Baselib_FileIO_SyncFileFromNativeHandle. // // \param pathname Platform defined pathname to open. // \param openFlags Open flags. // If file is created because one of Create flags is passed, it will have size of 0 bytes. // // Possible error codes: // - InvalidArgument: Invalid argument was passed. // - RequestedAccessIsNotAllowed: Request access is not allowed. // - IOError: Generic IO error occured. // // \returns SyncFile handle. BASELIB_API Baselib_FileIO_SyncFile Baselib_FileIO_SyncOpen( const char* pathname, Baselib_FileIO_OpenFlags openFlags, Baselib_ErrorState* errorState ); // Transfer ownership of native handle to Baselib_FileIO_SyncFile handle. // // This function transfers ownership, meaning you don't need to close native handle yourself, // instead returned SyncFile must closed via Baselib_FileIO_SyncClose. // Implementations might cache information about the file state, // so native handle shouldn't be used after transfering ownership. // // \param handle Platform defined native handle. // If invalid native handle is passed, will return Baselib_FileIO_SyncFile_Invalid. // \param type Platform defined native handle type from Baselib_FileIO_NativeHandleType enum. // If unsupported type is passed, will return Baselib_FileIO_SyncFile_Invalid. // // \returns SyncFile handle. BASELIB_API Baselib_FileIO_SyncFile Baselib_FileIO_SyncFileFromNativeHandle( uint64_t handle, uint32_t type ); // Synchronously reads data from a file. // // \param file File to read from. // If invalid file handle is passed, will raise InvalidArgument error and return 0. // \param offset Offset in the file to read data at. // If offset+size goes past end-of-file (EOF), function will read until EOF. // If offset points past EOF, will return 0. // \param buffer Pointer to data to read into. // \param size Size of data to read. // // Possible error codes: // - InvalidArgument: Invalid argument was passed. // - IOError: Generic IO error occured. // // \returns Amount of bytes read. BASELIB_API uint64_t Baselib_FileIO_SyncRead( Baselib_FileIO_SyncFile file, uint64_t offset, void* buffer, uint64_t size, Baselib_ErrorState* errorState ); // Synchronously writes data to a file. // // \param file File to write to. // If invalid file handle is passed, will raise InvalidArgument error and return 0. // \param offset Offset in the file to write data at. // If offset+size goes past end-of-file (EOF), then file will be resized. // \param buffer Pointer to data to write. // \param size Size of data to write. // // Possible error codes: // - InvalidArgument: Invalid argument was passed. // - IOError: Generic IO error occured. // // \returns Amount of bytes written. BASELIB_API uint64_t Baselib_FileIO_SyncWrite( Baselib_FileIO_SyncFile file, uint64_t offset, const void* buffer, uint64_t size, Baselib_ErrorState* errorState ); // Synchronously flushes file buffers. // // Operating system might buffer some write operations. // Flushing buffers is required to guarantee (best effort) writing data to disk. // // \param file File to flush. // If invalid file handle is passed, will no-op. // // Possible error codes: // - InvalidArgument: Invalid argument was passed. // - IOError: Generic IO error occured. BASELIB_API void Baselib_FileIO_SyncFlush( Baselib_FileIO_SyncFile file, Baselib_ErrorState* errorState ); // Synchronously changes file size. // // \param file File to get size of. // If invalid file handle is passed, will raise invalid argument error. // \param size New file size. // // Possible error codes: // - InvalidArgument: Invalid argument was passed. // - IOError: Generic IO error occured. // // \returns File size. BASELIB_API void Baselib_FileIO_SyncSetFileSize( Baselib_FileIO_SyncFile file, uint64_t size, Baselib_ErrorState* errorState ); // Synchronously retrieves file size. // // \param file File to get size of. // If invalid file handle is passed, will return 0. // // Possible error codes: // - InvalidArgument: Invalid argument was passed. // - IOError: Generic IO error occured. // // \returns File size. BASELIB_API uint64_t Baselib_FileIO_SyncGetFileSize( Baselib_FileIO_SyncFile file, Baselib_ErrorState* errorState ); // Synchronously closes a file. // // Close does not guarantee that the data was written to disk, // Please use Baselib_FileIO_SyncFlush to guarantee (best effort) that data was written to disk. // // \param file File to close. // If invalid file handle is passed, will no-op. // // Possible error codes: // - InvalidArgument: Invalid argument was passed. // - IOError: Generic IO error occured. BASELIB_API void Baselib_FileIO_SyncClose( Baselib_FileIO_SyncFile file, Baselib_ErrorState* errorState ); #include <C/Baselib_FileIO.inl.h> #ifdef __cplusplus } // BASELIB_C_INTERFACE #endif