2023-02-10 15:13:26 +08:00

401 lines
15 KiB
C

#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