2005-03-18 23:07:55 +01:00
|
|
|
// Copyright (c) 2003-2005 Jan Wassenberg
|
2004-03-03 00:56:51 +01:00
|
|
|
//
|
|
|
|
// This program is free software; you can redistribute it and/or
|
|
|
|
// modify it under the terms of the GNU General Public License as
|
|
|
|
// published by the Free Software Foundation; either version 2 of the
|
|
|
|
// License, or (at your option) any later version.
|
|
|
|
//
|
|
|
|
// This program is distributed in the hope that it will be useful, but
|
|
|
|
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
// General Public License for more details.
|
|
|
|
//
|
|
|
|
// Contact info:
|
|
|
|
// Jan.Wassenberg@stud.uni-karlsruhe.de
|
|
|
|
// http://www.stud.uni-karlsruhe.de/~urkt/
|
|
|
|
|
2005-03-18 23:07:55 +01:00
|
|
|
/*
|
|
|
|
|
|
|
|
[KEEP IN SYNC WITH WIKI]
|
|
|
|
|
|
|
|
low-level aka "lib"
|
|
|
|
-------------------
|
|
|
|
|
|
|
|
this codebase was grown from modules shared between several projects,
|
|
|
|
i.e. my personal library; hence the name "lib". it has been expanded to
|
|
|
|
fit the needs of 0ad - in particular, resource loading.
|
|
|
|
|
|
|
|
owing to the dual-use situation, the 0ad coding conventions are not met;
|
|
|
|
also, major changes are ill-advised because they may break other projects.
|
|
|
|
|
|
|
|
|
|
|
|
design goals
|
|
|
|
------------
|
|
|
|
|
|
|
|
- fast and low-overhead, including startup time
|
|
|
|
- portable: must run on Win32, Mac OS X and Linux
|
|
|
|
- reusable across projects, i.e. no dependency on a
|
|
|
|
central 'manager' that ties modules together.
|
|
|
|
|
|
|
|
|
|
|
|
scope
|
|
|
|
-----
|
|
|
|
|
|
|
|
- POSIX definitions
|
|
|
|
- resource management
|
|
|
|
- debugging tools (including memory tracker)
|
|
|
|
- low-level helper functions, e.g. ADTs, endian conversion and timing
|
|
|
|
- platform-dependent system/feature detection
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef LIB_H__
|
|
|
|
#define LIB_H__
|
2004-03-03 00:56:51 +01:00
|
|
|
|
|
|
|
#include <stddef.h>
|
2005-06-28 06:12:50 +02:00
|
|
|
|
2004-03-03 00:56:51 +01:00
|
|
|
|
|
|
|
#include "config.h"
|
2005-02-26 16:15:36 +01:00
|
|
|
#include "lib/types.h"
|
2004-03-03 00:56:51 +01:00
|
|
|
|
|
|
|
#include "sysdep/sysdep.h"
|
|
|
|
|
|
|
|
|
2005-05-30 06:51:47 +02:00
|
|
|
#if defined(__cplusplus)
|
|
|
|
#define EXTERN_C extern "C"
|
|
|
|
#else
|
|
|
|
#define EXTERN_C extern
|
|
|
|
#endif
|
|
|
|
|
2005-08-09 18:23:19 +02:00
|
|
|
// package code into a single statement.
|
|
|
|
// notes:
|
|
|
|
// - for(;;) { break; } and {} don't work because invocations of macros
|
|
|
|
// implemented with STMT often end with ";", thus breaking if() expressions.
|
|
|
|
// - we'd really like to eliminate "conditional expression is constant"
|
|
|
|
// warnings. replacing 0 literals with extern volatile variables fools
|
|
|
|
// VC7 but isn't guaranteed to be free of overhead. we will just
|
|
|
|
// squelch the warning (unfortunately non-portable).
|
|
|
|
#define STMT(STMT_code__) do { STMT_code__; } while(false)
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2005-04-10 00:24:08 +02:00
|
|
|
// may be called at any time (in particular before main), but is not
|
|
|
|
// thread-safe. if that's important, use pthread_once() instead.
|
2004-03-03 00:56:51 +01:00
|
|
|
#define ONCE(ONCE_code__)\
|
2004-05-06 19:14:30 +02:00
|
|
|
STMT(\
|
2004-03-03 00:56:51 +01:00
|
|
|
static bool ONCE_done__ = false;\
|
2004-06-01 19:34:12 +02:00
|
|
|
if(!ONCE_done__)\
|
2004-03-03 00:56:51 +01:00
|
|
|
{\
|
|
|
|
ONCE_done__ = true;\
|
|
|
|
ONCE_code__;\
|
|
|
|
}\
|
2004-05-06 19:14:30 +02:00
|
|
|
)
|
|
|
|
|
2005-04-10 00:24:08 +02:00
|
|
|
|
2005-07-14 02:19:56 +02:00
|
|
|
// be careful here. the given expression (e.g. variable or
|
|
|
|
// function return value) may be a Handle (=i64), so it needs to be
|
|
|
|
// stored and compared as such. (very large but legitimate Handle values
|
|
|
|
// casted to int can end up negative)
|
|
|
|
// all functions using this return int (instead of i64) for efficiency and
|
|
|
|
// simplicity. if the input was negative, it is an error code and is
|
|
|
|
// therefore known to fit; we still mask with UINT_MAX to avoid
|
|
|
|
// VC cast-to-smaller-type warnings.
|
2004-05-06 19:14:30 +02:00
|
|
|
|
2005-08-09 18:23:19 +02:00
|
|
|
// if expression evaluates to a negative i64, warn user and return the number.
|
|
|
|
#if OS_WIN
|
2005-07-14 02:19:56 +02:00
|
|
|
#define CHECK_ERR(expression)\
|
2004-05-06 19:14:30 +02:00
|
|
|
STMT(\
|
2005-07-14 02:19:56 +02:00
|
|
|
i64 err__ = (i64)(expression);\
|
2004-06-21 16:16:10 +02:00
|
|
|
if(err__ < 0)\
|
2004-05-13 15:52:48 +02:00
|
|
|
{\
|
2005-07-14 02:19:56 +02:00
|
|
|
debug_warn("FYI: CHECK_ERR reports that a function failed."\
|
|
|
|
"feel free to ignore or suppress this warning.");\
|
|
|
|
return (int)(err__ & UINT_MAX);\
|
2004-05-13 15:52:48 +02:00
|
|
|
}\
|
2004-05-06 19:14:30 +02:00
|
|
|
)
|
2004-06-02 17:12:48 +02:00
|
|
|
#else
|
2005-07-14 02:19:56 +02:00
|
|
|
#define CHECK_ERR(expression)\
|
2004-06-02 17:12:48 +02:00
|
|
|
STMT(\
|
2005-07-14 02:19:56 +02:00
|
|
|
i64 err__ = (i64)(expression);\
|
2004-06-21 16:16:10 +02:00
|
|
|
if(err__ < 0)\
|
2004-06-02 17:12:48 +02:00
|
|
|
{\
|
2005-05-12 00:40:19 +02:00
|
|
|
debug_printf("%s:%d: FYI: CHECK_ERR reports that a function failed."\
|
2004-06-09 15:46:06 +02:00
|
|
|
"feel free to ignore or suppress this warning.\n", __FILE__, __LINE__);\
|
2005-07-14 02:19:56 +02:00
|
|
|
return (int)(err__ & UINT_MAX);\
|
2004-06-02 17:12:48 +02:00
|
|
|
}\
|
|
|
|
)
|
|
|
|
#endif
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2005-03-27 19:40:40 +02:00
|
|
|
// just pass on errors without any kind of annoying warning
|
|
|
|
// (useful for functions that can legitimately fail, e.g. vfs_exists).
|
2005-07-14 02:19:56 +02:00
|
|
|
#define RETURN_ERR(expression)\
|
2005-03-27 19:40:40 +02:00
|
|
|
STMT(\
|
2005-07-14 02:19:56 +02:00
|
|
|
i64 err__ = (i64)(expression);\
|
2005-03-27 19:40:40 +02:00
|
|
|
if(err__ < 0)\
|
2005-07-14 02:19:56 +02:00
|
|
|
return (int)(err__ & UINT_MAX);\
|
2005-03-27 19:40:40 +02:00
|
|
|
)
|
|
|
|
|
2005-08-09 18:23:19 +02:00
|
|
|
// if expression evaluates to a negative i64, warn user and throw the number.
|
2005-07-14 02:19:56 +02:00
|
|
|
#define THROW_ERR(expression)\
|
2005-03-22 03:17:55 +01:00
|
|
|
STMT(\
|
2005-07-14 02:19:56 +02:00
|
|
|
i64 err__ = (i64)(expression);\
|
2005-03-22 03:17:55 +01:00
|
|
|
if(err__ < 0)\
|
|
|
|
{\
|
2005-08-09 18:23:19 +02:00
|
|
|
debug_warn("FYI: THROW_ERR reports that a function failed."\
|
2005-07-14 02:19:56 +02:00
|
|
|
"feel free to ignore or suppress this warning.");\
|
|
|
|
throw (int)(err__ & UINT_MAX);\
|
2005-03-22 03:17:55 +01:00
|
|
|
}\
|
|
|
|
)
|
|
|
|
|
2005-08-09 18:23:19 +02:00
|
|
|
// if expression evaluates to a negative i64, warn user and just return
|
|
|
|
// (useful for void functions that must bail and complain)
|
|
|
|
#define WARN_ERR_RETURN(expression)\
|
|
|
|
STMT(\
|
|
|
|
i64 err__ = (i64)(expression);\
|
|
|
|
if(err__ < 0)\
|
|
|
|
{\
|
|
|
|
debug_warn("FYI: WARN_ERR_RETURN reports that a function failed."\
|
|
|
|
"feel free to ignore or suppress this warning.");\
|
|
|
|
return;\
|
|
|
|
}\
|
|
|
|
)
|
|
|
|
|
|
|
|
// if expression evaluates to a negative i64, warn user
|
|
|
|
// (this is similar to debug_assert but also works in release mode)
|
|
|
|
#define WARN_ERR(expression)\
|
|
|
|
STMT(\
|
|
|
|
i64 err__ = (i64)(expression);\
|
|
|
|
if(err__ < 0)\
|
|
|
|
debug_warn("FYI: WARN_ERR reports that a function failed."\
|
|
|
|
"feel free to ignore or suppress this warning.");\
|
|
|
|
)
|
2004-06-08 14:10:51 +02:00
|
|
|
|
|
|
|
|
2005-06-16 23:46:33 +02:00
|
|
|
|
2005-03-18 23:07:55 +01:00
|
|
|
// useful because VC6 may return 0 on failure, instead of throwing.
|
2004-06-18 15:24:59 +02:00
|
|
|
// this wraps the exception handling, and creates a NULL pointer on failure.
|
2004-06-08 14:10:51 +02:00
|
|
|
#define SAFE_NEW(type, ptr)\
|
|
|
|
type* ptr;\
|
|
|
|
try\
|
|
|
|
{\
|
|
|
|
ptr = new type();\
|
|
|
|
}\
|
|
|
|
catch(std::bad_alloc)\
|
|
|
|
{\
|
|
|
|
ptr = 0;\
|
2004-06-18 15:24:59 +02:00
|
|
|
}
|
2004-06-08 14:10:51 +02:00
|
|
|
|
|
|
|
|
2004-07-14 05:06:15 +02:00
|
|
|
|
2004-03-03 00:56:51 +01:00
|
|
|
enum LibError
|
|
|
|
{
|
2005-06-16 23:46:33 +02:00
|
|
|
// function arguments
|
2004-12-01 22:37:01 +01:00
|
|
|
ERR_INVALID_PARAM = -1000,
|
|
|
|
ERR_INVALID_HANDLE = -1001,
|
2005-06-16 23:46:33 +02:00
|
|
|
ERR_BUF_SIZE = -1002,
|
|
|
|
|
|
|
|
// system limitations
|
|
|
|
// .. out of memory
|
|
|
|
ERR_NO_MEM = -1020,
|
|
|
|
// .. try again later
|
|
|
|
ERR_AGAIN = -1021,
|
|
|
|
// .. fixed limit exceeded
|
|
|
|
ERR_LIMIT = -1022,
|
|
|
|
// .. the OS doesn't provide an API we need
|
|
|
|
ERR_NO_SYS = -1023,
|
|
|
|
// .. feature not currently implemented (will probably change)
|
|
|
|
ERR_NOT_IMPLEMENTED = -1024,
|
|
|
|
// .. feature won't be supported
|
|
|
|
ERR_NOT_SUPPORTED = -1025,
|
2004-12-01 22:37:01 +01:00
|
|
|
|
2005-08-09 18:23:19 +02:00
|
|
|
// data (e.g. in file) is obviously incorrect
|
2005-06-16 23:46:33 +02:00
|
|
|
ERR_CORRUPTED = -1040,
|
2004-12-01 22:37:01 +01:00
|
|
|
|
2005-06-16 23:46:33 +02:00
|
|
|
ERR_UNKNOWN_FORMAT = -1050,
|
2004-12-01 22:37:01 +01:00
|
|
|
|
2005-06-16 23:46:33 +02:00
|
|
|
ERR_TIMED_OUT = -1060,
|
2005-01-10 14:42:10 +01:00
|
|
|
|
2004-12-01 22:37:01 +01:00
|
|
|
// file + vfs
|
2005-06-16 23:46:33 +02:00
|
|
|
ERR_FILE_NOT_FOUND = -1200,
|
|
|
|
ERR_PATH_NOT_FOUND = -1201,
|
2005-08-09 18:23:19 +02:00
|
|
|
ERR_PATH_LENGTH = -1202,
|
|
|
|
ERR_PATH_INVALID = -1203,
|
|
|
|
ERR_DIR_END = -1210,
|
|
|
|
ERR_NOT_FILE = -1220,
|
|
|
|
ERR_FILE_ACCESS = -1230,
|
|
|
|
ERR_IO = -1231,
|
|
|
|
ERR_EOF = -1232,
|
2004-12-01 22:37:01 +01:00
|
|
|
|
|
|
|
|
2005-06-16 23:46:33 +02:00
|
|
|
ERR_TEX_FMT_INVALID = -1300,
|
2004-12-01 22:37:01 +01:00
|
|
|
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2004-05-06 19:14:30 +02:00
|
|
|
ERR_LAST
|
2004-03-03 00:56:51 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2004-11-23 22:08:59 +01:00
|
|
|
|
|
|
|
|
2004-03-03 00:56:51 +01:00
|
|
|
#ifndef MIN
|
|
|
|
#define MIN(a, b) (((a) < (b))? (a) : (b))
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef MAX
|
|
|
|
#define MAX(a, b) (((a) > (b))? (a) : (b))
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2005-03-18 23:07:55 +01:00
|
|
|
// 2 ways of avoiding "unreferenced formal parameter" warnings:
|
2005-08-09 18:23:19 +02:00
|
|
|
// .. inside the function body, e.g. void f(int x) { UNUSED2(x); }
|
|
|
|
#define UNUSED2(param) (void)param;
|
|
|
|
// .. wrapped around the parameter name, e.g. void f(int UNUSED(x))
|
|
|
|
#define UNUSED(param)
|
|
|
|
|
|
|
|
#if MSC_VERSION
|
|
|
|
#define UNREACHABLE __assume(0)
|
|
|
|
#endif
|
2004-03-03 00:56:51 +01:00
|
|
|
|
|
|
|
|
2004-10-06 16:11:55 +02:00
|
|
|
#define ARRAY_SIZE(name) (sizeof(name) / sizeof(name[0]))
|
|
|
|
|
2004-03-03 00:56:51 +01:00
|
|
|
|
|
|
|
//
|
2005-06-28 06:12:50 +02:00
|
|
|
// compile-time debug_assert, especially useful for testing sizeof().
|
2004-03-03 00:56:51 +01:00
|
|
|
// no runtime overhead; may be used anywhere, including file scope.
|
|
|
|
//
|
|
|
|
|
|
|
|
// generate a symbol containing the line number of the macro invocation.
|
|
|
|
// used to give a unique name (per file) to types made by cassert.
|
2004-05-06 19:14:30 +02:00
|
|
|
// we can't prepend __FILE__ to make it globally unique - the filename
|
|
|
|
// may be enclosed in quotes. need the 2 macro expansions to make sure
|
|
|
|
// __LINE__ is expanded correctly.
|
2004-03-03 00:56:51 +01:00
|
|
|
#define MAKE_UID2__(l) LINE_ ## l
|
|
|
|
#define MAKE_UID1__(l) MAKE_UID2__(l)
|
|
|
|
#define UID__ MAKE_UID1__(__LINE__)
|
|
|
|
|
|
|
|
// more descriptive error message, but may cause a struct redefinition
|
|
|
|
// warning if used from the same line in different files.
|
2004-06-22 15:22:46 +02:00
|
|
|
#define cassert(expr) struct UID__ { int CASSERT_FAILURE: (expr); }
|
2004-03-03 00:56:51 +01:00
|
|
|
|
|
|
|
// less helpful error message, but redefinition doesn't trigger warnings.
|
2004-06-22 15:22:46 +02:00
|
|
|
#define cassert2(expr) extern char CASSERT_FAILURE[1][(expr)]
|
2004-03-03 00:56:51 +01:00
|
|
|
|
|
|
|
// note: alternative method in C++: specialize a struct only for true;
|
2004-05-06 19:14:30 +02:00
|
|
|
// using it will raise 'incomplete type' errors if instantiated with false.
|
|
|
|
|
2004-03-03 00:56:51 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2005-01-23 18:54:20 +01:00
|
|
|
const size_t KiB = 1ul << 10;
|
|
|
|
const size_t MiB = 1ul << 20;
|
|
|
|
const size_t GiB = 1ul << 30;
|
2004-03-03 00:56:51 +01:00
|
|
|
|
|
|
|
|
2005-08-09 18:23:19 +02:00
|
|
|
#if OS_WIN
|
2004-03-03 00:56:51 +01:00
|
|
|
#define DIR_SEP '\\'
|
|
|
|
#else
|
|
|
|
#define DIR_SEP '/'
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
#define BIT(n) (1ul << (n))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2004-06-01 19:34:12 +02:00
|
|
|
|
|
|
|
// FNV1-A hash - good for strings.
|
|
|
|
// if len = 0 (default), treat buf as a C-string;
|
|
|
|
// otherwise, hash <len> bytes of buf.
|
2005-08-11 04:02:10 +02:00
|
|
|
extern u32 fnv_hash(const void* buf, size_t len = 0);
|
|
|
|
extern u64 fnv_hash64(const void* buf, size_t len = 0);
|
2004-06-01 19:34:12 +02:00
|
|
|
|
2005-03-27 03:31:39 +02:00
|
|
|
// special version for strings: first converts to lowercase
|
|
|
|
// (useful for comparing mixed-case filenames)
|
2005-08-11 04:02:10 +02:00
|
|
|
extern u32 fnv_lc_hash(const char* str, size_t len = 0);
|
2005-03-27 03:31:39 +02:00
|
|
|
|
2004-06-01 19:34:12 +02:00
|
|
|
// hash (currently FNV) of a filename
|
|
|
|
typedef u32 FnHash;
|
|
|
|
|
|
|
|
|
|
|
|
extern u16 addusw(u16 x, u16 y);
|
|
|
|
extern u16 subusw(u16 x, u16 y);
|
|
|
|
|
2005-05-27 06:40:29 +02:00
|
|
|
// zero-extend <size> (truncated to 8) bytes of little-endian data to u64,
|
|
|
|
// starting at address <p> (need not be aligned).
|
|
|
|
extern u64 movzx_64le(const u8* p, size_t size);
|
|
|
|
|
|
|
|
// sign-extend <size> (truncated to 8) bytes of little-endian data to i64,
|
|
|
|
// starting at address <p> (need not be aligned).
|
|
|
|
extern i64 movsx_64le(const u8* p, size_t size);
|
2004-06-01 19:34:12 +02:00
|
|
|
|
|
|
|
|
2005-08-11 04:02:10 +02:00
|
|
|
extern bool is_pow2(uint n);
|
2004-06-01 19:34:12 +02:00
|
|
|
|
|
|
|
// return -1 if not an integral power of 2,
|
|
|
|
// otherwise the base2 logarithm
|
2005-08-11 04:02:10 +02:00
|
|
|
extern int ilog2(uint n);
|
2004-06-01 19:34:12 +02:00
|
|
|
|
2004-06-19 16:43:31 +02:00
|
|
|
// return log base 2, rounded up.
|
|
|
|
extern uint log2(uint x);
|
|
|
|
|
2004-06-01 19:34:12 +02:00
|
|
|
|
2005-05-03 07:05:16 +02:00
|
|
|
// multiple must be a power of two.
|
2004-06-01 19:34:12 +02:00
|
|
|
extern uintptr_t round_up(uintptr_t val, uintptr_t multiple);
|
|
|
|
|
2005-08-09 18:23:19 +02:00
|
|
|
// these avoid a common mistake in using >> (ANSI requires shift count be
|
|
|
|
// less than the bit width of the type).
|
|
|
|
extern u32 u64_hi(u64 x);
|
|
|
|
extern u32 u64_lo(u64 x);
|
|
|
|
|
2005-08-12 19:06:53 +02:00
|
|
|
extern u64 u64_from_u32(u32 hi, u32 lo);
|
|
|
|
|
2005-08-09 18:23:19 +02:00
|
|
|
|
2004-06-01 19:34:12 +02:00
|
|
|
extern u16 fp_to_u16(double in);
|
|
|
|
|
2005-04-26 18:47:48 +02:00
|
|
|
// return random integer in [0, limit).
|
|
|
|
// does not use poorly distributed lower bits of rand().
|
|
|
|
extern int rand_up_to(int limit);
|
|
|
|
|
2004-06-01 19:34:12 +02:00
|
|
|
// big endian!
|
|
|
|
extern void base32(const int len, const u8* in, u8* out);
|
|
|
|
|
|
|
|
|
2004-11-23 21:52:03 +01:00
|
|
|
// case-insensitive check if string <s> matches the pattern <w>,
|
|
|
|
// which may contain '?' or '*' wildcards. if so, return 1, otherwise 0.
|
2005-03-27 03:31:39 +02:00
|
|
|
// note: NULL wildcard pattern matches everything!
|
2004-11-23 21:52:03 +01:00
|
|
|
extern int match_wildcard(const char* s, const char* w);
|
2005-06-28 06:12:50 +02:00
|
|
|
extern int match_wildcardw(const wchar_t* s, const wchar_t* w);
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2005-03-18 23:07:55 +01:00
|
|
|
#endif // #ifndef LIB_H__
|