8000 miniaudio/external/fs/fs.c at dev · Zaczero/miniaudio · GitHub
[go: up one dir, main page]

Skip to content

Latest commit

 

History

History
10959 lines (8924 loc) · 330 KB

fs.c

File metadata and controls

10959 lines (8924 loc) · 330 KB
#ifndef fs_c
#define fs_c
#include "fs.h"
#include <errno.h>
/* BEG fs_common_macros.c */
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#if defined(__clang__) && defined(__has_attribute)
#if __has_attribute(suppress)
#define FS_SUPPRESS_CLANG_ANALYZER __attribute__((suppress))
#else
#define FS_SUPPRESS_CLANG_ANALYZER
#endif
#else
#define FS_SUPPRESS_CLANG_ANALYZER
#endif
/* BEG fs_va_copy.c */
#ifndef fs_va_copy
#if !defined(_MSC_VER) || _MSC_VER >= 1800
#if !defined(__STDC_VERSION__) || (defined(__GNUC__) && __GNUC__ < 3) /* <-- va_copy() is not available when using `-std=c89`. The `!defined(__STDC_VERSION__)` parts is what checks for this. */
#if defined(__va_copy)
#define fs_va_copy(dst, src) __va_copy(dst, src)
#else
#define fs_va_copy(dst, src) ((dst) = (src)) /* This is untested. Not sure if this is correct for old GCC. */
#endif
#else
#define fs_va_copy(dst, src) va_copy((dst), (src))
#endif
#else
#define fs_va_copy(dst, src) ((dst) = (src))
#endif
#endif
/* END fs_va_copy.c */
#define FS_UNUSED(x) (void)x
#ifndef FS_MALLOC
#define FS_MALLOC(sz) malloc((sz))
#endif
#ifndef FS_REALLOC
#define FS_REALLOC(p, sz) realloc((p), (sz))
#endif
#ifndef FS_FREE
#define FS_FREE(p) free((p))
#endif
static void fs_zero_memory_default(void* p, size_t sz)
{
if (sz > 0) {
memset(p, 0, sz);
}
}
#ifndef FS_ZERO_MEMORY
#define FS_ZERO_MEMORY(p, sz) fs_zero_memory_default((p), (sz))
#endif
#ifndef FS_COPY_MEMORY
#define FS_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz))
#endif
#ifndef FS_MOVE_MEMORY
#define FS_MOVE_MEMORY(dst, src, sz) memmove((dst), (src), (sz))
#endif
#ifndef FS_ASSERT
#define FS_ASSERT(condition) assert(condition)
#endif
#define FS_ZERO_OBJECT(p) FS_ZERO_MEMORY((p), sizeof(*(p)))
#define FS_COUNTOF(x) (sizeof(x) / sizeof(x[0]))
#define FS_MAX(x, y) (((x) > (y)) ? (x) : (y))
#define FS_MIN(x, y) (((x) < (y)) ? (x) : (y))
#define FS_ABS(x) (((x) > 0) ? (x) : -(x))
#define FS_CLAMP(x, lo, hi) (FS_MAX((lo), FS_MIN((x), (hi))))
#define FS_OFFSET_PTR(p, offset) (((unsigned char*)(p)) + (offset))
#define FS_ALIGN(x, a) ((x + (a-1)) & ~(a-1))
/* END fs_common_macros.c */
FS_API char* fs_strcpy(char* dst, const char* src)
{
char* dstorig;
FS_ASSERT(dst != NULL);
FS_ASSERT(src != NULL);
dstorig = dst;
/* No, we're not using this garbage: while (*dst++ = *src++); */
for (;;) {
*dst = *src;
if (*src == '\0') {
break;
}
dst += 1;
src += 1;
}
return dstorig;
}
FS_API int fs_strncpy(char* dst, const char* src, size_t count)
{
size_t maxcount;
size_t i;
if (dst == 0) {
return EINVAL;
}
if (src == 0) {
dst[0] = '\0';
return EINVAL;
}
maxcount = count;
for (i = 0; i < maxcount && src[i] != '\0'; ++i) {
dst[i] = src[i];
}
if (src[i] == '\0' || i == count || count == ((size_t)-1)) {
dst[i] = '\0';
return 0;
}
dst[0] = '\0';
return ERANGE;
}
FS_API int fs_strcpy_s(char* dst, size_t dstCap, const char* src)
{
size_t i;
if (dst == 0) {
return EINVAL;
}
if (dstCap == 0) {
return ERANGE;
}
if (src == 0) {
dst[0] = '\0';
return EINVAL;
}
for (i = 0; i < dstCap && src[i] != '\0'; ++i) {
dst[i] = src[i];
}
if (i < dstCap) {
dst[i] = '\0';
return 0;
}
dst[0] = '\0';
return ERANGE;
}
FS_API int fs_strncpy_s(char* dst, size_t dstCap, const char* src, size_t count)
{
size_t maxcount;
size_t i;
if (dst == 0) {
return EINVAL;
}
if (dstCap == 0) {
return EINVAL;
}
if (src == 0) {
dst[0] = '\0';
return EINVAL;
}
maxcount = count;
if (count == ((size_t)-1) || count >= dstCap) { /* -1 = _TRUNCATE */
maxcount = dstCap - 1;
}
for (i = 0; i < maxcount && src[i] != '\0'; ++i) {
dst[i] = src[i];
}
if (src[i] == '\0' || i == count || count == ((size_t)-1)) {
dst[i] = '\0';
return 0;
}
dst[0] = '\0';
return ERANGE;
}
FS_API int fs_strcat_s(char* dst, size_t dstCap, const char* src)
{
char* dstorig;
if (dst == 0) {
return EINVAL;
}
if (dstCap == 0) {
return ERANGE;
}
if (src == 0) {
dst[0] = '\0';
return EINVAL;
}
dstorig = dst;
while (dstCap > 0 && dst[0] != '\0') {
dst += 1;
dstCap -= 1;
}
if (dstCap == 0) {
return EINVAL; /* Unterminated. */
}
while (dstCap > 0 && src[0] != '\0') {
*dst++ = *src++;
dstCap -= 1;
}
if (dstCap > 0) {
dst[0] = '\0';
} else {
dstorig[0] = '\0';
return ERANGE;
}
return 0;
}
FS_API int fs_strncat_s(char* dst, size_t dstCap, const char* src, size_t count)
{
char* dstorig;
if (dst == 0) {
return EINVAL;
}
if (dstCap == 0) {
return ERANGE;
}
if (src == 0) {
return EINVAL;
}
dstorig = dst;
while (dstCap > 0 && dst[0] != '\0') {
dst += 1;
dstCap -= 1;
}
if (dstCap == 0) {
return EINVAL; /* Unterminated. */
}
if (count == ((size_t)-1)) { /* _TRUNCATE */
count = dstCap - 1;
}
while (dstCap > 0 && src[0] != '\0' && count > 0) {
*dst++ = *src++;
dstCap -= 1;
count -= 1;
}
if (dstCap > 0) {
dst[0] = '\0';
} else {
dstorig[0] = '\0';
return ERANGE;
}
return 0;
}
FS_API int fs_strncmp(const char* str1, const char* str2, size_t maxLen)
{
if (str1 == str2) return 0;
/* These checks differ from the standard implementation. It's not important, but I prefer it just for sanity. */
if (str1 == NULL) return -1;
if (str2 == NULL) return 1;
/* This function still needs to check for null terminators even though the length has been specified. */
for (;;) {
if (maxLen == 0) {
break;
}
if (str1[0] == '\0') {
break;
}
if (str1[0] != str2[0]) {
break;
}
str1 += 1;
str2 += 1;
maxLen -= 1;
}
if (maxLen == 0) {
return 0;
}
return ((unsigned char*)str1)[0] - ((unsigned char*)str2)[0];
}
FS_API int fs_strnicmp_ascii(const char* str1, const char* str2, size_t count)
{
if (str1 == NULL || str2 == NULL) {
return 0;
}
while (*str1 != '\0' && *str2 != '\0' && count > 0) {
int c1 = (int)*str1;
int c2 = (int)*str2;
if (c1 >= 'A' && c1 <= 'Z') {
c1 += 'a' - 'A';
}
if (c2 >= 'A' && c2 <= 'Z') {
c2 += 'a' - 'A';
}
if (c1 != c2) {
return c1 - c2;
}
str1 += 1;
str2 += 1;
count -= 1;
}
if (count == 0) {
return 0;
} else if (*str1 == '\0' && *str2 == '\0') {
return 0;
} else if (*str1 == '\0') {
return -1;
} else {
return 1;
}
}
FS_API int fs_strnicmp(const char* str1, const char* str2, size_t count)
{
/* We will use the standard implementations of strnicmp() and strncasecmp() if they are available. */
#if defined(_MSC_VER) && _MSC_VER >= 1400
return _strnicmp(str1, str2, count);
#elif defined(__GNUC__) && defined(__USE_GNU)
return strncasecmp(str1, str2, count);
#else
/* It would be good if we could use a custom implementation based on the Unicode standard here. Would require a lot of work to get that right, however. */
return fs_strnicmp_ascii(str1, str2, count);
#endif
}
/* BEG fs_result.c */
FS_API const char* fs_result_description(fs_result result)
{
switch (result)
{
case FS_SUCCESS: return "No error";
case FS_ERROR: return "Unknown error";
case FS_INVALID_ARGS: return "Invalid argument";
case FS_INVALID_OPERATION: return "Invalid operation";
case FS_OUT_OF_MEMORY: return "Out of memory";
case FS_OUT_OF_RANGE: return "Out of range";
case FS_ACCESS_DENIED: return "Permission denied";
case FS_DOES_NOT_EXIST: return "Resource does not exist";
case FS_ALREADY_EXISTS: return "Resource already exists";
case FS_TOO_MANY_OPEN_FILES: return "Too many open files";
case FS_INVALID_FILE: return "Invalid file";
case FS_TOO_BIG: return "Too large";
case FS_PATH_TOO_LONG: return "Path too long";
case FS_NAME_TOO_LONG: return "Name too long";
case FS_NOT_DIRECTORY: return "Not a directory";
case FS_IS_DIRECTORY: return "Is a directory";
case FS_DIRECTORY_NOT_EMPTY: return "Directory not empty";
case FS_AT_END: return "At end";
case FS_NO_SPACE: return "No space available";
case FS_BUSY: return "Device or resource busy";
case FS_IO_ERROR: return "Input/output error";
case FS_INTERRUPT: return "Interrupted";
case FS_UNAVAILABLE: return "Resource unavailable";
case FS_ALREADY_IN_USE: return "Resource already in use";
case FS_BAD_ADDRESS: return "Bad address";
case FS_BAD_SEEK: return "Illegal seek";
case FS_BAD_PIPE: return "Broken pipe";
case FS_DEADLOCK: return "Deadlock";
case FS_TOO_MANY_LINKS: return "Too many links";
case FS_NOT_IMPLEMENTED: return "Not implemented";
case FS_NO_MESSAGE: return "No message of desired type";
case FS_BAD_MESSAGE: return < 10BC8 span class="pl-s">"Invalid message";
case FS_NO_DATA_AVAILABLE: return "No data available";
case FS_INVALID_DATA: return "Invalid data";
case FS_TIMEOUT: return "Timeout";
case FS_NO_NETWORK: return "Network unavailable";
case FS_NOT_UNIQUE: return "Not unique";
case FS_NOT_SOCKET: return "Socket operation on non-socket";
case FS_NO_ADDRESS: return "Destination address required";
case FS_BAD_PROTOCOL: return "Protocol wrong type for socket";
case FS_PROTOCOL_UNAVAILABLE: return "Protocol not available";
case FS_PROTOCOL_NOT_SUPPORTED: return "Protocol not supported";
case FS_PROTOCOL_FAMILY_NOT_SUPPORTED: return "Protocol family not supported";
case FS_ADDRESS_FAMILY_NOT_SUPPORTED: return "Address family not supported";
case FS_SOCKET_NOT_SUPPORTED: return "Socket type not supported";
case FS_CONNECTION_RESET: return "Connection reset";
case FS_ALREADY_CONNECTED: return "Already connected";
case FS_NOT_CONNECTED: return "Not connected";
case FS_CONNECTION_REFUSED: return "Connection refused";
case FS_NO_HOST: return "No host";
case FS_IN_PROGRESS: return "Operation in progress";
case FS_CANCELLED: return "Operation cancelled";
case FS_MEMORY_ALREADY_MAPPED: return "Memory already mapped";
case FS_DIFFERENT_DEVICE: return "Different device";
default: return "Unknown error";
}
}
#include <errno.h>
FS_API fs_result fs_result_from_errno(int error)
{
if (error == 0) {
return FS_SUCCESS;
}
#ifdef EPERM
else if (error == EPERM) { return FS_INVALID_OPERATION; }
#endif
#ifdef ENOENT
else if (error == ENOENT) { return FS_DOES_NOT_EXIST; }
#endif
#ifdef ESRCH
else if (error == ESRCH) { return FS_DOES_NOT_EXIST; }
#endif
#ifdef EINTR
else if (error == EINTR) { return FS_INTERRUPT; }
#endif
#ifdef EIO
else if (error == EIO) { return FS_IO_ERROR; }
#endif
#ifdef ENXIO
else if (error == ENXIO) { return FS_DOES_NOT_EXIST; }
#endif
#ifdef E2BIG
else if (error == E2BIG) { return FS_INVALID_ARGS; }
#endif
#ifdef ENOEXEC
else if (error == ENOEXEC) { return FS_INVALID_FILE; }
#endif
#ifdef EBADF
else if (error == EBADF) { return FS_INVALID_FILE; }
#endif
#ifdef EAGAIN
else if (error == EAGAIN) { return FS_UNAVAILABLE; }
#endif
#ifdef ENOMEM
else if (error == ENOMEM) { return FS_OUT_OF_MEMORY; }
#endif
#ifdef EACCES
else if (error == EACCES) { return FS_ACCESS_DENIED; }
#endif
#ifdef EFAULT
else if (error == EFAULT) { return FS_BAD_ADDRESS; }
#endif
#ifdef EBUSY
else if (error == EBUSY) { return FS_BUSY; }
#endif
#ifdef EEXIST
else if (error == EEXIST) { return FS_ALREADY_EXISTS; }
#endif
#ifdef EXDEV
else if (error == EXDEV) { return FS_DIFFERENT_DEVICE; }
#endif
#ifdef ENODEV
else if (error == ENODEV) { return FS_DOES_NOT_EXIST; }
#endif
#ifdef ENOTDIR
else if (error == ENOTDIR) { return FS_NOT_DIRECTORY; }
#endif
#ifdef EISDIR
else if (error == EISDIR) { return FS_IS_DIRECTORY; }
#endif
#ifdef EINVAL
else if (error == EINVAL) { return FS_INVALID_ARGS; }
#endif
#ifdef ENFILE
else if (error == ENFILE) { return FS_TOO_MANY_OPEN_FILES; }
#endif
#ifdef EMFILE
else if (error == EMFILE) { return FS_TOO_MANY_OPEN_FILES; }
#endif
#ifdef ENOTTY
else if (error == ENOTTY) { return FS_INVALID_OPERATION; }
#endif
#ifdef ETXTBSY
else if (error == ETXTBSY) { return FS_BUSY; }
#endif
#ifdef EFBIG
else if (error == EFBIG) { return FS_TOO_BIG; }
#endif
#ifdef ENOSPC
else if (error == ENOSPC) { return FS_NO_SPACE; }
#endif
#ifdef ESPIPE
else if (error == ESPIPE) { return FS_BAD_SEEK; }
#endif
#ifdef EROFS
else if (error == EROFS) { return FS_ACCESS_DENIED; }
#endif
#ifdef EPIPE
else if (error == EPIPE) { return FS_BAD_PIPE; }
#endif
#ifdef EDOM
else if (error == EDOM) { return FS_OUT_OF_RANGE; }
#endif
#ifdef ERANGE
else if (error == ERANGE) { return FS_OUT_OF_RANGE; }
#endif
#ifdef EDEADLK
else if (error == EDEADLK) { return FS_DEADLOCK; }
#endif
#ifdef ENAMETOOLONG
else if (error == ENAMETOOLONG) { return FS_PATH_TOO_LONG; }
#endif
#ifdef ENOSYS
else if (error == ENOSYS) { return FS_NOT_IMPLEMENTED; }
#endif
#ifdef ENOTEMPTY
else if (error == ENOTEMPTY) { return FS_DIRECTORY_NOT_EMPTY; }
#endif
#ifdef ELNRNG
else if (error == ELNRNG) { return FS_OUT_OF_RANGE; }
#endif
#ifdef EBFONT
else if (error == EBFONT) { return FS_INVALID_FILE; }
#endif
#ifdef ENODATA
else if (error == ENODATA) { return FS_NO_DATA_AVAILABLE; }
#endif
#ifdef ETIME
else if (error == ETIME) { return FS_TIMEOUT; }
#endif
#ifdef ENOSR
else if (error == ENOSR) { return FS_NO_DATA_AVAILABLE; }
#endif
#ifdef ENONET
else if (error == ENONET) { return FS_NO_NETWORK; }
#endif
#ifdef EOVERFLOW
else if (error == EOVERFLOW) { return FS_TOO_BIG; }
#endif
#ifdef ELIBACC
else if (error == ELIBACC) { return FS_ACCESS_DENIED; }
#endif
#ifdef ELIBBAD
else if (error == ELIBBAD) { return FS_INVALID_FILE; }
#endif
#ifdef ELIBSCN
else if (error == ELIBSCN) { return FS_INVALID_FILE; }
#endif
#ifdef EILSEQ
else if (error == EILSEQ) { return FS_INVALID_DATA; }
#endif
#ifdef ENOTSOCK
else if (error == ENOTSOCK) { return FS_NOT_SOCKET; }
#endif
#ifdef EDESTADDRREQ
else if (error == EDESTADDRREQ) { return FS_NO_ADDRESS; }
#endif
#ifdef EMSGSIZE
else if (error == EMSGSIZE) { return FS_TOO_BIG; }
#endif
#ifdef EPROTOTYPE
else if (error == EPROTOTYPE) { return FS_BAD_PROTOCOL; }
#endif
#ifdef ENOPROTOOPT
else if (error == ENOPROTOOPT) { return FS_PROTOCOL_UNAVAILABLE; }
#endif
#ifdef EPROTONOSUPPORT
else if (error == EPROTONOSUPPORT) { return FS_PROTOCOL_NOT_SUPPORTED; }
#endif
#ifdef ESOCKTNOSUPPORT
else if (error == ESOCKTNOSUPPORT) { return FS_SOCKET_NOT_SUPPORTED; }
#endif
#ifdef EOPNOTSUPP
else if (error == EOPNOTSUPP) { return FS_INVALID_OPERATION; }
#endif
#ifdef EPFNOSUPPORT
else if (error == EPFNOSUPPORT) { return FS_PROTOCOL_FAMILY_NOT_SUPPORTED; }
#endif
#ifdef EAFNOSUPPORT
else if (error == EAFNOSUPPORT) { return FS_ADDRESS_FAMILY_NOT_SUPPORTED; }
#endif
#ifdef EADDRINUSE
else if (error == EADDRINUSE) { return FS_ALREADY_IN_USE; }
#endif
#ifdef ENETDOWN
else if (error == ENETDOWN) { return FS_NO_NETWORK; }
#endif
#ifdef ENETUNREACH
else if (error == ENETUNREACH) { return FS_NO_NETWORK; }
#endif
#ifdef ENETRESET
else if (error == ENETRESET) { return FS_NO_NETWORK; }
#endif
#ifdef ECONNABORTED
else if (error == ECONNABORTED) { return FS_NO_NETWORK; }
#endif
#ifdef ECONNRESET
else if (error == ECONNRESET) { return FS_CONNECTION_RESET; }
#endif
#ifdef ENOBUFS
else if (error == ENOBUFS) { return FS_NO_SPACE; }
#endif
#ifdef EISCONN
else if (error == EISCONN) { return FS_ALREADY_CONNECTED; }
#endif
#ifdef ENOTCONN
else if (error == ENOTCONN) { return FS_NOT_CONNECTED; }
#endif
#ifdef ETIMEDOUT
else if (error == ETIMEDOUT) { return FS_TIMEOUT; }
#endif
#ifdef ECONNREFUSED
else if (error == ECONNREFUSED) { return FS_CONNECTION_REFUSED; }
#endif
#ifdef EHOSTDOWN
else if (error == EHOSTDOWN) { return FS_NO_HOST; }
#endif
#ifdef EHOSTUNREACH
else if (error == EHOSTUNREACH) { return FS_NO_HOST; }
#endif
#ifdef EALREADY
else if (error == EALREADY) { return FS_IN_PROGRESS; }
#endif
#ifdef EINPROGRESS
else if (error == EINPROGRESS) { return FS_IN_PROGRESS; }
#endif
#ifdef ESTALE
else if (error == ESTALE) { return FS_INVALID_FILE; }
#endif
#ifdef EREMOTEIO
else if (error == EREMOTEIO) { return FS_IO_ERROR; }
#endif
#ifdef EDQUOT
else if (error == EDQUOT) { return FS_NO_SPACE; }
#endif
#ifdef ENOMEDIUM
else if (error == ENOMEDIUM) { return FS_DOES_NOT_EXIST; }
#endif
#ifdef ECANCELED
else if (error == ECANCELED) { return FS_CANCELLED; }
#endif
return FS_ERROR;
}
#if defined(_WIN32)
#include <windows.h> /* For GetLastError, ERROR_* constants. */
FS_API fs_result fs_result_from_GetLastError(void)
{
switch (GetLastError())
{
case ERROR_SUCCESS: return FS_SUCCESS;
case ERROR_NOT_ENOUGH_MEMORY: return FS_OUT_OF_MEMORY;
case ERROR_OUTOFMEMORY: return FS_OUT_OF_MEMORY;
case ERROR_BUSY: return FS_BUSY;
case ERROR_SEM_TIMEOUT: return FS_TIMEOUT;
case ERROR_ALREADY_EXISTS: return FS_ALREADY_EXISTS;
case ERROR_FILE_EXISTS: return FS_ALREADY_EXISTS;
case ERROR_ACCESS_DENIED: return FS_ACCESS_DENIED;
case ERROR_WRITE_PROTECT: return FS_ACCESS_DENIED;
case ERROR_PRIVILEGE_NOT_HELD: return FS_ACCESS_DENIED;
case ERROR_SHARING_VIOLATION: return FS_ACCESS_DENIED;
case ERROR_LOCK_VIOLATION: return FS_ACCESS_DENIED;
case ERROR_FILE_NOT_FOUND: return FS_DOES_NOT_EXIST;
case ERROR_PATH_NOT_FOUND: return FS_DOES_NOT_EXIST;
case ERROR_INVALID_NAME: return FS_INVALID_ARGS;
case ERROR_BAD_PATHNAME: return FS_INVALID_ARGS;
case ERROR_INVALID_PARAMETER: return FS_INVALID_ARGS;
case ERROR_INVALID_HANDLE: return FS_INVALID_ARGS;
case ERROR_INVALID_FUNCTION: return FS_INVALID_OPERATION;
case ERROR_FILENAME_EXCED_RANGE: return FS_PATH_TOO_LONG;
case ERROR_DIRECTORY: return FS_NOT_DIRECTORY;
case ERROR_DIR_NOT_EMPTY: return FS_DIRECTORY_NOT_EMPTY;
case ERROR_FILE_TOO_LARGE: return FS_TOO_BIG;
case ERROR_DISK_FULL: return FS_OUT_OF_RANGE;
case ERROR_HANDLE_EOF: return FS_AT_END;
case ERROR_SEEK: return FS_BAD_SEEK;
case ERROR_OPERATION_ABORTED: return FS_CANCELLED;
case ERROR_CANCELLED: return FS_INTERRUPT;
case ERROR_TOO_MANY_OPEN_FILES: return FS_TOO_MANY_OPEN_FILES;
case ERROR_INVALID_DATA: return FS_INVALID_DATA;
case ERROR_NO_DATA: return FS_NO_DATA_AVAILABLE;
case ERROR_NOT_SAME_DEVICE: return FS_DIFFERENT_DEVICE;
default: return FS_ERROR; /* Generic error. */
}
}
#endif /* _WIN32 */
/* END fs_result.c */
/* BEG fs_allocation_callbacks.c */
static void* fs_malloc_default(size_t sz, void* pUserData)
{
FS_UNUSED(pUserData);
return FS_MALLOC(sz);
}
static void* fs_realloc_default(void* p, size_t sz, void* pUserData)
{
FS_UNUSED(pUserData);
return FS_REALLOC(p, sz);
}
static void fs_free_default(void* p, void* pUserData)
{
FS_UNUSED(pUserData);
FS_FREE(p);
}
static fs_allocation_callbacks fs_allocation_callbacks_init_default(void)
{
fs_allocation_callbacks allocationCallbacks;
allocationCallbacks.pUserData = NULL;
allocationCallbacks.onMalloc = fs_malloc_default;
allocationCallbacks.onRealloc = fs_realloc_default;
allocationCallbacks.onFree = fs_free_default;
return allocationCallbacks;
}
static fs_allocation_callbacks fs_allocation_callbacks_init_copy(const fs_allocation_callbacks* pAllocationCallbacks)
{
if (pAllocationCallbacks != NULL) {
return *pAllocationCallbacks;
} else {
return fs_allocation_callbacks_init_default();
}
}
FS_API void* fs_malloc(size_t sz, const fs_allocation_callbacks* pAllocationCallbacks)
{
if (pAllocationCallbacks != NULL) {
if (pAllocationCallbacks->onMalloc != NULL) {
return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData);
} else {
return NULL; /* Do not fall back to the default implementation. */
}
} else {
return fs_malloc_default(sz, NULL);
}
}
FS_API void* fs_calloc(size_t sz, const fs_allocation_callbacks* pAllocationCallbacks)
{
void* p = fs_malloc(sz, pAllocationCallbacks);
if (p != NULL) {
FS_ZERO_MEMORY(p, sz);
}
return p;
}
FS_API void* fs_realloc(void* p, size_t sz, const fs_allocation_callbacks* pAllocationCallbacks)
{
if (pAllocationCallbacks != NULL) {
if (pAllocationCallbacks->onRealloc != NULL) {
return pAllocationCallbacks->onRealloc(p, sz, pAllocationCallbacks->pUserData);
} else {
return NULL; /* Do not fall back to the default implementation. */
}
} else {
return fs_realloc_default(p, sz, NULL);
}
}
FS_API void fs_free(void* p, const fs_allocation_callbacks* pAllocationCallbacks)
{
if (p == NULL) {
return;
}
if (pAllocationCallbacks != NULL) {
if (pAllocationCallbacks->onFree != NULL) {
pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
} else {
return; /* Do no fall back to the default implementation. */
}
} else {
fs_free_default(p, NULL);
}
}
/* EN 10452 D fs_allocation_callbacks.c */
/* BEG fs_thread.c */
/*
This section has been designed to be mostly compatible with c89thread with only a few minor changes
if you wanted to amalgamate this into another project which uses c89thread and want to avoid duplicate
code. These are the differences:
* The c89 namespace is replaced with "fs_".
* There is no c89mtx_trylock() or c89mtx_timedlock() equivalent.
* c89thrd_success is FS_SUCCESS
* c89thrd_error is FS_ERROR
* c89thrd_busy is FS_BUSY
* c89thrd_pthread_* is fs_pthread_*
Parameter ordering is the same as c89thread to make amalgamation easier.
*/
/* BEG fs_thread_mtx.c */
#if defined(FS_WIN32) && !defined(FS_USE_PTHREAD)
FS_API int fs_mtx_init(fs_mtx* mutex, int type)
{
HANDLE hMutex;
if (mutex == NULL) {
return FS_ERROR;
}
/* Initialize the object to zero for safety. */
mutex->handle = NULL;
mutex->type = 0;
/*
CreateMutex() will create a thread-aware mutex (allowing recursiveness), whereas an auto-reset
event (CreateEvent()) is not thread-aware and will deadlock (will not allow recursiveness). In
Win32 I'm making all mutex's timeable.
*/
if ((type & fs_mtx_recursive) != 0) {
hMutex = CreateMutexA(NULL, FALSE, NULL);
} else {
hMutex = CreateEventA(NULL, FALSE, TRUE, NULL);
}
if (hMutex == NULL) {
return fs_result_from_GetLastError();
}
mutex->handle = (void*)hMutex;
mutex->type = type;
return FS_SUCCESS;
}
FS_API void fs_mtx_destroy(fs_mtx* mutex)
{
if (mutex == NULL) {
return;
}
CloseHandle((HANDLE)mutex->handle);
}
FS_API int fs_mtx_lock(fs_mtx* mutex)
{
DWORD result;
if (mutex == NULL) {
return FS_ERROR;
}
result = WaitForSingleObject((HANDLE)mutex->handle, INFINITE);
if (result != WAIT_OBJECT_0) {
return FS_ERROR;
}
return FS_SUCCESS;
}
FS_API int fs_mtx_unlock(fs_mtx* mutex)
{
BOOL result;
if (mutex == NULL) {
return FS_ERROR;
}
if ((mutex->type & fs_mtx_recursive) != 0) {
result = ReleaseMutex((HANDLE)mutex->handle);
} else {
result = SetEvent((HANDLE)mutex->handle);
}
if (!result) {
return FS_ERROR;
}
return FS_SUCCESS;
}
#else
FS_API int fs_mtx_init(fs_mtx* mutex, int type)
{
int result;
if (mutex == NULL) {
return FS_ERROR;
}
#ifdef FS_USE_MANUAL_RECURSIVE_MUTEX
{
/* Initialize the main mutex */
result = pthread_mutex_init(&mutex->mutex, NULL);
if (result != 0) {
return FS_ERROR;
}
/* For recursive mutexes, we need the guard mutex and metadata */
if ((type & fs_mtx_recursive) != 0) {
if (pthread_mutex_init(&mutex->guard, NULL) != 0) {
pthread_mutex_destroy(&mutex->mutex);
return FS_ERROR;
}
mutex->owner = 0; /* No owner initially. */
mutex->recursionCount = 0;
}
mutex->type = type;
return FS_SUCCESS;
}
#else
{
pthread_mutexattr_t attr; /* For specifying whether or not the mutex is recursive. */
pthread_mutexattr_init(&attr);
if ((type & fs_mtx_recursive) != 0) {
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
} else {
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL); /* Will deadlock. Consistent with Win32. */
}
result = pthread_mutex_init((pthread_mutex_t*)mutex, &attr);
pthread_mutexattr_destroy(&attr);
if (result != 0) {
return FS_ERROR;
}
return FS_SUCCESS;
}
#endif
}
FS_API void fs_mtx_destroy(fs_mtx* mutex)
{
if (mutex == NULL) {
return;
}
#ifdef FS_USE_MANUAL_RECURSIVE_MUTEX
{
/* Only destroy the guard mutex if it was initialized (for recursive mutexes) */
if ((mutex->type & fs_mtx_recursive) != 0) {
pthread_mutex_destroy(&mutex->guard);
}
pthread_mutex_destroy(&mutex->mutex);
}
#else
{
pthread_mutex_destroy((pthread_mutex_t*)mutex);
}
#endif
}
FS_API int fs_mtx_lock(fs_mtx* mutex)
{
int result;
if (mutex == NULL) {
return FS_ERROR;
}
0