From f72176c750b033329e33edf7aa7af524bf513fb9 Mon Sep 17 00:00:00 2001 From: Yonggang Luo Date: Wed, 9 Dec 2020 11:27:02 +0000 Subject: [PATCH] Split quickjs-port.h out Move all platform related functions into quickjs-port.h and quickjs-port.c Add full msvc support. BOOL conflict with Windows.h, do not define it in header file, define it in c file directly Now run-test262 pass all tests match unix platform after add atomic/thread support on win32 by using quickjs-port.h win/stdatomic.h comes from https://github.com/cdschreiber/c11 win/dirent.h comes from https://github.com/tronkko/dirent win/getopt.h comes from mingw Now `make test2-update` passed all tests under msys2/mingw Signed-off-by: Yonggang Luo --- Makefile | 5 +- cutils.c | 6 +- cutils.h | 89 ++- libbf.c | 2 + libregexp.c | 2 + libunicode.c | 2 + qjs.c | 73 +-- qjsc.c | 10 +- quickjs-libc.c | 55 +- quickjs-port.c | 510 +++++++++++++++ quickjs-port.h | 151 +++++ quickjs.c | 252 ++------ quickjs.h | 4 + run-test262.c | 95 +-- unicode_gen.c | 2 + win/dirent.h | 1166 +++++++++++++++++++++++++++++++++++ win/getopt.h | 653 ++++++++++++++++++++ win/stdatomic.h | 1575 +++++++++++++++++++++++++++++++++++++++++++++++ 18 files changed, 4328 insertions(+), 324 deletions(-) create mode 100644 quickjs-port.c create mode 100644 quickjs-port.h create mode 100644 win/dirent.h create mode 100644 win/getopt.h create mode 100644 win/stdatomic.h diff --git a/Makefile b/Makefile index e6ae8276d..2e2740433 100644 --- a/Makefile +++ b/Makefile @@ -25,6 +25,9 @@ ifeq ($(shell uname -s),Darwin) CONFIG_DARWIN=y endif +ifeq ($(OS),Windows_NT) +CONFIG_WIN32=y +endif # Windows cross compilation from Linux #CONFIG_WIN32=y # use link time optimization (smaller and faster executables but slower build) @@ -166,7 +169,7 @@ endif all: $(OBJDIR) $(OBJDIR)/quickjs.check.o $(OBJDIR)/qjs.check.o $(PROGS) -QJS_LIB_OBJS=$(OBJDIR)/quickjs.o $(OBJDIR)/libregexp.o $(OBJDIR)/libunicode.o $(OBJDIR)/cutils.o $(OBJDIR)/quickjs-libc.o +QJS_LIB_OBJS=$(OBJDIR)/quickjs.o $(OBJDIR)/libregexp.o $(OBJDIR)/libunicode.o $(OBJDIR)/cutils.o $(OBJDIR)/quickjs-libc.o $(OBJDIR)/quickjs-port.o QJS_OBJS=$(OBJDIR)/qjs.o $(OBJDIR)/repl.o $(QJS_LIB_OBJS) ifdef CONFIG_BIGNUM diff --git a/cutils.c b/cutils.c index a02fb7688..4589045e9 100644 --- a/cutils.c +++ b/cutils.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "cutils.h" @@ -89,15 +90,14 @@ static void *dbuf_default_realloc(void *opaque, void *ptr, size_t size) void dbuf_init2(DynBuf *s, void *opaque, DynBufReallocFunc *realloc_func) { memset(s, 0, sizeof(*s)); - if (!realloc_func) - realloc_func = dbuf_default_realloc; + assert(realloc_func != NULL); s->opaque = opaque; s->realloc_func = realloc_func; } void dbuf_init(DynBuf *s) { - dbuf_init2(s, NULL, NULL); + dbuf_init2(s, NULL, dbuf_default_realloc); } /* return < 0 if error */ diff --git a/cutils.h b/cutils.h index 31f7cd84a..b6b165b24 100644 --- a/cutils.h +++ b/cutils.h @@ -28,14 +28,29 @@ #include #include +#ifdef _MSC_VER +#include +#endif + /* set if CPU is big endian */ #undef WORDS_BIGENDIAN +#if defined(_MSC_VER) +#define likely(x) (x) +#define unlikely(x) (x) +#define force_inline __forceinline +#define no_inline __declspec(noinline) +#define __maybe_unused +#define __attribute__(x) +#define __attribute(x) +typedef intptr_t ssize_t; +#else #define likely(x) __builtin_expect(!!(x), 1) #define unlikely(x) __builtin_expect(!!(x), 0) #define force_inline inline __attribute__((always_inline)) #define no_inline __attribute__((noinline)) #define __maybe_unused __attribute__((unused)) +#endif #define xglue(x, y) x ## y #define glue(x, y) xglue(x, y) @@ -49,7 +64,6 @@ #define countof(x) (sizeof(x) / sizeof((x)[0])) #endif -typedef int BOOL; #ifndef FALSE enum { @@ -114,27 +128,91 @@ static inline int64_t min_int64(int64_t a, int64_t b) /* WARNING: undefined if a = 0 */ static inline int clz32(unsigned int a) { +#ifdef _MSC_VER + unsigned long idx; + _BitScanReverse(&idx, a); + return 31 ^ idx; +#else return __builtin_clz(a); +#endif } /* WARNING: undefined if a = 0 */ static inline int clz64(uint64_t a) { - return __builtin_clzll(a); +#ifdef _MSC_VER + unsigned long where; + // BitScanReverse scans from MSB to LSB for first set bit. + // Returns 0 if no set bit is found. +#if INTPTR_MAX >= INT64_MAX // 64-bit + if (_BitScanReverse64(&where, a)) + return (int)(63 - where); +#else + // Scan the high 32 bits. + if (_BitScanReverse(&where, (uint32_t)(a >> 32))) + return (int)(63 - (where + 32)); // Create a bit offset from the MSB. + // Scan the low 32 bits. + if (_BitScanReverse(&where, (uint32_t)(a))) + return (int)(63 - where); +#endif + return 64; // Undefined Behavior. +#else + return __builtin_clzll(a); +#endif } /* WARNING: undefined if a = 0 */ static inline int ctz32(unsigned int a) { +#ifdef _MSC_VER + unsigned long idx; + _BitScanForward(&idx, a); + return idx; +#else return __builtin_ctz(a); +#endif } /* WARNING: undefined if a = 0 */ static inline int ctz64(uint64_t a) { - return __builtin_ctzll(a); +#ifdef _MSC_VER + unsigned long where; + // Search from LSB to MSB for first set bit. + // Returns zero if no set bit is found. +#if INTPTR_MAX >= INT64_MAX // 64-bit + if (_BitScanForward64(&where, a)) + return (int)(where); +#else + // Win32 doesn't have _BitScanForward64 so emulate it with two 32 bit calls. + // Scan the Low Word. + if (_BitScanForward(&where, (uint32_t)(a))) + return (int)(where); + // Scan the High Word. + if (_BitScanForward(&where, (uint32_t)(a >> 32))) + return (int)(where + 32); // Create a bit offset from the LSB. +#endif + return 64; +#else + return __builtin_ctzll(a); +#endif } +#ifdef _MSC_VER +#pragma pack(push, 1) +struct packed_u64 { + uint64_t v; +}; + +struct packed_u32 { + uint32_t v; +}; + +struct packed_u16 { + uint16_t v; +}; +#pragma pack(pop) +#else struct __attribute__((packed)) packed_u64 { uint64_t v; }; @@ -146,6 +224,7 @@ struct __attribute__((packed)) packed_u32 { struct __attribute__((packed)) packed_u16 { uint16_t v; }; +#endif static inline uint64_t get_u64(const uint8_t *tab) { @@ -237,7 +316,7 @@ typedef struct DynBuf { uint8_t *buf; size_t size; size_t allocated_size; - BOOL error; /* true if a memory allocation error occurred */ + int error; /* true if a memory allocation error occurred */ DynBufReallocFunc *realloc_func; void *opaque; /* for realloc_func */ } DynBuf; @@ -265,7 +344,7 @@ static inline int dbuf_put_u64(DynBuf *s, uint64_t val) int __attribute__((format(printf, 2, 3))) dbuf_printf(DynBuf *s, const char *fmt, ...); void dbuf_free(DynBuf *s); -static inline BOOL dbuf_error(DynBuf *s) { +static inline int dbuf_error(DynBuf *s) { return s->error; } static inline void dbuf_set_error(DynBuf *s) diff --git a/libbf.c b/libbf.c index 52ccc4c92..3a2f3e2de 100644 --- a/libbf.c +++ b/libbf.c @@ -35,6 +35,8 @@ #include "cutils.h" #include "libbf.h" +#define BOOL int + /* enable it to check the multiplication result */ //#define USE_MUL_CHECK /* enable it to use FFT/NTT multiplication */ diff --git a/libregexp.c b/libregexp.c index 379bfc7a9..31eb3fdbe 100644 --- a/libregexp.c +++ b/libregexp.c @@ -31,6 +31,8 @@ #include "cutils.h" #include "libregexp.h" +#define BOOL int + /* TODO: diff --git a/libunicode.c b/libunicode.c index 63c12a077..0c6dc61f5 100644 --- a/libunicode.c +++ b/libunicode.c @@ -31,6 +31,8 @@ #include "libunicode.h" #include "libunicode-table.h" +#define BOOL int + enum { RUN_TYPE_U, RUN_TYPE_L, diff --git a/qjs.c b/qjs.c index 4dd11f83b..2be3afe74 100644 --- a/qjs.c +++ b/qjs.c @@ -28,18 +28,24 @@ #include #include #include -#include #include #include #include #if defined(__APPLE__) #include +#include #elif defined(__linux__) #include +#include #endif #include "cutils.h" #include "quickjs-libc.h" +#include "quickjs-port.h" + +#define malloc(s) malloc_is_forbidden(s) +#define free(p) free_is_forbidden(p) +#define realloc(p,s) realloc_is_forbidden(p,s) extern const uint8_t qjsc_repl[]; extern const uint32_t qjsc_repl_size; @@ -139,24 +145,7 @@ static inline unsigned long long js_trace_malloc_ptr_offset(uint8_t *ptr, return ptr - dp->base; } -/* default memory allocation functions with memory limitation */ -static inline size_t js_trace_malloc_usable_size(void *ptr) -{ -#if defined(__APPLE__) - return malloc_size(ptr); -#elif defined(_WIN32) - return _msize(ptr); -#elif defined(EMSCRIPTEN) - return 0; -#elif defined(__linux__) - return malloc_usable_size(ptr); -#else - /* change this to `return 0;` if compilation fails */ - return malloc_usable_size(ptr); -#endif -} - -static void __attribute__((format(printf, 2, 3))) +static void __js_printf_like(2, 3) js_trace_malloc_printf(JSMallocState *s, const char *fmt, ...) { va_list ap; @@ -173,7 +162,7 @@ static void __attribute__((format(printf, 2, 3))) } else { printf("H%+06lld.%zd", js_trace_malloc_ptr_offset(ptr, s->opaque), - js_trace_malloc_usable_size(ptr)); + qjs_malloc_usable_size(ptr)); } fmt++; continue; @@ -192,7 +181,7 @@ static void __attribute__((format(printf, 2, 3))) static void js_trace_malloc_init(struct trace_malloc_data *s) { - free(s->base = malloc(8)); + qjs_free(s->base = qjs_malloc(8)); } static void *js_trace_malloc(JSMallocState *s, size_t size) @@ -204,11 +193,11 @@ static void *js_trace_malloc(JSMallocState *s, size_t size) if (unlikely(s->malloc_size + size > s->malloc_limit)) return NULL; - ptr = malloc(size); + ptr = qjs_malloc(size); js_trace_malloc_printf(s, "A %zd -> %p\n", size, ptr); if (ptr) { s->malloc_count++; - s->malloc_size += js_trace_malloc_usable_size(ptr) + MALLOC_OVERHEAD; + s->malloc_size += qjs_malloc_usable_size(ptr) + MALLOC_OVERHEAD; } return ptr; } @@ -220,8 +209,8 @@ static void js_trace_free(JSMallocState *s, void *ptr) js_trace_malloc_printf(s, "F %p\n", ptr); s->malloc_count--; - s->malloc_size -= js_trace_malloc_usable_size(ptr) + MALLOC_OVERHEAD; - free(ptr); + s->malloc_size -= qjs_malloc_usable_size(ptr) + MALLOC_OVERHEAD; + qjs_free(ptr); } static void *js_trace_realloc(JSMallocState *s, void *ptr, size_t size) @@ -233,12 +222,12 @@ static void *js_trace_realloc(JSMallocState *s, void *ptr, size_t size) return NULL; return js_trace_malloc(s, size); } - old_size = js_trace_malloc_usable_size(ptr); + old_size = qjs_malloc_usable_size(ptr); if (size == 0) { js_trace_malloc_printf(s, "R %zd %p\n", size, ptr); s->malloc_count--; s->malloc_size -= old_size + MALLOC_OVERHEAD; - free(ptr); + qjs_free(ptr); return NULL; } if (s->malloc_size + size - old_size > s->malloc_limit) @@ -246,10 +235,10 @@ static void *js_trace_realloc(JSMallocState *s, void *ptr, size_t size) js_trace_malloc_printf(s, "R %zd %p", size, ptr); - ptr = realloc(ptr, size); + ptr = qjs_realloc(ptr, size); js_trace_malloc_printf(s, " -> %p\n", ptr); if (ptr) { - s->malloc_size += js_trace_malloc_usable_size(ptr) - old_size; + s->malloc_size += qjs_malloc_usable_size(ptr) - old_size; } return ptr; } @@ -258,18 +247,7 @@ static const JSMallocFunctions trace_mf = { js_trace_malloc, js_trace_free, js_trace_realloc, -#if defined(__APPLE__) - malloc_size, -#elif defined(_WIN32) - (size_t (*)(const void *))_msize, -#elif defined(EMSCRIPTEN) - NULL, -#elif defined(__linux__) - (size_t (*)(const void *))malloc_usable_size, -#else - /* change this to `NULL,` if compilation fails */ - malloc_usable_size, -#endif + qjs_malloc_usable_size, }; #define PROG_NAME "qjs" @@ -326,6 +304,14 @@ int main(int argc, char **argv) const char *p, *exename; exename = argv[0]; p = strrchr(exename, '/'); +#ifdef _WIN32 + { + const char *q = strrchr(exename, '\\'); + if (p == NULL || q > p) { + p = q; + } + } +#endif if (p) exename = p + 1; load_jscalc = !strcmp(exename, "qjscalc"); @@ -448,9 +434,11 @@ int main(int argc, char **argv) } } +#ifdef CONFIG_BIGNUM if (load_jscalc) bignum_ext = 1; - +#endif + JS_Initialize(); if (trace_memory) { js_trace_malloc_init(&trace_data); rt = JS_NewRuntime2(&trace_mf, &trace_data); @@ -560,5 +548,6 @@ int main(int argc, char **argv) js_std_free_handlers(rt); JS_FreeContext(ctx); JS_FreeRuntime(rt); + JS_Finalize(); return 1; } diff --git a/qjsc.c b/qjsc.c index f5bda57f6..fb6bc44b9 100644 --- a/qjsc.c +++ b/qjsc.c @@ -27,10 +27,15 @@ #include #include #include -#include #include #if !defined(_WIN32) #include +#include +#include "getopt.h" +#define BOOL int +#else +#include +#include "win/getopt.h" #endif #include "cutils.h" @@ -628,7 +633,7 @@ int main(int argc, char **argv) exit(1); } outfile = fo; - + JS_Initialize(); rt = JS_NewRuntime(); ctx = JS_NewContext(rt); #ifdef CONFIG_BIGNUM @@ -748,6 +753,7 @@ int main(int argc, char **argv) JS_FreeContext(ctx); JS_FreeRuntime(rt); + JS_Finalize(); fclose(fo); diff --git a/quickjs-libc.c b/quickjs-libc.c index e8b81e910..b764bbf27 100644 --- a/quickjs-libc.c +++ b/quickjs-libc.c @@ -25,23 +25,33 @@ #include #include #include -#include +#include #include #include -#include #include #include -#include #include #include #include #include -#include #if defined(_WIN32) #include #include -#include +#include +#include +#include +#include +#include +#include "win/dirent.h" +#ifndef PATH_MAX +#define PATH_MAX MAX_PATH +#endif +#define popen _popen +#define pclose _pclose #else +#include +#include +#include #include #include #include @@ -54,7 +64,7 @@ typedef sig_t sighandler_t; #define environ (*_NSGetEnviron()) #endif #endif /* __APPLE__ */ - +#define BOOL int #endif #if !defined(_WIN32) @@ -70,6 +80,7 @@ typedef sig_t sighandler_t; #include "cutils.h" #include "list.h" #include "quickjs-libc.h" +#include "quickjs-port.h" /* TODO: - add socket calls @@ -564,12 +575,19 @@ int js_module_set_import_meta(JSContext *ctx, JSValueConst func_val, return 0; } +#if defined(_WIN32) +#define NATIVE_LIBRARY_SUFFIX ".dll" +#elif defined(__APPLE__) +#define NATIVE_LIBRARY_SUFFIX ".dylib" +#elif defined(__linux__) +#define NATIVE_LIBRARY_SUFFIX ".so" +#endif JSModuleDef *js_module_loader(JSContext *ctx, const char *module_name, void *opaque) { JSModuleDef *m; - if (has_suffix(module_name, ".so")) { + if (has_suffix(module_name, NATIVE_LIBRARY_SUFFIX) || has_suffix(module_name, ".module")) { m = js_module_loader_so(ctx, module_name); } else { size_t buf_len; @@ -1943,23 +1961,6 @@ static JSValue js_os_signal(JSContext *ctx, JSValueConst this_val, return JS_UNDEFINED; } -#if defined(__linux__) || defined(__APPLE__) -static int64_t get_time_ms(void) -{ - struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - return (uint64_t)ts.tv_sec * 1000 + (ts.tv_nsec / 1000000); -} -#else -/* more portable, but does not work if the date is updated */ -static int64_t get_time_ms(void) -{ - struct timeval tv; - gettimeofday(&tv, NULL); - return (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000); -} -#endif - static void unlink_timer(JSRuntime *rt, JSOSTimer *th) { if (th->link.prev) { @@ -2019,7 +2020,7 @@ static JSValue js_os_setTimeout(JSContext *ctx, JSValueConst this_val, return JS_EXCEPTION; } th->has_object = TRUE; - th->timeout = get_time_ms() + delay; + th->timeout = qjs_get_time_ms() + delay; th->func = JS_DupValue(ctx, func); list_add_tail(&th->link, &ts->os_timers); JS_SetOpaque(obj, th); @@ -2073,7 +2074,7 @@ static int js_os_poll(JSContext *ctx) /* XXX: only timers and basic console input are supported */ if (!list_empty(&ts->os_timers)) { - cur_time = get_time_ms(); + cur_time = qjs_get_time_ms(); min_delay = 10000; list_for_each(el, &ts->os_timers) { JSOSTimer *th = list_entry(el, JSOSTimer, link); @@ -2242,7 +2243,7 @@ static int js_os_poll(JSContext *ctx) return -1; /* no more events */ if (!list_empty(&ts->os_timers)) { - cur_time = get_time_ms(); + cur_time = qjs_get_time_ms(); min_delay = 10000; list_for_each(el, &ts->os_timers) { JSOSTimer *th = list_entry(el, JSOSTimer, link); diff --git a/quickjs-port.c b/quickjs-port.c new file mode 100644 index 000000000..bc1fdf2b5 --- /dev/null +++ b/quickjs-port.c @@ -0,0 +1,510 @@ +#include +#include +#include +#include +#include +#include + +#if defined(__APPLE__) +#include +#include +#include +#include +#include +#elif defined(__linux__) +#include +#include +#include +#include +#elif defined(_WIN32) +#include +#endif + +#include "cutils.h" +#include "quickjs-port.h" + +void qjs_abort() { + abort(); +} + + // From: https://stackoverflow.com/a/26085827 +int qjs_gettimeofday(struct qjs_timeval * tp) +{ +#if defined (_WIN32) + static const uint64_t EPOCH = ((uint64_t)116444736000000000ULL); + FILETIME file_time; + uint64_t time; + + GetSystemTimeAsFileTime(&file_time); + time = ((uint64_t)file_time.dwLowDateTime); + time += ((uint64_t)file_time.dwHighDateTime) << 32; + + tp->tv_sec = (int64_t)((time - EPOCH) / 10000000L); + tp->tv_usec = (long)((time / 10) % 1000000L); + return 0; +#else + struct timeval tv; + int result = gettimeofday(&tv, NULL); + tp->tv_sec = tv.tv_sec; + tp->tv_usec = tv.tv_usec; + return result; +#endif +} + + +#ifdef _WIN32 + +static const LONGLONG UnixEpochInTicks = 116444736000000000LL; /* difference between 1970 and 1601 */ +static const LONGLONG TicksPerMs = 10000LL; /* 1 tick is 100 nanoseconds */ + +/* + * If you take the limit of SYSTEMTIME (last millisecond in 30827) then you end up with + * a FILETIME of 0x7fff35f4f06c58f0 by using SystemTimeToFileTime(). However, if you put + * 0x7fffffffffffffff into FileTimeToSystemTime() then you will end up in the year 30828, + * although this date is invalid for SYSTEMTIME. Any larger value (0x8000000000000000 and above) + * causes FileTimeToSystemTime() to fail. + * https://docs.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-systemtime + * https://docs.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-filetime + */ +static const LONGLONG UnixEpochOfDate_1601_01_02 = -11644387200000LL; /* unit: ms */ +static const LONGLONG UnixEpochOfDate_30827_12_29 = 9106702560000000LL; /* unit: ms */ + +/* https://support.microsoft.com/en-us/help/167296/how-to-convert-a-unix-time-t-to-a-win32-filetime-or-systemtime */ +static void UnixTimeMsToFileTime(double t, LPFILETIME pft) +{ + LONGLONG ll = (LONGLONG)t * TicksPerMs + UnixEpochInTicks; + pft->dwLowDateTime = (DWORD)ll; + pft->dwHighDateTime = (DWORD)(ll >> 32); +} /* UnixTimeMsToFileTime */ + +#endif /* _WIN32 */ + +int qjs_gettimezoneoffset(int64_t time) +{ +#if defined(_WIN32) + FILETIME utcFileTime, localFileTime; + SYSTEMTIME utcSystemTime, localSystemTime; + int timeConverted = 0; + + /* + * If the time is earlier than the date 1601-01-02, then always using date 1601-01-02 to + * query time zone adjustment. This date (1601-01-02) will make sure both UTC and local + * time succeed with Win32 API. The date 1601-01-01 may lead to a win32 api failure, as + * after converting between local time and utc time, the time may be earlier than 1601-01-01 + * in UTC time, that exceeds the FILETIME representation range. + */ + if (time < (double)UnixEpochOfDate_1601_01_02) + { + time = (double)UnixEpochOfDate_1601_01_02; + } + + /* Like above, do not use the last supported day */ + if (time > (double)UnixEpochOfDate_30827_12_29) + { + time = (double)UnixEpochOfDate_30827_12_29; + } + + UnixTimeMsToFileTime (time, &utcFileTime); + if (FileTimeToSystemTime (&utcFileTime, &utcSystemTime) + && SystemTimeToTzSpecificLocalTime (0, &utcSystemTime, &localSystemTime) + && SystemTimeToFileTime (&localSystemTime, &localFileTime)) + { + timeConverted = 1; + } + if (timeConverted) + { + ULARGE_INTEGER utcTime, localTime; + utcTime.LowPart = utcFileTime.dwLowDateTime; + utcTime.HighPart = utcFileTime.dwHighDateTime; + localTime.LowPart = localFileTime.dwLowDateTime; + localTime.HighPart = localFileTime.dwHighDateTime; + return (double)(((LONGLONG)localTime.QuadPart - (LONGLONG)utcTime.QuadPart) / TicksPerMs); + } + return 0.0; +#else + time_t ti; + struct tm tm; + + time /= 1000; /* convert to seconds */ + if (sizeof(time_t) == 4) { + /* on 32-bit systems, we need to clamp the time value to the + range of `time_t`. This is better than truncating values to + 32 bits and hopefully provides the same result as 64-bit + implementation of localtime_r. + */ + if ((time_t)-1 < 0) { + if (time < INT32_MIN) { + time = INT32_MIN; + } else if (time > INT32_MAX) { + time = INT32_MAX; + } + } else { + if (time < 0) { + time = 0; + } else if (time > UINT32_MAX) { + time = UINT32_MAX; + } + } + } + ti = time; + localtime_r(&ti, &tm); + return -tm.tm_gmtoff / 60; +#endif +} + +int64_t qjs_get_time_ms(void) +{ +#if defined(__linux__) || defined(__APPLE__) + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return (int64_t)ts.tv_sec * 1000 + (ts.tv_nsec / 1000000); +#else + struct qjs_timeval tv; + qjs_gettimeofday(&tv); + return (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000); +#endif +} + +void qjs_usleep(int32_t us) +{ +#if defined(_WIN32) + Sleep(us / 1000); +#else + usleep(us); +#endif +} + +#if !defined(_WIN32) +static int qjs__fs_scandir_filter(struct dirent *dent) { + return strcmp(dent->d_name, ".") != 0 && strcmp(dent->d_name, "..") != 0; +} +#endif + +int qjs_listdir(void* context, const char* path, int recurse, qjs_listdir_callback_t callback) +{ +#if defined(_WIN32) + HANDLE hFind; + WIN32_FIND_DATA wfd; + BOOL cont = TRUE; + char search_path[MAX_PATH]; + + sprintf(search_path, "%s\\*", path); + + if ((hFind = FindFirstFile(search_path, &wfd)) == INVALID_HANDLE_VALUE) + { + return -1; + } + + while (cont == TRUE) + { + if ((strncmp(".", wfd.cFileName, 1) != 0) && (strncmp("..", wfd.cFileName, 2) != 0)) + { + sprintf(search_path, "%s\\%s", path, wfd.cFileName); + if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + if (callback(context, search_path, 1) != 0) + { + goto done; + } + if (recurse) + { + if (qjs_listdir(context, search_path, recurse, callback) != 0) + { + goto done; + } + } + } + else + { + if (callback(context, search_path, 0) != 0) + { + goto done; + } + } + } + cont = FindNextFile(hFind, &wfd); + } +done: + if (GetLastError() != ERROR_NO_MORE_FILES) + { + FindClose(hFind); + return -1; + } + if (FindClose(hFind) == FALSE) + { + return -1; + } + return 0; +#else + DIR *dir = NULL; + struct dirent *dent = NULL; + char search_path[PATH_MAX]; + int result = -1; + + dir = opendir(path); + if (dir == NULL) + { + return -1; + } + + while ((dent = readdir(dir)) != NULL) + { + if (qjs__fs_scandir_filter(dent)) { + sprintf(search_path, "%s/%s", path, dent->d_name); + if (dent->d_type & DT_DIR) { + if (callback(context, search_path, 1) != 0) + { + goto done; + } + if (recurse) + { + if (qjs_listdir(context, search_path, recurse, callback) != 0) + { + goto done; + } + } + } else { + if (callback(context, search_path, 0) != 0) + { + goto done; + } + } + } + } + result = 0; +done: + closedir(dir); + return result; +#endif +} + +extern force_inline void *qjs_malloc(size_t __size) +{ + return malloc(__size); +} + +extern force_inline void *qjs_realloc(void *__ptr, size_t __size) +{ + return realloc(__ptr, __size); +} + +extern force_inline void qjs_free(void *__ptr) +{ + free(__ptr); +} + +extern force_inline size_t qjs_malloc_usable_size(const void *ptr) +{ +#if defined(__APPLE__) + return malloc_size(ptr); +#elif defined(_WIN32) + return _msize((void *)ptr); +#elif defined(EMSCRIPTEN) + return 0; +#elif defined(__linux__) + return malloc_usable_size((void*)ptr); +#else + /* change this to `return 0;` if compilation fails */ + return malloc_usable_size(ptr); +#endif +} + +#if defined(_WIN32) + +typedef struct _internal_parameters +{ + qjs_thread_method i_method; + void* i_data; +}t_internal_parameters; + +static DWORD WINAPI internal_method_ptr(LPVOID arg) +{ + t_internal_parameters *params = (t_internal_parameters *)arg; + params->i_method(params->i_data); + free(params); + return 0; +} + +int qjs_thread_create(qjs_thread* thread, qjs_thread_method method, void* data) +{ + t_internal_parameters* params = (t_internal_parameters *)malloc(sizeof(t_internal_parameters)); + if(params) + { + params->i_method = method; + params->i_data = data; + *thread = CreateThread(NULL, 0, internal_method_ptr, params, 0, NULL); + if(*thread == NULL) + { + free(params); + return 1; + } + return 0; + } + return 1; +} + +int qjs_thread_join(qjs_thread* thread) +{ + if(WaitForSingleObject(*thread, INFINITE) != WAIT_FAILED) + { + if(CloseHandle(*thread)) + { + return 0; + } + } + return 1; +} +#else + +int qjs_thread_create(qjs_thread* thread, qjs_thread_method method, void* data) +{ + return pthread_create(thread, 0, (void *)method, data); +} + + +int qjs_thread_join(qjs_thread* thread) +{ + return pthread_join(*thread, NULL); +} + +#endif + +/* https://github.com/pierreguillot/thread */ +int qjs_mutex_init(qjs_mutex* mutex) +{ +#if defined(_WIN32) + *mutex = malloc(sizeof(CRITICAL_SECTION)); + InitializeCriticalSection((CRITICAL_SECTION*)*mutex); +#endif + return 0; +} + +//! @brief Locks a mutex. +int qjs_mutex_lock(qjs_mutex* mutex) +{ +#if defined(_WIN32) + EnterCriticalSection((CRITICAL_SECTION*)*mutex); + return 0; +#else + return pthread_mutex_lock(mutex); +#endif +} + +//! @brief Tries to locks a mutex. +int qjs_mutex_trylock(qjs_mutex* mutex) +{ +#if defined(_WIN32) + return !TryEnterCriticalSection((CRITICAL_SECTION*)*mutex); +#else + return pthread_mutex_trylock(mutex); +#endif +} + +//! @brief Unlocks a mutex. +int qjs_mutex_unlock(qjs_mutex* mutex) +{ +#if defined(_WIN32) + LeaveCriticalSection((CRITICAL_SECTION*)*mutex); + return 0; +#else + return pthread_mutex_unlock(mutex); +#endif +} + +//! @brief Destroy a mutex. +int qjs_mutex_destroy(qjs_mutex* mutex) +{ + int result = 0; +#if defined(_WIN32) + DeleteCriticalSection((CRITICAL_SECTION*)*mutex); + free(*mutex); + *mutex = NULL; +#else +#endif + return result; +} + +#if defined(_WIN32) +int qjs_condition_init(qjs_condition* cond) +{ + cond->Ptr = 0; + InitializeConditionVariable((CONDITION_VARIABLE*)cond); + return 0; +} + +int qjs_condition_signal(qjs_condition* cond) +{ + WakeConditionVariable((CONDITION_VARIABLE*)cond); + return 0; +} + +int qjs_condition_broadcast(qjs_condition* cond) +{ + WakeAllConditionVariable((CONDITION_VARIABLE*)cond); + return 0; +} + +int qjs_condition_wait(qjs_condition* cond, qjs_mutex* mutex) +{ + return !SleepConditionVariableCS((CONDITION_VARIABLE*)cond, (CRITICAL_SECTION*)*mutex, INFINITE); +} + +int qjs_condition_timedwait(qjs_condition* cond, qjs_mutex* mutex, int64_t time_ns) +{ + if (SleepConditionVariableCS((CONDITION_VARIABLE*)cond, (CRITICAL_SECTION*)*mutex, time_ns / 1000000)) { + return 0; + } + if (GetLastError() == ERROR_TIMEOUT) { + return ETIMEDOUT; + } + return 1; +} + +int qjs_condition_destroy(qjs_condition* cond) +{ + cond->Ptr = 0; + return 0; +} + +#else + +int qjs_condition_init(qjs_condition* cond) +{ + return pthread_cond_init(cond, NULL); +} + +int qjs_condition_signal(qjs_condition* cond) +{ + return pthread_cond_signal(cond); +} + +int qjs_condition_broadcast(qjs_condition* cond) +{ + return pthread_cond_broadcast(cond); +} + +int qjs_condition_wait(qjs_condition* cond, qjs_mutex* mutex) +{ + return pthread_cond_wait(cond, mutex); +} + +int qjs_condition_timedwait(qjs_condition* cond, qjs_mutex* mutex, int64_t time_ns) +{ + /* XXX: use clock monotonic */ + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + ts.tv_sec += time_ns / 1000000000; + ts.tv_nsec += time_ns % 1000000000; + if (ts.tv_nsec >= 1000000000) { + ts.tv_nsec -= 1000000000; + ts.tv_sec++; + } + return pthread_cond_timedwait(cond, mutex, &ts); +} + +int qjs_condition_destroy(qjs_condition* cond) +{ + int result = pthread_cond_destroy(cond); + return result; +} +#endif \ No newline at end of file diff --git a/quickjs-port.h b/quickjs-port.h new file mode 100644 index 000000000..a1e6e9c18 --- /dev/null +++ b/quickjs-port.h @@ -0,0 +1,151 @@ +/* + * QuickJS C library + * + * Copyright (c) 2017-2018 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef QUICKJS_PORT_H +#define QUICKJS_PORT_H + +#include +#include +#if defined(_MSC_VER) +#include +#include "win/stdatomic.h" +#else +#include +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +struct qjs_timeval { + int64_t tv_sec; /* seconds */ + long tv_usec; /* and microseconds */ +}; + +//! @brief The thread method. +typedef void (*qjs_thread_method)(void *); + +#if defined(_WIN32) +typedef void* qjs_thread; +typedef void* qjs_mutex; +typedef struct qjs_condition_s { + void* Ptr; +} qjs_condition; +#define QJS_MUTEX_INITIALIZER NULL +#else +typedef pthread_t qjs_thread; +typedef pthread_mutex_t qjs_mutex; +typedef pthread_cond_t qjs_condition; +#define QJS_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER +#endif + +void qjs_abort(); +int qjs_gettimeofday(struct qjs_timeval * tp); +int qjs_gettimezoneoffset(int64_t time); +int64_t qjs_get_time_ms(void); +void qjs_usleep(int32_t us); + +typedef int (*qjs_listdir_callback_t)(void* context, const char* path, int is_dir); +int qjs_listdir(void* context, const char* path, int recurse, qjs_listdir_callback_t callback); + +/* Memory relate functions */ +void *qjs_malloc(size_t __size); +void *qjs_realloc(void *__ptr, size_t __size); +void qjs_free(void *__ptr); +size_t qjs_malloc_usable_size(const void *ptr); + +#if defined(EMSCRIPTEN) + +static inline uint8_t *qjs_get_stack_pointer(void) +{ + return NULL; +} + +static inline size_t qjs_stack_size(const uint8_t* stack_top) +{ + return stack_top - stack_top; +} +#else + +/* Note: OS and CPU dependent */ +static inline uint8_t *qjs_get_stack_pointer(void) +{ +#if defined(_MSC_VER) + return _AddressOfReturnAddress(); +#else + return __builtin_frame_address(0); +#endif +} + +static inline size_t qjs_stack_size(const uint8_t* stack_top) +{ + return stack_top - qjs_get_stack_pointer(); +} + +#endif + +//! @brief Detaches a thread. +int qjs_thread_create(qjs_thread* thread, qjs_thread_method method, void* data); + +//! @brief Joins a thread. +int qjs_thread_join(qjs_thread* thread); + +/* Mutex relaed functions */ +int qjs_mutex_init(qjs_mutex* mutex); + +//! @brief Locks a mutex. +int qjs_mutex_lock(qjs_mutex* mutex); + +//! @brief Tries to locks a mutex. +int qjs_mutex_trylock(qjs_mutex* mutex); + +//! @brief Unlocks a mutex. +int qjs_mutex_unlock(qjs_mutex* mutex); + +//! @brief Destroy a mutex. +int qjs_mutex_destroy(qjs_mutex* mutex); + +//! @brief Initializes a condition. +int qjs_condition_init(qjs_condition* cond); + +//! @brief Restarts one of the threads that are waiting on the condition. +int qjs_condition_signal(qjs_condition* cond); + +//! @brief Shall unblock all threads currently blocked on the specified condition variable cond. +int qjs_condition_broadcast(qjs_condition* cond); + +//! @brief Unlocks the mutex and waits for the condition to be signalled. +int qjs_condition_wait(qjs_condition* cond, qjs_mutex* mutex); + +//! @brief Unlocks the mutex and waits for the condition to be signalled or reach abstime. +int qjs_condition_timedwait(qjs_condition* cond, qjs_mutex* mutex, int64_t time_ns); + +//! @brief Destroy a condition. +int qjs_condition_destroy(qjs_condition* cond); + +#ifdef __cplusplus +} /* extern "C" { */ +#endif + +#endif /* QUICKJS_PORT_H */ diff --git a/quickjs.c b/quickjs.c index 418041843..84dfbd749 100644 --- a/quickjs.c +++ b/quickjs.c @@ -23,24 +23,19 @@ * THE SOFTWARE. */ #include -#include #include #include #include #include -#include #include #include +#include #include -#if defined(__APPLE__) -#include -#elif defined(__linux__) -#include -#endif #include "cutils.h" #include "list.h" #include "quickjs.h" +#include "quickjs-port.h" #include "libregexp.h" #ifdef CONFIG_BIGNUM #include "libbf.h" @@ -48,7 +43,7 @@ #define OPTIMIZE 1 #define SHORT_OPCODES 1 -#if defined(EMSCRIPTEN) +#if defined(EMSCRIPTEN) || defined(_MSC_VER) #define DIRECT_DISPATCH 0 #else #define DIRECT_DISPATCH 1 @@ -69,11 +64,11 @@ #define CONFIG_ATOMICS #endif -#if !defined(EMSCRIPTEN) -/* enable stack limitation */ -#define CONFIG_STACK_CHECK -#endif - +#define BOOL JS_BOOL +#define abort qjs_abort +#define malloc(s) malloc_is_forbidden(s) +#define free(p) free_is_forbidden(p) +#define realloc(p,s) realloc_is_forbidden(p,s) /* dump object free */ //#define DUMP_FREE @@ -121,12 +116,6 @@ /* test the GC by forcing it before each object allocation */ //#define FORCE_GC_AT_MALLOC -#ifdef CONFIG_ATOMICS -#include -#include -#include -#endif - enum { /* classid tag */ /* union usage | properties */ JS_CLASS_OBJECT = 1, /* must be first */ @@ -1583,31 +1572,11 @@ static void set_dummy_numeric_ops(JSNumericOperations *ops) #endif /* CONFIG_BIGNUM */ -#if !defined(CONFIG_STACK_CHECK) -/* no stack limitation */ -static inline uint8_t *js_get_stack_pointer(void) -{ - return NULL; -} - static inline BOOL js_check_stack_overflow(JSRuntime *rt, size_t alloca_size) { - return FALSE; -} -#else -/* Note: OS and CPU dependent */ -static inline uint8_t *js_get_stack_pointer(void) -{ - return __builtin_frame_address(0); -} - -static inline BOOL js_check_stack_overflow(JSRuntime *rt, size_t alloca_size) -{ - size_t size; - size = rt->stack_top - js_get_stack_pointer(); + size_t size = qjs_stack_size(rt->stack_top); return unlikely((size + alloca_size) > rt->stack_size); } -#endif JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque) { @@ -1665,7 +1634,7 @@ JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque) if (init_shape_hash(rt)) goto fail; - rt->stack_top = js_get_stack_pointer(); + rt->stack_top = qjs_get_stack_pointer(); rt->stack_size = JS_DEFAULT_STACK_SIZE; rt->current_exception = JS_NULL; @@ -1685,23 +1654,6 @@ void JS_SetRuntimeOpaque(JSRuntime *rt, void *opaque) rt->user_opaque = opaque; } -/* default memory allocation functions with memory limitation */ -static inline size_t js_def_malloc_usable_size(void *ptr) -{ -#if defined(__APPLE__) - return malloc_size(ptr); -#elif defined(_WIN32) - return _msize(ptr); -#elif defined(EMSCRIPTEN) - return 0; -#elif defined(__linux__) - return malloc_usable_size(ptr); -#else - /* change this to `return 0;` if compilation fails */ - return malloc_usable_size(ptr); -#endif -} - static void *js_def_malloc(JSMallocState *s, size_t size) { void *ptr; @@ -1712,12 +1664,12 @@ static void *js_def_malloc(JSMallocState *s, size_t size) if (unlikely(s->malloc_size + size > s->malloc_limit)) return NULL; - ptr = malloc(size); + ptr = qjs_malloc(size); if (!ptr) return NULL; s->malloc_count++; - s->malloc_size += js_def_malloc_usable_size(ptr) + MALLOC_OVERHEAD; + s->malloc_size += qjs_malloc_usable_size(ptr) + MALLOC_OVERHEAD; return ptr; } @@ -1727,8 +1679,8 @@ static void js_def_free(JSMallocState *s, void *ptr) return; s->malloc_count--; - s->malloc_size -= js_def_malloc_usable_size(ptr) + MALLOC_OVERHEAD; - free(ptr); + s->malloc_size -= qjs_malloc_usable_size(ptr) + MALLOC_OVERHEAD; + qjs_free(ptr); } static void *js_def_realloc(JSMallocState *s, void *ptr, size_t size) @@ -1740,40 +1692,41 @@ static void *js_def_realloc(JSMallocState *s, void *ptr, size_t size) return NULL; return js_def_malloc(s, size); } - old_size = js_def_malloc_usable_size(ptr); + old_size = qjs_malloc_usable_size(ptr); if (size == 0) { s->malloc_count--; s->malloc_size -= old_size + MALLOC_OVERHEAD; - free(ptr); + qjs_free(ptr); return NULL; } if (s->malloc_size + size - old_size > s->malloc_limit) return NULL; - ptr = realloc(ptr, size); + ptr = qjs_realloc(ptr, size); if (!ptr) return NULL; - s->malloc_size += js_def_malloc_usable_size(ptr) - old_size; + s->malloc_size += qjs_malloc_usable_size(ptr) - old_size; return ptr; } +static qjs_mutex js_atomics_mutex = QJS_MUTEX_INITIALIZER; + +void JS_Initialize(void) +{ + qjs_mutex_init(&js_atomics_mutex); +} + +void JS_Finalize(void) +{ + qjs_mutex_destroy(&js_atomics_mutex); +} + static const JSMallocFunctions def_malloc_funcs = { js_def_malloc, js_def_free, js_def_realloc, -#if defined(__APPLE__) - malloc_size, -#elif defined(_WIN32) - (size_t (*)(const void *))_msize, -#elif defined(EMSCRIPTEN) - NULL, -#elif defined(__linux__) - (size_t (*)(const void *))malloc_usable_size, -#else - /* change this to `NULL,` if compilation fails */ - malloc_usable_size, -#endif + qjs_malloc_usable_size, }; JSRuntime *JS_NewRuntime(void) @@ -1792,9 +1745,6 @@ void JS_SetGCThreshold(JSRuntime *rt, size_t gc_threshold) rt->malloc_gc_threshold = gc_threshold; } -#define malloc(s) malloc_is_forbidden(s) -#define free(p) free_is_forbidden(p) -#define realloc(p,s) realloc_is_forbidden(p,s) void JS_SetInterruptHandler(JSRuntime *rt, JSInterruptHandler *cb, void *opaque) { @@ -41740,8 +41690,8 @@ static uint64_t xorshift64star(uint64_t *pstate) static void js_random_init(JSContext *ctx) { - struct timeval tv; - gettimeofday(&tv, NULL); + struct qjs_timeval tv; + qjs_gettimeofday(&tv); ctx->random_state = ((int64_t)tv.tv_sec * 1000000) + tv.tv_usec; /* the state must be non zero */ if (ctx->random_state == 0) @@ -41824,27 +41774,13 @@ static const JSCFunctionListEntry js_math_obj[] = { }; /* Date */ - -#if 0 -/* OS dependent: return the UTC time in ms since 1970. */ -static JSValue js___date_now(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int64_t d; - struct timeval tv; - gettimeofday(&tv, NULL); - d = (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000); - return JS_NewInt64(ctx, d); -} -#endif - /* OS dependent: return the UTC time in microseconds since 1970. */ static JSValue js___date_clock(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { int64_t d; - struct timeval tv; - gettimeofday(&tv, NULL); + struct qjs_timeval tv; + qjs_gettimeofday(&tv); d = (int64_t)tv.tv_sec * 1000000 + tv.tv_usec; return JS_NewInt64(ctx, d); } @@ -41852,84 +41788,9 @@ static JSValue js___date_clock(JSContext *ctx, JSValueConst this_val, /* OS dependent. d = argv[0] is in ms from 1970. Return the difference between local time and UTC time 'd' in minutes */ static int getTimezoneOffset(int64_t time) { -#if defined(_WIN32) - /* XXX: TODO */ - return 0; -#else - time_t ti; - struct tm tm; - - time /= 1000; /* convert to seconds */ - if (sizeof(time_t) == 4) { - /* on 32-bit systems, we need to clamp the time value to the - range of `time_t`. This is better than truncating values to - 32 bits and hopefully provides the same result as 64-bit - implementation of localtime_r. - */ - if ((time_t)-1 < 0) { - if (time < INT32_MIN) { - time = INT32_MIN; - } else if (time > INT32_MAX) { - time = INT32_MAX; - } - } else { - if (time < 0) { - time = 0; - } else if (time > UINT32_MAX) { - time = UINT32_MAX; - } - } - } - ti = time; - localtime_r(&ti, &tm); - return -tm.tm_gmtoff / 60; -#endif + return qjs_gettimezoneoffset(time); } -#if 0 -static JSValue js___date_getTimezoneOffset(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - double dd; - - if (JS_ToFloat64(ctx, &dd, argv[0])) - return JS_EXCEPTION; - if (isnan(dd)) - return __JS_NewFloat64(ctx, dd); - else - return JS_NewInt32(ctx, getTimezoneOffset((int64_t)dd)); -} - -static JSValue js_get_prototype_from_ctor(JSContext *ctx, JSValueConst ctor, - JSValueConst def_proto) -{ - JSValue proto; - proto = JS_GetProperty(ctx, ctor, JS_ATOM_prototype); - if (JS_IsException(proto)) - return proto; - if (!JS_IsObject(proto)) { - JS_FreeValue(ctx, proto); - proto = JS_DupValue(ctx, def_proto); - } - return proto; -} - -/* create a new date object */ -static JSValue js___date_create(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue obj, proto; - proto = js_get_prototype_from_ctor(ctx, argv[0], argv[1]); - if (JS_IsException(proto)) - return proto; - obj = JS_NewObjectProtoClass(ctx, proto, JS_CLASS_DATE); - JS_FreeValue(ctx, proto); - if (!JS_IsException(obj)) - JS_SetObjectData(ctx, obj, JS_DupValue(ctx, argv[2])); - return obj; -} -#endif - /* RegExp */ static void js_regexp_finalizer(JSRuntime *rt, JSValue val) @@ -47711,9 +47572,6 @@ static const JSCFunctionListEntry js_global_funcs[] = { /* for the 'Date' implementation */ JS_CFUNC_DEF("__date_clock", 0, js___date_clock ), - //JS_CFUNC_DEF("__date_now", 0, js___date_now ), - //JS_CFUNC_DEF("__date_getTimezoneOffset", 1, js___date_getTimezoneOffset ), - //JS_CFUNC_DEF("__date_create", 3, js___date_create ), }; /* Date */ @@ -48052,8 +47910,8 @@ static JSValue get_date_string(JSContext *ctx, JSValueConst this_val, /* OS dependent: return the UTC time in ms since 1970. */ static int64_t date_now(void) { - struct timeval tv; - gettimeofday(&tv, NULL); + struct qjs_timeval tv; + qjs_gettimeofday(&tv); return (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000); } @@ -48432,7 +48290,6 @@ static JSValue js_date_Symbol_toPrimitive(JSContext *ctx, JSValueConst this_val, static JSValue js_date_getTimezoneOffset(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { - // getTimezoneOffset() double v; if (JS_ThisTimeValue(ctx, &v, this_val)) @@ -53579,11 +53436,10 @@ static JSValue js_atomics_isLockFree(JSContext *ctx, typedef struct JSAtomicsWaiter { struct list_head link; BOOL linked; - pthread_cond_t cond; + qjs_condition cond; int32_t *ptr; } JSAtomicsWaiter; -static pthread_mutex_t js_atomics_mutex = PTHREAD_MUTEX_INITIALIZER; static struct list_head js_atomics_waiter_list = LIST_HEAD_INIT(js_atomics_waiter_list); @@ -53595,7 +53451,6 @@ static JSValue js_atomics_wait(JSContext *ctx, int32_t v32; void *ptr; int64_t timeout; - struct timespec ts; JSAtomicsWaiter waiter_s, *waiter; int ret, size_log2, res; double d; @@ -53629,42 +53484,33 @@ static JSValue js_atomics_wait(JSContext *ctx, /* XXX: inefficient if large number of waiters, should hash on 'ptr' value */ /* XXX: use Linux futexes when available ? */ - pthread_mutex_lock(&js_atomics_mutex); + qjs_mutex_lock(&js_atomics_mutex); if (size_log2 == 3) { res = *(int64_t *)ptr != v; } else { res = *(int32_t *)ptr != v; } if (res) { - pthread_mutex_unlock(&js_atomics_mutex); + qjs_mutex_unlock(&js_atomics_mutex); return JS_AtomToString(ctx, JS_ATOM_not_equal); } waiter = &waiter_s; waiter->ptr = ptr; - pthread_cond_init(&waiter->cond, NULL); + qjs_condition_init(&waiter->cond); waiter->linked = TRUE; list_add_tail(&waiter->link, &js_atomics_waiter_list); if (timeout == INT64_MAX) { - pthread_cond_wait(&waiter->cond, &js_atomics_mutex); + qjs_condition_wait(&waiter->cond, &js_atomics_mutex); ret = 0; } else { - /* XXX: use clock monotonic */ - clock_gettime(CLOCK_REALTIME, &ts); - ts.tv_sec += timeout / 1000; - ts.tv_nsec += (timeout % 1000) * 1000000; - if (ts.tv_nsec >= 1000000000) { - ts.tv_nsec -= 1000000000; - ts.tv_sec++; - } - ret = pthread_cond_timedwait(&waiter->cond, &js_atomics_mutex, - &ts); + ret = qjs_condition_timedwait(&waiter->cond, &js_atomics_mutex, timeout * 1000000); } if (waiter->linked) list_del(&waiter->link); - pthread_mutex_unlock(&js_atomics_mutex); - pthread_cond_destroy(&waiter->cond); + qjs_mutex_unlock(&js_atomics_mutex); + qjs_condition_destroy(&waiter->cond); if (ret == ETIMEDOUT) { return JS_AtomToString(ctx, JS_ATOM_timed_out); } else { @@ -53697,7 +53543,7 @@ static JSValue js_atomics_notify(JSContext *ctx, n = 0; if (abuf->shared && count > 0) { - pthread_mutex_lock(&js_atomics_mutex); + qjs_mutex_lock(&js_atomics_mutex); init_list_head(&waiter_list); list_for_each_safe(el, el1, &js_atomics_waiter_list) { waiter = list_entry(el, JSAtomicsWaiter, link); @@ -53712,9 +53558,9 @@ static JSValue js_atomics_notify(JSContext *ctx, } list_for_each(el, &waiter_list) { waiter = list_entry(el, JSAtomicsWaiter, link); - pthread_cond_signal(&waiter->cond); + qjs_condition_signal(&waiter->cond); } - pthread_mutex_unlock(&js_atomics_mutex); + qjs_mutex_unlock(&js_atomics_mutex); } return JS_NewInt32(ctx, n); } diff --git a/quickjs.h b/quickjs.h index f63719d1f..9db245b3a 100644 --- a/quickjs.h +++ b/quickjs.h @@ -331,6 +331,10 @@ typedef struct JSMallocFunctions { typedef struct JSGCObjectHeader JSGCObjectHeader; +/* These pair of function should be called at the very beginning and the very end */ +void JS_Initialize(void); +void JS_Finalize(void); + JSRuntime *JS_NewRuntime(void); /* info lifetime must exceed that of rt */ void JS_SetRuntimeInfo(JSRuntime *rt, const char *info); diff --git a/run-test262.c b/run-test262.c index 6242c5792..db4f2f98a 100644 --- a/run-test262.c +++ b/run-test262.c @@ -29,16 +29,15 @@ #include #include #include -#include #include #include -#include -#include #include "cutils.h" #include "list.h" #include "quickjs-libc.h" +#include "quickjs-port.h" +#define BOOL int /* enable test262 thread support to test SharedArrayBuffer and Atomics */ #define CONFIG_AGENT @@ -359,10 +358,10 @@ void namelist_free(namelist_t *lp) lp->size = 0; } -static int add_test_file(const char *filename, const struct stat *ptr, int flag) +static int add_test_file(void *context, const char *filename, int is_dir) { - namelist_t *lp = &test_list; - if (has_suffix(filename, ".js") && !has_suffix(filename, "_FIXTURE.js")) + namelist_t *lp = (namelist_t *)context; + if (!is_dir && has_suffix(filename, ".js") && !has_suffix(filename, "_FIXTURE.js")) namelist_add(lp, NULL, filename); return 0; } @@ -372,7 +371,7 @@ static void enumerate_tests(const char *path) { namelist_t *lp = &test_list; int start = lp->count; - ftw(path, add_test_file, 100); + qjs_listdir((void*)lp, path, 1, add_test_file); qsort(lp->array + start, lp->count - start, sizeof(*lp->array), namelist_cmp_indirect); } @@ -426,11 +425,9 @@ static JSValue js_evalScript(JSContext *ctx, JSValue this_val, #ifdef CONFIG_AGENT -#include - typedef struct { struct list_head link; - pthread_t tid; + qjs_thread tid; char *script; JSValue broadcast_func; BOOL broadcast_pending; @@ -448,16 +445,16 @@ typedef struct { static JSValue add_helpers1(JSContext *ctx); static void add_helpers(JSContext *ctx); -static pthread_mutex_t agent_mutex = PTHREAD_MUTEX_INITIALIZER; -static pthread_cond_t agent_cond = PTHREAD_COND_INITIALIZER; +static qjs_mutex agent_mutex = QJS_MUTEX_INITIALIZER; +static qjs_condition agent_cond; /* list of Test262Agent.link */ static struct list_head agent_list = LIST_HEAD_INIT(agent_list); -static pthread_mutex_t report_mutex = PTHREAD_MUTEX_INITIALIZER; +static qjs_mutex report_mutex = QJS_MUTEX_INITIALIZER; /* list of AgentReport.link */ static struct list_head report_list = LIST_HEAD_INIT(report_list); -static void *agent_start(void *arg) +static void agent_start(void *arg) { Test262Agent *agent = arg; JSRuntime *rt; @@ -499,15 +496,15 @@ static void *agent_start(void *arg) } else { JSValue args[2]; - pthread_mutex_lock(&agent_mutex); + qjs_mutex_lock(&agent_mutex); while (!agent->broadcast_pending) { - pthread_cond_wait(&agent_cond, &agent_mutex); + qjs_condition_wait(&agent_cond, &agent_mutex); } agent->broadcast_pending = FALSE; - pthread_cond_signal(&agent_cond); + qjs_condition_signal(&agent_cond); - pthread_mutex_unlock(&agent_mutex); + qjs_mutex_unlock(&agent_mutex); args[0] = JS_NewArrayBuffer(ctx, agent->broadcast_sab_buf, agent->broadcast_sab_size, @@ -529,7 +526,6 @@ static void *agent_start(void *arg) JS_FreeContext(ctx); JS_FreeRuntime(rt); - return NULL; } static JSValue js_agent_start(JSContext *ctx, JSValue this_val, @@ -551,7 +547,7 @@ static JSValue js_agent_start(JSContext *ctx, JSValue this_val, agent->script = strdup(script); JS_FreeCString(ctx, script); list_add_tail(&agent->link, &agent_list); - pthread_create(&agent->tid, NULL, agent_start, agent); + qjs_thread_create(&agent->tid, agent_start, agent); return JS_UNDEFINED; } @@ -562,7 +558,7 @@ static void js_agent_free(JSContext *ctx) list_for_each_safe(el, el1, &agent_list) { agent = list_entry(el, Test262Agent, link); - pthread_join(agent->tid, NULL); + qjs_thread_join(&agent->tid); JS_FreeValue(ctx, agent->broadcast_sab); list_del(&agent->link); free(agent); @@ -612,7 +608,7 @@ static JSValue js_agent_broadcast(JSContext *ctx, JSValue this_val, /* broadcast the values and wait until all agents have started calling their callbacks */ - pthread_mutex_lock(&agent_mutex); + qjs_mutex_lock(&agent_mutex); list_for_each(el, &agent_list) { agent = list_entry(el, Test262Agent, link); agent->broadcast_pending = TRUE; @@ -623,12 +619,12 @@ static JSValue js_agent_broadcast(JSContext *ctx, JSValue this_val, agent->broadcast_sab_size = buf_size; agent->broadcast_val = val; } - pthread_cond_broadcast(&agent_cond); + qjs_condition_broadcast(&agent_cond); while (is_broadcast_pending()) { - pthread_cond_wait(&agent_cond, &agent_mutex); + qjs_condition_wait(&agent_cond, &agent_mutex); } - pthread_mutex_unlock(&agent_mutex); + qjs_mutex_unlock(&agent_mutex); return JS_UNDEFINED; } @@ -651,21 +647,15 @@ static JSValue js_agent_sleep(JSContext *ctx, JSValue this_val, uint32_t duration; if (JS_ToUint32(ctx, &duration, argv[0])) return JS_EXCEPTION; - usleep(duration * 1000); + qjs_usleep(duration * 1000); return JS_UNDEFINED; } -static int64_t get_clock_ms(void) -{ - struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - return (uint64_t)ts.tv_sec * 1000 + (ts.tv_nsec / 1000000); -} static JSValue js_agent_monotonicNow(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { - return JS_NewInt64(ctx, get_clock_ms()); + return JS_NewInt64(ctx, qjs_get_time_ms()); } static JSValue js_agent_getReport(JSContext *ctx, JSValue this_val, @@ -674,14 +664,14 @@ static JSValue js_agent_getReport(JSContext *ctx, JSValue this_val, AgentReport *rep; JSValue ret; - pthread_mutex_lock(&report_mutex); + qjs_mutex_lock(&report_mutex); if (list_empty(&report_list)) { rep = NULL; } else { rep = list_entry(report_list.next, AgentReport, link); list_del(&rep->link); } - pthread_mutex_unlock(&report_mutex); + qjs_mutex_unlock(&report_mutex); if (rep) { ret = JS_NewString(ctx, rep->str); free(rep->str); @@ -705,9 +695,9 @@ static JSValue js_agent_report(JSContext *ctx, JSValue this_val, rep->str = strdup(str); JS_FreeCString(ctx, str); - pthread_mutex_lock(&report_mutex); + qjs_mutex_lock(&report_mutex); list_add_tail(&rep->link, &report_list); - pthread_mutex_unlock(&report_mutex); + qjs_mutex_unlock(&report_mutex); return JS_UNDEFINED; } @@ -735,6 +725,8 @@ static JSValue js_new_agent(JSContext *ctx) } #endif +static JSValue add_helpers1(JSContext *ctx); + static JSValue js_createRealm(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { @@ -1505,6 +1497,24 @@ void update_stats(JSRuntime *rt, const char *filename) { #undef update } +void run_test262_init() { + JS_Initialize(); +#ifdef CONFIG_AGENT + qjs_mutex_init(&agent_mutex); + qjs_mutex_init(&report_mutex); + qjs_condition_init(&agent_cond); +#endif +} + +void run_test262_final() { + JS_Finalize(); +#ifdef CONFIG_AGENT + qjs_mutex_destroy(&agent_mutex); + qjs_mutex_destroy(&report_mutex); + qjs_condition_destroy(&agent_cond); +#endif +} + int run_test_buf(const char *filename, char *harness, namelist_t *ip, char *buf, size_t buf_len, const char* error_type, int eval_flags, BOOL is_negative, BOOL is_async, @@ -1513,7 +1523,7 @@ int run_test_buf(const char *filename, char *harness, namelist_t *ip, JSRuntime *rt; JSContext *ctx; int i, ret; - + run_test262_init(); rt = JS_NewRuntime(); if (rt == NULL) { fatal(1, "JS_NewRuntime failure"); @@ -1535,7 +1545,7 @@ int run_test_buf(const char *filename, char *harness, namelist_t *ip, for (i = 0; i < ip->count; i++) { if (eval_file(ctx, harness, ip->array[i], JS_EVAL_TYPE_GLOBAL | JS_EVAL_FLAG_STRIP)) { - fatal(1, "error including %s for %s", ip->array[i], filename); + fprintf(stderr, "error including %s for %s", ip->array[i], filename); } } @@ -1551,6 +1561,7 @@ int run_test_buf(const char *filename, char *harness, namelist_t *ip, #endif JS_FreeContext(ctx); JS_FreeRuntime(rt); + run_test262_final(); test_count++; if (ret) { @@ -1809,6 +1820,7 @@ int run_test262_harness_test(const char *filename, BOOL is_module) BOOL can_block; outfile = stdout; /* for js_print */ + run_test262_init(); rt = JS_NewRuntime(); if (rt == NULL) { @@ -1860,6 +1872,7 @@ int run_test262_harness_test(const char *filename, BOOL is_module) #endif JS_FreeContext(ctx); JS_FreeRuntime(rt); + run_test262_final(); return ret_code; } @@ -1894,13 +1907,13 @@ void run_test_dir_list(namelist_t *lp, int start_index, int stop_index) } else { int ti; if (slow_test_threshold != 0) { - ti = get_clock_ms(); + ti = qjs_get_time_ms(); } else { ti = 0; } run_test(p, test_index); if (slow_test_threshold != 0) { - ti = get_clock_ms() - ti; + ti = qjs_get_time_ms() - ti; if (ti >= slow_test_threshold) fprintf(stderr, "\n%s (%d ms)\n", p, ti); } diff --git a/unicode_gen.c b/unicode_gen.c index f18aaa0ab..0f6ea70c8 100644 --- a/unicode_gen.c +++ b/unicode_gen.c @@ -33,6 +33,8 @@ #include "cutils.h" +#define BOOL int + /* define it to be able to test unicode.c */ //#define USE_TEST /* profile tests */ diff --git a/win/dirent.h b/win/dirent.h new file mode 100644 index 000000000..077674e45 --- /dev/null +++ b/win/dirent.h @@ -0,0 +1,1166 @@ +/* + * Dirent interface for Microsoft Visual Studio + * + * Copyright (C) 1998-2019 Toni Ronkko + * This file is part of dirent. Dirent may be freely distributed + * under the MIT license. For all details and documentation, see + * https://github.com/tronkko/dirent + */ +#ifndef DIRENT_H +#define DIRENT_H + +/* Hide warnings about unreferenced local functions */ +#if defined(__clang__) +# pragma clang diagnostic ignored "-Wunused-function" +#elif defined(_MSC_VER) +# pragma warning(disable:4505) +#elif defined(__GNUC__) +# pragma GCC diagnostic ignored "-Wunused-function" +#endif + +/* + * Include windows.h without Windows Sockets 1.1 to prevent conflicts with + * Windows Sockets 2.0. + */ +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Indicates that d_type field is available in dirent structure */ +#define _DIRENT_HAVE_D_TYPE + +/* Indicates that d_namlen field is available in dirent structure */ +#define _DIRENT_HAVE_D_NAMLEN + +/* Entries missing from MSVC 6.0 */ +#if !defined(FILE_ATTRIBUTE_DEVICE) +# define FILE_ATTRIBUTE_DEVICE 0x40 +#endif + +/* File type and permission flags for stat(), general mask */ +#if !defined(S_IFMT) +# define S_IFMT _S_IFMT +#endif + +/* Directory bit */ +#if !defined(S_IFDIR) +# define S_IFDIR _S_IFDIR +#endif + +/* Character device bit */ +#if !defined(S_IFCHR) +# define S_IFCHR _S_IFCHR +#endif + +/* Pipe bit */ +#if !defined(S_IFFIFO) +# define S_IFFIFO _S_IFFIFO +#endif + +/* Regular file bit */ +#if !defined(S_IFREG) +# define S_IFREG _S_IFREG +#endif + +/* Read permission */ +#if !defined(S_IREAD) +# define S_IREAD _S_IREAD +#endif + +/* Write permission */ +#if !defined(S_IWRITE) +# define S_IWRITE _S_IWRITE +#endif + +/* Execute permission */ +#if !defined(S_IEXEC) +# define S_IEXEC _S_IEXEC +#endif + +/* Pipe */ +#if !defined(S_IFIFO) +# define S_IFIFO _S_IFIFO +#endif + +/* Block device */ +#if !defined(S_IFBLK) +# define S_IFBLK 0 +#endif + +/* Link */ +#if !defined(S_IFLNK) +# define S_IFLNK 0 +#endif + +/* Socket */ +#if !defined(S_IFSOCK) +# define S_IFSOCK 0 +#endif + +/* Read user permission */ +#if !defined(S_IRUSR) +# define S_IRUSR S_IREAD +#endif + +/* Write user permission */ +#if !defined(S_IWUSR) +# define S_IWUSR S_IWRITE +#endif + +/* Execute user permission */ +#if !defined(S_IXUSR) +# define S_IXUSR 0 +#endif + +/* Read group permission */ +#if !defined(S_IRGRP) +# define S_IRGRP 0 +#endif + +/* Write group permission */ +#if !defined(S_IWGRP) +# define S_IWGRP 0 +#endif + +/* Execute group permission */ +#if !defined(S_IXGRP) +# define S_IXGRP 0 +#endif + +/* Read others permission */ +#if !defined(S_IROTH) +# define S_IROTH 0 +#endif + +/* Write others permission */ +#if !defined(S_IWOTH) +# define S_IWOTH 0 +#endif + +/* Execute others permission */ +#if !defined(S_IXOTH) +# define S_IXOTH 0 +#endif + +/* Maximum length of file name */ +#if !defined(PATH_MAX) +# define PATH_MAX MAX_PATH +#endif +#if !defined(FILENAME_MAX) +# define FILENAME_MAX MAX_PATH +#endif +#if !defined(NAME_MAX) +# define NAME_MAX FILENAME_MAX +#endif + +/* File type flags for d_type */ +#define DT_UNKNOWN 0 +#define DT_REG S_IFREG +#define DT_DIR S_IFDIR +#define DT_FIFO S_IFIFO +#define DT_SOCK S_IFSOCK +#define DT_CHR S_IFCHR +#define DT_BLK S_IFBLK +#define DT_LNK S_IFLNK + +/* Macros for converting between st_mode and d_type */ +#define IFTODT(mode) ((mode) & S_IFMT) +#define DTTOIF(type) (type) + +/* + * File type macros. Note that block devices, sockets and links cannot be + * distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are + * only defined for compatibility. These macros should always return false + * on Windows. + */ +#if !defined(S_ISFIFO) +# define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO) +#endif +#if !defined(S_ISDIR) +# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) +#endif +#if !defined(S_ISREG) +# define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) +#endif +#if !defined(S_ISLNK) +# define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) +#endif +#if !defined(S_ISSOCK) +# define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK) +#endif +#if !defined(S_ISCHR) +# define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) +#endif +#if !defined(S_ISBLK) +# define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) +#endif + +/* Return the exact length of the file name without zero terminator */ +#define _D_EXACT_NAMLEN(p) ((p)->d_namlen) + +/* Return the maximum size of a file name */ +#define _D_ALLOC_NAMLEN(p) ((PATH_MAX)+1) + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Wide-character version */ +struct _wdirent { + /* Always zero */ + long d_ino; + + /* File position within stream */ + long d_off; + + /* Structure size */ + unsigned short d_reclen; + + /* Length of name without \0 */ + size_t d_namlen; + + /* File type */ + int d_type; + + /* File name */ + wchar_t d_name[PATH_MAX+1]; +}; +typedef struct _wdirent _wdirent; + +struct _WDIR { + /* Current directory entry */ + struct _wdirent ent; + + /* Private file data */ + WIN32_FIND_DATAW data; + + /* True if data is valid */ + int cached; + + /* Win32 search handle */ + HANDLE handle; + + /* Initial directory name */ + wchar_t *patt; +}; +typedef struct _WDIR _WDIR; + +/* Multi-byte character version */ +struct dirent { + /* Always zero */ + long d_ino; + + /* File position within stream */ + long d_off; + + /* Structure size */ + unsigned short d_reclen; + + /* Length of name without \0 */ + size_t d_namlen; + + /* File type */ + int d_type; + + /* File name */ + char d_name[PATH_MAX+1]; +}; +typedef struct dirent dirent; + +struct DIR { + struct dirent ent; + struct _WDIR *wdirp; +}; +typedef struct DIR DIR; + + +/* Dirent functions */ +static DIR *opendir (const char *dirname); +static _WDIR *_wopendir (const wchar_t *dirname); + +static struct dirent *readdir (DIR *dirp); +static struct _wdirent *_wreaddir (_WDIR *dirp); + +static int readdir_r( + DIR *dirp, struct dirent *entry, struct dirent **result); +static int _wreaddir_r( + _WDIR *dirp, struct _wdirent *entry, struct _wdirent **result); + +static int closedir (DIR *dirp); +static int _wclosedir (_WDIR *dirp); + +static void rewinddir (DIR* dirp); +static void _wrewinddir (_WDIR* dirp); + +static int scandir (const char *dirname, struct dirent ***namelist, + int (*filter)(const struct dirent*), + int (*compare)(const struct dirent**, const struct dirent**)); + +static int alphasort (const struct dirent **a, const struct dirent **b); + +static int versionsort (const struct dirent **a, const struct dirent **b); + + +/* For compatibility with Symbian */ +#define wdirent _wdirent +#define WDIR _WDIR +#define wopendir _wopendir +#define wreaddir _wreaddir +#define wclosedir _wclosedir +#define wrewinddir _wrewinddir + + +/* Internal utility functions */ +static WIN32_FIND_DATAW *dirent_first (_WDIR *dirp); +static WIN32_FIND_DATAW *dirent_next (_WDIR *dirp); + +static int dirent_mbstowcs_s( + size_t *pReturnValue, + wchar_t *wcstr, + size_t sizeInWords, + const char *mbstr, + size_t count); + +static int dirent_wcstombs_s( + size_t *pReturnValue, + char *mbstr, + size_t sizeInBytes, + const wchar_t *wcstr, + size_t count); + +static void dirent_set_errno (int error); + + +/* + * Open directory stream DIRNAME for read and return a pointer to the + * internal working area that is used to retrieve individual directory + * entries. + */ +static _WDIR* +_wopendir( + const wchar_t *dirname) +{ + _WDIR *dirp; +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + /* Desktop */ + DWORD n; +#else + /* WinRT */ + size_t n; +#endif + wchar_t *p; + + /* Must have directory name */ + if (dirname == NULL || dirname[0] == '\0') { + dirent_set_errno (ENOENT); + return NULL; + } + + /* Allocate new _WDIR structure */ + dirp = (_WDIR*) malloc (sizeof (struct _WDIR)); + if (!dirp) { + return NULL; + } + + /* Reset _WDIR structure */ + dirp->handle = INVALID_HANDLE_VALUE; + dirp->patt = NULL; + dirp->cached = 0; + + /* + * Compute the length of full path plus zero terminator + * + * Note that on WinRT there's no way to convert relative paths + * into absolute paths, so just assume it is an absolute path. + */ +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + /* Desktop */ + n = GetFullPathNameW (dirname, 0, NULL, NULL); +#else + /* WinRT */ + n = wcslen (dirname); +#endif + + /* Allocate room for absolute directory name and search pattern */ + dirp->patt = (wchar_t*) malloc (sizeof (wchar_t) * n + 16); + if (dirp->patt == NULL) { + goto exit_closedir; + } + + /* + * Convert relative directory name to an absolute one. This + * allows rewinddir() to function correctly even when current + * working directory is changed between opendir() and rewinddir(). + * + * Note that on WinRT there's no way to convert relative paths + * into absolute paths, so just assume it is an absolute path. + */ +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + /* Desktop */ + n = GetFullPathNameW (dirname, n, dirp->patt, NULL); + if (n <= 0) { + goto exit_closedir; + } +#else + /* WinRT */ + wcsncpy_s (dirp->patt, n+1, dirname, n); +#endif + + /* Append search pattern \* to the directory name */ + p = dirp->patt + n; + switch (p[-1]) { + case '\\': + case '/': + case ':': + /* Directory ends in path separator, e.g. c:\temp\ */ + /*NOP*/; + break; + + default: + /* Directory name doesn't end in path separator */ + *p++ = '\\'; + } + *p++ = '*'; + *p = '\0'; + + /* Open directory stream and retrieve the first entry */ + if (!dirent_first (dirp)) { + goto exit_closedir; + } + + /* Success */ + return dirp; + + /* Failure */ +exit_closedir: + _wclosedir (dirp); + return NULL; +} + +/* + * Read next directory entry. + * + * Returns pointer to static directory entry which may be overwritten by + * subsequent calls to _wreaddir(). + */ +static struct _wdirent* +_wreaddir( + _WDIR *dirp) +{ + struct _wdirent *entry; + + /* + * Read directory entry to buffer. We can safely ignore the return value + * as entry will be set to NULL in case of error. + */ + (void) _wreaddir_r (dirp, &dirp->ent, &entry); + + /* Return pointer to statically allocated directory entry */ + return entry; +} + +/* + * Read next directory entry. + * + * Returns zero on success. If end of directory stream is reached, then sets + * result to NULL and returns zero. + */ +static int +_wreaddir_r( + _WDIR *dirp, + struct _wdirent *entry, + struct _wdirent **result) +{ + WIN32_FIND_DATAW *datap; + + /* Read next directory entry */ + datap = dirent_next (dirp); + if (datap) { + size_t n; + DWORD attr; + + /* + * Copy file name as wide-character string. If the file name is too + * long to fit in to the destination buffer, then truncate file name + * to PATH_MAX characters and zero-terminate the buffer. + */ + n = 0; + while (n < PATH_MAX && datap->cFileName[n] != 0) { + entry->d_name[n] = datap->cFileName[n]; + n++; + } + entry->d_name[n] = 0; + + /* Length of file name excluding zero terminator */ + entry->d_namlen = n; + + /* File type */ + attr = datap->dwFileAttributes; + if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { + entry->d_type = DT_CHR; + } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { + entry->d_type = DT_DIR; + } else { + entry->d_type = DT_REG; + } + + /* Reset dummy fields */ + entry->d_ino = 0; + entry->d_off = 0; + entry->d_reclen = sizeof (struct _wdirent); + + /* Set result address */ + *result = entry; + + } else { + + /* Return NULL to indicate end of directory */ + *result = NULL; + + } + + return /*OK*/0; +} + +/* + * Close directory stream opened by opendir() function. This invalidates the + * DIR structure as well as any directory entry read previously by + * _wreaddir(). + */ +static int +_wclosedir( + _WDIR *dirp) +{ + int ok; + if (dirp) { + + /* Release search handle */ + if (dirp->handle != INVALID_HANDLE_VALUE) { + FindClose (dirp->handle); + } + + /* Release search pattern */ + free (dirp->patt); + + /* Release directory structure */ + free (dirp); + ok = /*success*/0; + + } else { + + /* Invalid directory stream */ + dirent_set_errno (EBADF); + ok = /*failure*/-1; + + } + return ok; +} + +/* + * Rewind directory stream such that _wreaddir() returns the very first + * file name again. + */ +static void +_wrewinddir( + _WDIR* dirp) +{ + if (dirp) { + /* Release existing search handle */ + if (dirp->handle != INVALID_HANDLE_VALUE) { + FindClose (dirp->handle); + } + + /* Open new search handle */ + dirent_first (dirp); + } +} + +/* Get first directory entry (internal) */ +static WIN32_FIND_DATAW* +dirent_first( + _WDIR *dirp) +{ + WIN32_FIND_DATAW *datap; + DWORD error; + + /* Open directory and retrieve the first entry */ + dirp->handle = FindFirstFileExW( + dirp->patt, FindExInfoStandard, &dirp->data, + FindExSearchNameMatch, NULL, 0); + if (dirp->handle != INVALID_HANDLE_VALUE) { + + /* a directory entry is now waiting in memory */ + datap = &dirp->data; + dirp->cached = 1; + + } else { + + /* Failed to open directory: no directory entry in memory */ + dirp->cached = 0; + datap = NULL; + + /* Set error code */ + error = GetLastError (); + switch (error) { + case ERROR_ACCESS_DENIED: + /* No read access to directory */ + dirent_set_errno (EACCES); + break; + + case ERROR_DIRECTORY: + /* Directory name is invalid */ + dirent_set_errno (ENOTDIR); + break; + + case ERROR_PATH_NOT_FOUND: + default: + /* Cannot find the file */ + dirent_set_errno (ENOENT); + } + + } + return datap; +} + +/* + * Get next directory entry (internal). + * + * Returns + */ +static WIN32_FIND_DATAW* +dirent_next( + _WDIR *dirp) +{ + WIN32_FIND_DATAW *p; + + /* Get next directory entry */ + if (dirp->cached != 0) { + + /* A valid directory entry already in memory */ + p = &dirp->data; + dirp->cached = 0; + + } else if (dirp->handle != INVALID_HANDLE_VALUE) { + + /* Get the next directory entry from stream */ + if (FindNextFileW (dirp->handle, &dirp->data) != FALSE) { + /* Got a file */ + p = &dirp->data; + } else { + /* The very last entry has been processed or an error occurred */ + FindClose (dirp->handle); + dirp->handle = INVALID_HANDLE_VALUE; + p = NULL; + } + + } else { + + /* End of directory stream reached */ + p = NULL; + + } + + return p; +} + +/* + * Open directory stream using plain old C-string. + */ +static DIR* +opendir( + const char *dirname) +{ + struct DIR *dirp; + + /* Must have directory name */ + if (dirname == NULL || dirname[0] == '\0') { + dirent_set_errno (ENOENT); + return NULL; + } + + /* Allocate memory for DIR structure */ + dirp = (DIR*) malloc (sizeof (struct DIR)); + if (!dirp) { + return NULL; + } + { + int error; + wchar_t wname[PATH_MAX + 1]; + size_t n; + + /* Convert directory name to wide-character string */ + error = dirent_mbstowcs_s( + &n, wname, PATH_MAX + 1, dirname, PATH_MAX + 1); + if (error) { + /* + * Cannot convert file name to wide-character string. This + * occurs if the string contains invalid multi-byte sequences or + * the output buffer is too small to contain the resulting + * string. + */ + goto exit_free; + } + + + /* Open directory stream using wide-character name */ + dirp->wdirp = _wopendir (wname); + if (!dirp->wdirp) { + goto exit_free; + } + + } + + /* Success */ + return dirp; + + /* Failure */ +exit_free: + free (dirp); + return NULL; +} + +/* + * Read next directory entry. + */ +static struct dirent* +readdir( + DIR *dirp) +{ + struct dirent *entry; + + /* + * Read directory entry to buffer. We can safely ignore the return value + * as entry will be set to NULL in case of error. + */ + (void) readdir_r (dirp, &dirp->ent, &entry); + + /* Return pointer to statically allocated directory entry */ + return entry; +} + +/* + * Read next directory entry into called-allocated buffer. + * + * Returns zero on success. If the end of directory stream is reached, then + * sets result to NULL and returns zero. + */ +static int +readdir_r( + DIR *dirp, + struct dirent *entry, + struct dirent **result) +{ + WIN32_FIND_DATAW *datap; + + /* Read next directory entry */ + datap = dirent_next (dirp->wdirp); + if (datap) { + size_t n; + int error; + + /* Attempt to convert file name to multi-byte string */ + error = dirent_wcstombs_s( + &n, entry->d_name, PATH_MAX + 1, datap->cFileName, PATH_MAX + 1); + + /* + * If the file name cannot be represented by a multi-byte string, + * then attempt to use old 8+3 file name. This allows traditional + * Unix-code to access some file names despite of unicode + * characters, although file names may seem unfamiliar to the user. + * + * Be ware that the code below cannot come up with a short file + * name unless the file system provides one. At least + * VirtualBox shared folders fail to do this. + */ + if (error && datap->cAlternateFileName[0] != '\0') { + error = dirent_wcstombs_s( + &n, entry->d_name, PATH_MAX + 1, + datap->cAlternateFileName, PATH_MAX + 1); + } + + if (!error) { + DWORD attr; + + /* Length of file name excluding zero terminator */ + entry->d_namlen = n - 1; + + /* File attributes */ + attr = datap->dwFileAttributes; + if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { + entry->d_type = DT_CHR; + } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { + entry->d_type = DT_DIR; + } else { + entry->d_type = DT_REG; + } + + /* Reset dummy fields */ + entry->d_ino = 0; + entry->d_off = 0; + entry->d_reclen = sizeof (struct dirent); + + } else { + + /* + * Cannot convert file name to multi-byte string so construct + * an erroneous directory entry and return that. Note that + * we cannot return NULL as that would stop the processing + * of directory entries completely. + */ + entry->d_name[0] = '?'; + entry->d_name[1] = '\0'; + entry->d_namlen = 1; + entry->d_type = DT_UNKNOWN; + entry->d_ino = 0; + entry->d_off = -1; + entry->d_reclen = 0; + + } + + /* Return pointer to directory entry */ + *result = entry; + + } else { + + /* No more directory entries */ + *result = NULL; + + } + + return /*OK*/0; +} + +/* + * Close directory stream. + */ +static int +closedir( + DIR *dirp) +{ + int ok; + if (dirp) { + + /* Close wide-character directory stream */ + ok = _wclosedir (dirp->wdirp); + dirp->wdirp = NULL; + + /* Release multi-byte character version */ + free (dirp); + + } else { + + /* Invalid directory stream */ + dirent_set_errno (EBADF); + ok = /*failure*/-1; + + } + return ok; +} + +/* + * Rewind directory stream to beginning. + */ +static void +rewinddir( + DIR* dirp) +{ + /* Rewind wide-character string directory stream */ + _wrewinddir (dirp->wdirp); +} + +/* + * Scan directory for entries. + */ +static int +scandir( + const char *dirname, + struct dirent ***namelist, + int (*filter)(const struct dirent*), + int (*compare)(const struct dirent**, const struct dirent**)) +{ + struct dirent **files = NULL; + size_t size = 0; + size_t allocated = 0; + const size_t init_size = 1; + DIR *dir = NULL; + struct dirent *entry; + struct dirent *tmp = NULL; + size_t i; + int result = 0; + + /* Open directory stream */ + dir = opendir (dirname); + if (dir) { + + /* Read directory entries to memory */ + while (1) { + + /* Enlarge pointer table to make room for another pointer */ + if (size >= allocated) { + void *p; + size_t num_entries; + + /* Compute number of entries in the enlarged pointer table */ + if (size < init_size) { + /* Allocate initial pointer table */ + num_entries = init_size; + } else { + /* Double the size */ + num_entries = size * 2; + } + + /* Allocate first pointer table or enlarge existing table */ + p = realloc (files, sizeof (void*) * num_entries); + if (p != NULL) { + /* Got the memory */ + files = (dirent**) p; + allocated = num_entries; + } else { + /* Out of memory */ + result = -1; + break; + } + + } + + /* Allocate room for temporary directory entry */ + if (tmp == NULL) { + tmp = (struct dirent*) malloc (sizeof (struct dirent)); + if (tmp == NULL) { + /* Cannot allocate temporary directory entry */ + result = -1; + break; + } + } + + /* Read directory entry to temporary area */ + if (readdir_r (dir, tmp, &entry) == /*OK*/0) { + + /* Did we get an entry? */ + if (entry != NULL) { + int pass; + + /* Determine whether to include the entry in result */ + if (filter) { + /* Let the filter function decide */ + pass = filter (tmp); + } else { + /* No filter function, include everything */ + pass = 1; + } + + if (pass) { + /* Store the temporary entry to pointer table */ + files[size++] = tmp; + tmp = NULL; + + /* Keep up with the number of files */ + result++; + } + + } else { + + /* + * End of directory stream reached => sort entries and + * exit. + */ + qsort (files, size, sizeof (void*), + (int (*) (const void*, const void*)) compare); + break; + + } + + } else { + /* Error reading directory entry */ + result = /*Error*/ -1; + break; + } + + } + + } else { + /* Cannot open directory */ + result = /*Error*/ -1; + } + + /* Release temporary directory entry */ + free (tmp); + + /* Release allocated memory on error */ + if (result < 0) { + for (i = 0; i < size; i++) { + free (files[i]); + } + free (files); + files = NULL; + } + + /* Close directory stream */ + if (dir) { + closedir (dir); + } + + /* Pass pointer table to caller */ + if (namelist) { + *namelist = files; + } + return result; +} + +/* Alphabetical sorting */ +static int +alphasort( + const struct dirent **a, const struct dirent **b) +{ + return strcoll ((*a)->d_name, (*b)->d_name); +} + +/* Sort versions */ +static int +versionsort( + const struct dirent **a, const struct dirent **b) +{ + /* FIXME: implement strverscmp and use that */ + return alphasort (a, b); +} + +/* Convert multi-byte string to wide character string */ +static int +dirent_mbstowcs_s( + size_t *pReturnValue, + wchar_t *wcstr, + size_t sizeInWords, + const char *mbstr, + size_t count) +{ + int error; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 or later */ + error = mbstowcs_s (pReturnValue, wcstr, sizeInWords, mbstr, count); + +#else + + /* Older Visual Studio or non-Microsoft compiler */ + size_t n; + + /* Convert to wide-character string (or count characters) */ + n = mbstowcs (wcstr, mbstr, sizeInWords); + if (!wcstr || n < count) { + + /* Zero-terminate output buffer */ + if (wcstr && sizeInWords) { + if (n >= sizeInWords) { + n = sizeInWords - 1; + } + wcstr[n] = 0; + } + + /* Length of resulting multi-byte string WITH zero terminator */ + if (pReturnValue) { + *pReturnValue = n + 1; + } + + /* Success */ + error = 0; + + } else { + + /* Could not convert string */ + error = 1; + + } + +#endif + return error; +} + +/* Convert wide-character string to multi-byte string */ +static int +dirent_wcstombs_s( + size_t *pReturnValue, + char *mbstr, + size_t sizeInBytes, /* max size of mbstr */ + const wchar_t *wcstr, + size_t count) +{ + int error; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 or later */ + error = wcstombs_s (pReturnValue, mbstr, sizeInBytes, wcstr, count); + +#else + + /* Older Visual Studio or non-Microsoft compiler */ + size_t n; + + /* Convert to multi-byte string (or count the number of bytes needed) */ + n = wcstombs (mbstr, wcstr, sizeInBytes); + if (!mbstr || n < count) { + + /* Zero-terminate output buffer */ + if (mbstr && sizeInBytes) { + if (n >= sizeInBytes) { + n = sizeInBytes - 1; + } + mbstr[n] = '\0'; + } + + /* Length of resulting multi-bytes string WITH zero-terminator */ + if (pReturnValue) { + *pReturnValue = n + 1; + } + + /* Success */ + error = 0; + + } else { + + /* Cannot convert string */ + error = 1; + + } + +#endif + return error; +} + +/* Set errno variable */ +static void +dirent_set_errno( + int error) +{ +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 and later */ + _set_errno (error); + +#else + + /* Non-Microsoft compiler or older Microsoft compiler */ + errno = error; + +#endif +} + + +#ifdef __cplusplus +} +#endif +#endif /*DIRENT_H*/ \ No newline at end of file diff --git a/win/getopt.h b/win/getopt.h new file mode 100644 index 000000000..d78b753b2 --- /dev/null +++ b/win/getopt.h @@ -0,0 +1,653 @@ +#ifndef __GETOPT_H__ +/** + * DISCLAIMER + * This file is part of the mingw-w64 runtime package. + * + * The mingw-w64 runtime package and its code is distributed in the hope that it + * will be useful but WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESSED OR + * IMPLIED ARE HEREBY DISCLAIMED. This includes but is not limited to + * warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + /* + * Copyright (c) 2002 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and Air Force Research Laboratory, Air Force + * Materiel Command, USAF, under agreement number F39502-99-1-0512. + */ +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Dieter Baron and Thomas Klausner. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma warning(disable:4996) + +#define __GETOPT_H__ + +/* All the headers include this file. */ +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define REPLACE_GETOPT /* use this getopt as the system getopt(3) */ + +#ifdef REPLACE_GETOPT +int opterr = 1; /* if error message should be printed */ +int optind = 1; /* index into parent argv vector */ +int optopt = '?'; /* character checked for validity */ +#undef optreset /* see getopt.h */ +#define optreset __mingw_optreset +int optreset; /* reset getopt */ +char *optarg; /* argument associated with option */ +#endif + +//extern int optind; /* index of first non-option in argv */ +//extern int optopt; /* single option character, as parsed */ +//extern int opterr; /* flag to enable built-in diagnostics... */ +// /* (user may set to zero, to suppress) */ +// +//extern char *optarg; /* pointer to argument of current option */ + +#define PRINT_ERROR ((opterr) && (*options != ':')) + +#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */ +#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */ +#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */ + +/* return values */ +#define BADCH (int)'?' +#define BADARG ((*options == ':') ? (int)':' : (int)'?') +#define INORDER (int)1 + +#ifndef __CYGWIN__ +#define __progname __argv[0] +#else +extern char __declspec(dllimport) *__progname; +#endif + +#ifdef __CYGWIN__ +static char EMSG[] = ""; +#else +#define EMSG "" +#endif + +static int getopt_internal(int, char * const *, const char *, + const struct option *, int *, int); +static int parse_long_options(char * const *, const char *, + const struct option *, int *, int); +static int gcd(int, int); +static void permute_args(int, int, int, char * const *); + +static char *place = EMSG; /* option letter processing */ + +/* XXX: set optreset to 1 rather than these two */ +static int nonopt_start = -1; /* first non option argument (for permute) */ +static int nonopt_end = -1; /* first option after non options (for permute) */ + +/* Error messages */ +static const char recargchar[] = "option requires an argument -- %c"; +static const char recargstring[] = "option requires an argument -- %s"; +static const char ambig[] = "ambiguous option -- %.*s"; +static const char noarg[] = "option doesn't take an argument -- %.*s"; +static const char illoptchar[] = "unknown option -- %c"; +static const char illoptstring[] = "unknown option -- %s"; + +static void +_vwarnx(const char *fmt,va_list ap) +{ + (void)fprintf(stderr,"%s: ",__progname); + if (fmt != NULL) + (void)vfprintf(stderr,fmt,ap); + (void)fprintf(stderr,"\n"); +} + +static void +warnx(const char *fmt,...) +{ + va_list ap; + va_start(ap,fmt); + _vwarnx(fmt,ap); + va_end(ap); +} + +/* + * Compute the greatest common divisor of a and b. + */ +static int +gcd(int a, int b) +{ + int c; + + c = a % b; + while (c != 0) { + a = b; + b = c; + c = a % b; + } + + return (b); +} + +/* + * Exchange the block from nonopt_start to nonopt_end with the block + * from nonopt_end to opt_end (keeping the same order of arguments + * in each block). + */ +static void +permute_args(int panonopt_start, int panonopt_end, int opt_end, + char * const *nargv) +{ + int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos; + char *swap; + + /* + * compute lengths of blocks and number and size of cycles + */ + nnonopts = panonopt_end - panonopt_start; + nopts = opt_end - panonopt_end; + ncycle = gcd(nnonopts, nopts); + cyclelen = (opt_end - panonopt_start) / ncycle; + + for (i = 0; i < ncycle; i++) { + cstart = panonopt_end+i; + pos = cstart; + for (j = 0; j < cyclelen; j++) { + if (pos >= panonopt_end) + pos -= nnonopts; + else + pos += nopts; + swap = nargv[pos]; + /* LINTED const cast */ + ((char **) nargv)[pos] = nargv[cstart]; + /* LINTED const cast */ + ((char **)nargv)[cstart] = swap; + } + } +} + +#ifdef REPLACE_GETOPT +/* + * getopt -- + * Parse argc/argv argument vector. + * + * [eventually this will replace the BSD getopt] + */ +int +getopt(int nargc, char * const *nargv, const char *options) +{ + + /* + * We don't pass FLAG_PERMUTE to getopt_internal() since + * the BSD getopt(3) (unlike GNU) has never done this. + * + * Furthermore, since many privileged programs call getopt() + * before dropping privileges it makes sense to keep things + * as simple (and bug-free) as possible. + */ + return (getopt_internal(nargc, nargv, options, NULL, NULL, 0)); +} +#endif /* REPLACE_GETOPT */ + +//extern int getopt(int nargc, char * const *nargv, const char *options); + +#ifdef _BSD_SOURCE +/* + * BSD adds the non-standard `optreset' feature, for reinitialisation + * of `getopt' parsing. We support this feature, for applications which + * proclaim their BSD heritage, before including this header; however, + * to maintain portability, developers are advised to avoid it. + */ +# define optreset __mingw_optreset +extern int optreset; +#endif +#ifdef __cplusplus +} +#endif +/* + * POSIX requires the `getopt' API to be specified in `unistd.h'; + * thus, `unistd.h' includes this header. However, we do not want + * to expose the `getopt_long' or `getopt_long_only' APIs, when + * included in this manner. Thus, close the standard __GETOPT_H__ + * declarations block, and open an additional __GETOPT_LONG_H__ + * specific block, only when *not* __UNISTD_H_SOURCED__, in which + * to declare the extended API. + */ +#endif /* !defined(__GETOPT_H__) */ + +#if !defined(__UNISTD_H_SOURCED__) && !defined(__GETOPT_LONG_H__) +#define __GETOPT_LONG_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +struct option /* specification for a long form option... */ +{ + const char *name; /* option name, without leading hyphens */ + int has_arg; /* does it take an argument? */ + int *flag; /* where to save its status, or NULL */ + int val; /* its associated status value */ +}; + +enum /* permitted values for its `has_arg' field... */ +{ + no_argument = 0, /* option never takes an argument */ + required_argument, /* option always requires an argument */ + optional_argument /* option may take an argument */ +}; + +/* + * parse_long_options -- + * Parse long options in argc/argv argument vector. + * Returns -1 if short_too is set and the option does not match long_options. + */ +static int +parse_long_options(char * const *nargv, const char *options, + const struct option *long_options, int *idx, int short_too) +{ + char *current_argv, *has_equal; + size_t current_argv_len; + int i, ambiguous, match; + +#define IDENTICAL_INTERPRETATION(_x, _y) \ + (long_options[(_x)].has_arg == long_options[(_y)].has_arg && \ + long_options[(_x)].flag == long_options[(_y)].flag && \ + long_options[(_x)].val == long_options[(_y)].val) + + current_argv = place; + match = -1; + ambiguous = 0; + + optind++; + + if ((has_equal = strchr(current_argv, '=')) != NULL) { + /* argument found (--option=arg) */ + current_argv_len = has_equal - current_argv; + has_equal++; + } else + current_argv_len = strlen(current_argv); + + for (i = 0; long_options[i].name; i++) { + /* find matching long option */ + if (strncmp(current_argv, long_options[i].name, + current_argv_len)) + continue; + + if (strlen(long_options[i].name) == current_argv_len) { + /* exact match */ + match = i; + ambiguous = 0; + break; + } + /* + * If this is a known short option, don't allow + * a partial match of a single character. + */ + if (short_too && current_argv_len == 1) + continue; + + if (match == -1) /* partial match */ + match = i; + else if (!IDENTICAL_INTERPRETATION(i, match)) + ambiguous = 1; + } + if (ambiguous) { + /* ambiguous abbreviation */ + if (PRINT_ERROR) + warnx(ambig, (int)current_argv_len, + current_argv); + optopt = 0; + return (BADCH); + } + if (match != -1) { /* option found */ + if (long_options[match].has_arg == no_argument + && has_equal) { + if (PRINT_ERROR) + warnx(noarg, (int)current_argv_len, + current_argv); + /* + * XXX: GNU sets optopt to val regardless of flag + */ + if (long_options[match].flag == NULL) + optopt = long_options[match].val; + else + optopt = 0; + return (BADARG); + } + if (long_options[match].has_arg == required_argument || + long_options[match].has_arg == optional_argument) { + if (has_equal) + optarg = has_equal; + else if (long_options[match].has_arg == + required_argument) { + /* + * optional argument doesn't use next nargv + */ + optarg = nargv[optind++]; + } + } + if ((long_options[match].has_arg == required_argument) + && (optarg == NULL)) { + /* + * Missing argument; leading ':' indicates no error + * should be generated. + */ + if (PRINT_ERROR) + warnx(recargstring, + current_argv); + /* + * XXX: GNU sets optopt to val regardless of flag + */ + if (long_options[match].flag == NULL) + optopt = long_options[match].val; + else + optopt = 0; + --optind; + return (BADARG); + } + } else { /* unknown option */ + if (short_too) { + --optind; + return (-1); + } + if (PRINT_ERROR) + warnx(illoptstring, current_argv); + optopt = 0; + return (BADCH); + } + if (idx) + *idx = match; + if (long_options[match].flag) { + *long_options[match].flag = long_options[match].val; + return (0); + } else + return (long_options[match].val); +#undef IDENTICAL_INTERPRETATION +} + +/* + * getopt_internal -- + * Parse argc/argv argument vector. Called by user level routines. + */ +static int +getopt_internal(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx, int flags) +{ + char *oli; /* option letter list index */ + int optchar, short_too; + static int posixly_correct = -1; + + if (options == NULL) + return (-1); + + /* + * XXX Some GNU programs (like cvs) set optind to 0 instead of + * XXX using optreset. Work around this braindamage. + */ + if (optind == 0) + optind = optreset = 1; + + /* + * Disable GNU extensions if POSIXLY_CORRECT is set or options + * string begins with a '+'. + * + * CV, 2009-12-14: Check POSIXLY_CORRECT anew if optind == 0 or + * optreset != 0 for GNU compatibility. + */ + if (posixly_correct == -1 || optreset != 0) + posixly_correct = (getenv("POSIXLY_CORRECT") != NULL); + if (*options == '-') + flags |= FLAG_ALLARGS; + else if (posixly_correct || *options == '+') + flags &= ~FLAG_PERMUTE; + if (*options == '+' || *options == '-') + options++; + + optarg = NULL; + if (optreset) + nonopt_start = nonopt_end = -1; +start: + if (optreset || !*place) { /* update scanning pointer */ + optreset = 0; + if (optind >= nargc) { /* end of argument vector */ + place = EMSG; + if (nonopt_end != -1) { + /* do permutation, if we have to */ + permute_args(nonopt_start, nonopt_end, + optind, nargv); + optind -= nonopt_end - nonopt_start; + } + else if (nonopt_start != -1) { + /* + * If we skipped non-options, set optind + * to the first of them. + */ + optind = nonopt_start; + } + nonopt_start = nonopt_end = -1; + return (-1); + } + if (*(place = nargv[optind]) != '-' || + (place[1] == '\0' && strchr(options, '-') == NULL)) { + place = EMSG; /* found non-option */ + if (flags & FLAG_ALLARGS) { + /* + * GNU extension: + * return non-option as argument to option 1 + */ + optarg = nargv[optind++]; + return (INORDER); + } + if (!(flags & FLAG_PERMUTE)) { + /* + * If no permutation wanted, stop parsing + * at first non-option. + */ + return (-1); + } + /* do permutation */ + if (nonopt_start == -1) + nonopt_start = optind; + else if (nonopt_end != -1) { + permute_args(nonopt_start, nonopt_end, + optind, nargv); + nonopt_start = optind - + (nonopt_end - nonopt_start); + nonopt_end = -1; + } + optind++; + /* process next argument */ + goto start; + } + if (nonopt_start != -1 && nonopt_end == -1) + nonopt_end = optind; + + /* + * If we have "-" do nothing, if "--" we are done. + */ + if (place[1] != '\0' && *++place == '-' && place[1] == '\0') { + optind++; + place = EMSG; + /* + * We found an option (--), so if we skipped + * non-options, we have to permute. + */ + if (nonopt_end != -1) { + permute_args(nonopt_start, nonopt_end, + optind, nargv); + optind -= nonopt_end - nonopt_start; + } + nonopt_start = nonopt_end = -1; + return (-1); + } + } + + /* + * Check long options if: + * 1) we were passed some + * 2) the arg is not just "-" + * 3) either the arg starts with -- we are getopt_long_only() + */ + if (long_options != NULL && place != nargv[optind] && + (*place == '-' || (flags & FLAG_LONGONLY))) { + short_too = 0; + if (*place == '-') + place++; /* --foo long option */ + else if (*place != ':' && strchr(options, *place) != NULL) + short_too = 1; /* could be short option too */ + + optchar = parse_long_options(nargv, options, long_options, + idx, short_too); + if (optchar != -1) { + place = EMSG; + return (optchar); + } + } + + if ((optchar = (int)*place++) == (int)':' || + (optchar == (int)'-' && *place != '\0') || + (oli = (char*)strchr(options, optchar)) == NULL) { + /* + * If the user specified "-" and '-' isn't listed in + * options, return -1 (non-option) as per POSIX. + * Otherwise, it is an unknown option character (or ':'). + */ + if (optchar == (int)'-' && *place == '\0') + return (-1); + if (!*place) + ++optind; + if (PRINT_ERROR) + warnx(illoptchar, optchar); + optopt = optchar; + return (BADCH); + } + if (long_options != NULL && optchar == 'W' && oli[1] == ';') { + /* -W long-option */ + if (*place) /* no space */ + /* NOTHING */; + else if (++optind >= nargc) { /* no arg */ + place = EMSG; + if (PRINT_ERROR) + warnx(recargchar, optchar); + optopt = optchar; + return (BADARG); + } else /* white space */ + place = nargv[optind]; + optchar = parse_long_options(nargv, options, long_options, + idx, 0); + place = EMSG; + return (optchar); + } + if (*++oli != ':') { /* doesn't take argument */ + if (!*place) + ++optind; + } else { /* takes (optional) argument */ + optarg = NULL; + if (*place) /* no white space */ + optarg = place; + else if (oli[1] != ':') { /* arg not optional */ + if (++optind >= nargc) { /* no arg */ + place = EMSG; + if (PRINT_ERROR) + warnx(recargchar, optchar); + optopt = optchar; + return (BADARG); + } else + optarg = nargv[optind]; + } + place = EMSG; + ++optind; + } + /* dump back option letter */ + return (optchar); +} + +/* + * getopt_long -- + * Parse argc/argv argument vector. + */ +int +getopt_long(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx) +{ + + return (getopt_internal(nargc, nargv, options, long_options, idx, + FLAG_PERMUTE)); +} + +/* + * getopt_long_only -- + * Parse argc/argv argument vector. + */ +int +getopt_long_only(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx) +{ + + return (getopt_internal(nargc, nargv, options, long_options, idx, + FLAG_PERMUTE|FLAG_LONGONLY)); +} + +//extern int getopt_long(int nargc, char * const *nargv, const char *options, +// const struct option *long_options, int *idx); +//extern int getopt_long_only(int nargc, char * const *nargv, const char *options, +// const struct option *long_options, int *idx); +/* + * Previous MinGW implementation had... + */ +#ifndef HAVE_DECL_GETOPT +/* + * ...for the long form API only; keep this for compatibility. + */ +# define HAVE_DECL_GETOPT 1 +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* !defined(__UNISTD_H_SOURCED__) && !defined(__GETOPT_LONG_H__) */ \ No newline at end of file diff --git a/win/stdatomic.h b/win/stdatomic.h new file mode 100644 index 000000000..7e0ab5726 --- /dev/null +++ b/win/stdatomic.h @@ -0,0 +1,1575 @@ +#ifndef __STDATOMIC_H__ +#define __STDATOMIC_H__ + +/* + * Copyright (c) 2015-2021 Christoph Schreiber + * + * Distributed under the Boost Software License, Version 1.0. + * (http://www.boost.org/LICENSE_1_0.txt) + */ + +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) \ + && !defined(__STDC_NO_ATOMICS__) +# include +#elif defined(_MSC_VER) && (_MSC_VER >= 1800) /* Visual Studio 2013 */ \ + && (defined(_M_X64) || defined(_M_IX86)) +# define USE_TEMPORARY_MSVC_WORKAROUND 1 +#else +# error Atomic operations are not supported on your platform +#endif /* defined(__STDC_VERSION__) ... */ + +#if defined(USE_TEMPORARY_MSVC_WORKAROUND) + +#include +#include +#include +#include + +#pragma intrinsic(_ReadWriteBarrier) +#pragma intrinsic(_InterlockedIncrement) +#pragma intrinsic(_InterlockedExchange8) +#pragma intrinsic(_InterlockedExchange16) +#pragma intrinsic(_InterlockedExchange) +#if defined(_M_X64) +# pragma intrinsic(_InterlockedExchange64) +#endif /* defined(_M_X64) */ +#pragma intrinsic(_InterlockedCompareExchange8) +#pragma intrinsic(_InterlockedCompareExchange16) +#pragma intrinsic(_InterlockedCompareExchange) +#pragma intrinsic(_InterlockedCompareExchange64) +#pragma intrinsic(_InterlockedExchangeAdd8) +#pragma intrinsic(_InterlockedExchangeAdd16) +#pragma intrinsic(_InterlockedExchangeAdd) +#if defined(_M_X64) +# pragma intrinsic(_InterlockedExchangeAdd64) +#endif /* defined(_M_X64) */ +#pragma intrinsic(_InterlockedOr8) +#pragma intrinsic(_InterlockedOr16) +#pragma intrinsic(_InterlockedOr) +#if defined(_M_X64) +# pragma intrinsic(_InterlockedOr64) +#endif /* defined(_M_X64) */ +#pragma intrinsic(_InterlockedXor8) +#pragma intrinsic(_InterlockedXor16) +#pragma intrinsic(_InterlockedXor) +#if defined(_M_X64) +# pragma intrinsic(_InterlockedXor64) +#endif /* defined(_M_X64) */ +#pragma intrinsic(_InterlockedAnd8) +#pragma intrinsic(_InterlockedAnd16) +#pragma intrinsic(_InterlockedAnd) +#if defined(_M_X64) +# pragma intrinsic(_InterlockedAnd64) +#endif /* defined(_M_X64) */ + +/* + * 7.17.1 Atomic lock-free macros + */ + +#define ATOMIC_BOOL_LOCK_FREE 2 +#define ATOMIC_CHAR_LOCK_FREE 2 +#define ATOMIC_CHAR16_T_LOCK_FREE 2 +#define ATOMIC_CHAR32_T_LOCK_FREE 2 +#define ATOMIC_WCHAR_T_LOCK_FREE 2 +#define ATOMIC_SHORT_LOCK_FREE 2 +#define ATOMIC_INT_LOCK_FREE 2 +#define ATOMIC_LONG_LOCK_FREE 2 +#define ATOMIC_LLONG_LOCK_FREE 2 +#define ATOMIC_POINTER_LOCK_FREE 2 + +/* + * 7.17.2 Initialization + */ + +#define ATOMIC_VAR_INIT(value) (value) + +#define atomic_init(obj, desired) (void)(*(obj) = (desired)) + +/* + * 7.17.3 Order and consistency + */ + +typedef enum memory_order +{ + memory_order_relaxed, + memory_order_consume, + memory_order_acquire, + memory_order_release, + memory_order_acq_rel, + memory_order_seq_cst +} memory_order; + +#define kill_dependency(y) (y) + +/* + * 7.17.4 Fences + */ + +static __forceinline void atomic_thread_fence(memory_order order) +{ + if (order == memory_order_seq_cst) + { + long addend = 0; + _InterlockedIncrement(&addend); + } + else if (order != memory_order_relaxed) + { + _ReadWriteBarrier(); + } +} + +static __forceinline void atomic_signal_fence(memory_order order) +{ + if (order != memory_order_relaxed) + _ReadWriteBarrier(); +} + +/* + * 7.17.5 Lock-free property + */ + +#define atomic_is_lock_free(obj) \ + (sizeof((obj)->val) <= sizeof(__int64)) + +/* + * 7.17.6 Atomic integer types + */ + +/* + * _Atomic - Starting in Visual Studio 2019 version 16.8, this + * keyword is recognized but not supported by the compiler in + * code compiled as C when the /std:c11 or /std:c17 compiler + * options are specified. + * + * MSDN: "You can't redefine keywords. However, you can specify + * text to replace keywords before compilation by using + * C preprocessor directives." + */ + +#define _Atomic(T) T + +typedef _Atomic(bool) atomic_bool; +typedef _Atomic(char) atomic_char; +typedef _Atomic(unsigned char) atomic_uchar; +typedef _Atomic(short) atomic_short; +typedef _Atomic(unsigned short) atomic_ushort; +typedef _Atomic(int) atomic_int; +typedef _Atomic(unsigned int) atomic_uint; +typedef _Atomic(long) atomic_long; +typedef _Atomic(unsigned long) atomic_ulong; +typedef _Atomic(__int64) atomic_llong; +typedef _Atomic(unsigned __int64) atomic_ullong; +typedef _Atomic(uint_least16_t) atomic_wchar_t; +typedef _Atomic(uint_least16_t) atomic_char16_t; +typedef _Atomic(uint_least32_t) atomic_char32_t; +typedef _Atomic(int_least8_t) atomic_int_least8_t; +typedef _Atomic(uint_least8_t) atomic_uint_least8_t; +typedef _Atomic(int_least16_t) atomic_int_least16_t; +typedef _Atomic(uint_least16_t) atomic_uint_least16_t; +typedef _Atomic(int_least32_t) atomic_int_least32_t; +typedef _Atomic(uint_least32_t) atomic_uint_least32_t; +typedef _Atomic(int_least64_t) atomic_int_least64_t; +typedef _Atomic(uint_least64_t) atomic_uint_least64_t; +typedef _Atomic(int_fast8_t) atomic_int_fast8_t; +typedef _Atomic(uint_fast8_t) atomic_uint_fast8_t; +typedef _Atomic(int_fast16_t) atomic_int_fast16_t; +typedef _Atomic(uint_fast16_t) atomic_uint_fast16_t; +typedef _Atomic(int_fast32_t) atomic_int_fast32_t; +typedef _Atomic(uint_fast32_t) atomic_uint_fast32_t; +typedef _Atomic(int_fast64_t) atomic_int_fast64_t; +typedef _Atomic(uint_fast64_t) atomic_uint_fast64_t; +typedef _Atomic(intptr_t) atomic_intptr_t; +typedef _Atomic(uintptr_t) atomic_uintptr_t; +typedef _Atomic(size_t) atomic_size_t; +typedef _Atomic(ptrdiff_t) atomic_ptrdiff_t; +typedef _Atomic(intmax_t) atomic_intmax_t; +typedef _Atomic(uintmax_t) atomic_uintmax_t; + +/* + * 7.17.7 Operations on atomic types + */ + +#define atomic_store(obj, desired) \ + atomic_store_explicit((obj), (desired), memory_order_seq_cst) + +#define atomic_load(obj) \ + atomic_load_explicit((obj), memory_order_seq_cst) + +#define atomic_exchange(obj, desired) \ + atomic_exchange_explicit((obj), (desired), memory_order_seq_cst) + +#define atomic_compare_exchange_strong(obj, expected, desired) \ + atomic_compare_exchange_strong_explicit((obj), (expected) \ + , (desired), memory_order_seq_cst, memory_order_seq_cst) + +#define atomic_compare_exchange_weak(obj, expected, desired) \ + atomic_compare_exchange_weak_explicit((obj), (expected) \ + , (desired), memory_order_seq_cst, memory_order_seq_cst) + +#define atomic_fetch_add(obj, desired) \ + atomic_fetch_add_explicit((obj), (desired), memory_order_seq_cst) + +#define atomic_fetch_sub(obj, desired) \ + atomic_fetch_sub_explicit((obj), (desired), memory_order_seq_cst) + +#define atomic_fetch_or(obj, desired) \ + atomic_fetch_or_explicit((obj), (desired), memory_order_seq_cst) + +#define atomic_fetch_xor(obj, desired) \ + atomic_fetch_xor_explicit((obj), (desired), memory_order_seq_cst) + +#define atomic_fetch_and(obj, desired) \ + atomic_fetch_and_explicit((obj), (desired), memory_order_seq_cst) + +#if (_MSC_VER >= 1928) + +/* + * Visual Studio 2019 version 16.8 + * + * _Generic - Starting in Visual Studio 2019 version 16.8, this + * keyword is supported in code compiled as C when the /std:c11 + * or /std:c17 compiler options are specified. + */ + +#define atomic_store_explicit(obj, desired, order) \ + _Generic(*(obj), \ + char: atomic_store_char, \ + unsigned char: atomic_store_uchar, \ + bool: atomic_store_bool, \ + short: atomic_store_short, \ + unsigned short: atomic_store_ushort, \ + int: atomic_store_int, \ + unsigned int: atomic_store_uint, \ + long: atomic_store_long, \ + unsigned long: atomic_store_ulong, \ + __int64: atomic_store_llong, \ + unsigned __int64: atomic_store_ullong, \ + default: atomic_store_ptr) \ + ((obj), (desired), (order)) + +#define atomic_load_explicit(obj, order) \ + _Generic(*(obj), \ + char: atomic_load_char, \ + unsigned char: atomic_load_uchar, \ + bool: atomic_load_bool, \ + short: atomic_load_short, \ + unsigned short: atomic_load_ushort, \ + int: atomic_load_int, \ + unsigned int: atomic_load_uint, \ + long: atomic_load_long, \ + unsigned long: atomic_load_ulong, \ + __int64: atomic_load_llong, \ + unsigned __int64: atomic_load_ullong, \ + default: atomic_load_ptr) \ + ((obj), (order)) + +#define atomic_exchange_explicit(obj, desired, order) \ + _Generic(*(obj), \ + char: atomic_exchange_char, \ + unsigned char: atomic_exchange_uchar, \ + bool: atomic_exchange_bool, \ + short: atomic_exchange_short, \ + unsigned short: atomic_exchange_ushort, \ + int: atomic_exchange_int, \ + unsigned int: atomic_exchange_uint, \ + long: atomic_exchange_long, \ + unsigned long: atomic_exchange_ulong, \ + __int64: atomic_exchange_llong, \ + unsigned __int64: atomic_exchange_ullong, \ + default: atomic_exchange_ptr) \ + ((obj), (desired), (order)) + +#define atomic_compare_exchange_strong_explicit(obj, expected \ + , desired, success, failure) \ + _Generic(*(obj), \ + char: atomic_compare_exchange_char, \ + unsigned char: atomic_compare_exchange_uchar, \ + short: atomic_compare_exchange_short, \ + unsigned short: atomic_compare_exchange_ushort, \ + long: atomic_compare_exchange_long, \ + unsigned long: atomic_compare_exchange_ulong, \ + int: atomic_compare_exchange_int, \ + unsigned int: atomic_compare_exchange_uint, \ + __int64: atomic_compare_exchange_llong, \ + unsigned __int64: atomic_compare_exchange_ullong, \ + default: atomic_compare_exchange_ptr) \ + ((obj), (expected), (desired), (success), (failure)) + +#define atomic_compare_exchange_weak_explicit \ + atomic_compare_exchange_strong_explicit + +#define atomic_fetch_add_explicit(obj, op, order) \ + _Generic(*(obj), \ + char: atomic_fetch_add_char, \ + unsigned char: atomic_fetch_add_uchar, \ + short: atomic_fetch_add_short, \ + unsigned short: atomic_fetch_add_ushort, \ + int: atomic_fetch_add_int, \ + unsigned int: atomic_fetch_add_uint, \ + long: atomic_fetch_add_long, \ + unsigned long: atomic_fetch_add_ulong, \ + __int64: atomic_fetch_add_llong, \ + unsigned __int64: atomic_fetch_add_ullong) \ + ((obj), (op), (order)) + +#define atomic_fetch_sub_explicit(obj, op, order) \ + _Generic(*(obj), \ + char: atomic_fetch_sub_char, \ + unsigned char: atomic_fetch_sub_uchar, \ + short: atomic_fetch_sub_short, \ + unsigned short: atomic_fetch_sub_ushort, \ + int: atomic_fetch_sub_int, \ + unsigned int: atomic_fetch_sub_uint, \ + long: atomic_fetch_sub_long, \ + unsigned long: atomic_fetch_sub_ulong, \ + __int64: atomic_fetch_sub_llong, \ + unsigned __int64: atomic_fetch_sub_ullong) \ + ((obj), (op), (order)) + +#define atomic_fetch_or_explicit(obj, op, order) \ + _Generic(*(obj), \ + char: atomic_fetch_or_char, \ + unsigned char: atomic_fetch_or_uchar, \ + short: atomic_fetch_or_short, \ + unsigned short: atomic_fetch_or_ushort, \ + int: atomic_fetch_or_int, \ + unsigned int: atomic_fetch_or_uint, \ + long: atomic_fetch_or_long, \ + unsigned long: atomic_fetch_or_ulong, \ + __int64: atomic_fetch_or_llong, \ + unsigned __int64: atomic_fetch_or_ullong) \ + ((obj), (op), (order)) + +#define atomic_fetch_xor_explicit(obj, op, order) \ + _Generic(*(obj), \ + char: atomic_fetch_xor_char, \ + unsigned char: atomic_fetch_xor_uchar, \ + short: atomic_fetch_xor_short, \ + unsigned short: atomic_fetch_xor_ushort, \ + int: atomic_fetch_xor_int, \ + unsigned int: atomic_fetch_xor_uint, \ + long: atomic_fetch_xor_long, \ + unsigned long: atomic_fetch_xor_ulong, \ + __int64: atomic_fetch_xor_llong, \ + unsigned __int64: atomic_fetch_xor_ullong) \ + ((obj), (op), (order)) + +#define atomic_fetch_and_explicit(obj, op, order) \ + _Generic(*(obj), \ + char: atomic_fetch_and_char, \ + unsigned char: atomic_fetch_and_uchar, \ + short: atomic_fetch_and_short, \ + unsigned short: atomic_fetch_and_ushort, \ + int: atomic_fetch_and_int, \ + unsigned int: atomic_fetch_and_uint, \ + long: atomic_fetch_and_long, \ + unsigned long: atomic_fetch_and_ulong, \ + __int64: atomic_fetch_and_llong, \ + unsigned __int64: atomic_fetch_and_ullong) \ + ((obj), (op), (order)) + +#else /* (_MSC_VER < 1928) */ + +/* + * C4047: type1 differs in levels of indirection from type2 + * C4310: cast truncates constant value + */ + +#define atomic_store_explicit(obj, desired, order) \ + __pragma(warning(suppress: 4310)) \ + ((sizeof(*(obj)) == 1U) \ + ? (atomic_store_char((atomic_char*)(obj) \ + , (char)(intptr_t)(desired), (order)), 0) \ + : ((sizeof(*(obj)) == 2U) \ + ? (atomic_store_short((atomic_short*)(obj) \ + , (short)(intptr_t)(desired), (order)), 0) \ + : ((sizeof(*(obj)) == 4U) \ + ? (atomic_store_long((atomic_long*)(obj) \ + , (long)(intptr_t)(desired), (order)), 0) \ + : ((sizeof(*(obj)) == 8U) \ + ? (atomic_store_llong((atomic_llong*)(obj) \ + , (__int64)(desired), (order)), 0) \ + : (assert(!"Invalid type"), 0))))) + +#define atomic_load_explicit(obj, order) \ + __pragma(warning(suppress: 4047)) \ + ((sizeof(*(obj)) == 1U) \ + ? atomic_load_char((atomic_char*)(obj), (order)) \ + : ((sizeof(*(obj)) == 2U) \ + ? atomic_load_short((atomic_short*)(obj), (order)) \ + : ((sizeof(*(obj)) == 4U) \ + ? atomic_load_long((atomic_long*)(obj), (order)) \ + : ((sizeof(*(obj)) == 8U) \ + ? atomic_load_llong((atomic_llong*)(obj), (order)) \ + : (assert(!"Invalid type"), 0))))) + +#define atomic_exchange_explicit(obj, desired, order) \ + __pragma(warning(suppress: 4047 4310)) \ + ((sizeof(*(obj)) == 1U) \ + ? atomic_exchange_char((atomic_char*)(obj) \ + , (char)(intptr_t)(desired), (order)) \ + : ((sizeof(*(obj)) == 2U) \ + ? atomic_exchange_short((atomic_short*)(obj) \ + , (short)(intptr_t)(desired), (order)) \ + : ((sizeof(*(obj)) == 4U) \ + ? atomic_exchange_long((atomic_long*)(obj) \ + , (long)(intptr_t)(desired), (order)) \ + : ((sizeof(*(obj)) == 8U) \ + ? atomic_exchange_llong((atomic_llong*)(obj) \ + , (__int64)(desired), (order)) \ + : (assert(!"Invalid type"), 0))))) + +#define atomic_compare_exchange_strong_explicit(obj, expected \ + , desired, success, failure) \ + __pragma(warning(suppress: 4310)) \ + ((sizeof(*(obj)) == 1U) \ + ? atomic_compare_exchange_char( \ + (atomic_char*)(obj), (char*)(expected) \ + , (char)(intptr_t)(desired), (success), (failure)) \ + : ((sizeof(*(obj)) == 2U) \ + ? atomic_compare_exchange_short( \ + (atomic_short*)(obj), (short*)(expected) \ + , (short)(intptr_t)(desired), (success), (failure)) \ + : ((sizeof(*(obj)) == 4U) \ + ? atomic_compare_exchange_long( \ + (atomic_long*)(obj), (long*)(expected) \ + , (long)(intptr_t)(desired), (success), (failure)) \ + : ((sizeof(*(obj)) == 8U) \ + ? atomic_compare_exchange_llong( \ + (atomic_llong*)(obj), (__int64*)(expected) \ + , (__int64)(desired), (success), (failure)) \ + : (assert(!"Invalid type"), 0))))) + +#define atomic_compare_exchange_weak_explicit \ + atomic_compare_exchange_strong_explicit + +#define atomic_fetch_add_explicit(obj, op, order) \ + __pragma(warning(suppress: 4310)) \ + ((sizeof(*(obj)) == 1U) \ + ? atomic_fetch_add_char((atomic_char*)(obj) \ + , (char)(intptr_t)(op), (order)) \ + : ((sizeof(*(obj)) == 2U) \ + ? atomic_fetch_add_short((atomic_short*)(obj) \ + , (short)(intptr_t)(op), (order)) \ + : ((sizeof(*(obj)) == 4U) \ + ? atomic_fetch_add_long((atomic_long*)(obj) \ + , (long)(intptr_t)(op), (order)) \ + : ((sizeof(*(obj)) == 8U) \ + ? atomic_fetch_add_llong((atomic_llong*)(obj) \ + , (__int64)(intptr_t)(op), (order)) \ + : (assert(!"Invalid type"), 0))))) + +#define atomic_fetch_sub_explicit(obj, op, order) \ + __pragma(warning(suppress: 4310)) \ + ((sizeof(*(obj)) == 1U) \ + ? atomic_fetch_sub_char((atomic_char*)(obj) \ + , (char)(intptr_t)(op), (order)) \ + : ((sizeof(*(obj)) == 2U) \ + ? atomic_fetch_sub_short((atomic_short*)(obj) \ + , (short)(intptr_t)(op), (order)) \ + : ((sizeof(*(obj)) == 4U) \ + ? atomic_fetch_sub_long((atomic_long*)(obj) \ + , (long)(intptr_t)(op), (order)) \ + : ((sizeof(*(obj)) == 8U) \ + ? atomic_fetch_sub_llong((atomic_llong*)(obj) \ + , (__int64)(intptr_t)(op), (order)) \ + : (assert(!"Invalid type"), 0))))) + +#define atomic_fetch_or_explicit(obj, op, order) \ + __pragma(warning(suppress: 4310)) \ + ((sizeof(*(obj)) == 1U) \ + ? atomic_fetch_or_char((atomic_char*)(obj) \ + , (char)(intptr_t)(op), (order)) \ + : ((sizeof(*(obj)) == 2U) \ + ? atomic_fetch_or_short((atomic_short*)(obj) \ + , (short)(intptr_t)(op), (order)) \ + : ((sizeof(*(obj)) == 4U) \ + ? atomic_fetch_or_long((atomic_long*)(obj) \ + , (long)(intptr_t)(op), (order)) \ + : ((sizeof(*(obj)) == 8U) \ + ? atomic_fetch_or_llong((atomic_llong*)(obj) \ + , (__int64)(intptr_t)(op), (order)) \ + : (assert(!"Invalid type"), 0))))) + +#define atomic_fetch_xor_explicit(obj, op, order) \ + __pragma(warning(suppress: 4310)) \ + ((sizeof(*(obj)) == 1U) \ + ? atomic_fetch_xor_char((atomic_char*)(obj) \ + , (char)(intptr_t)(op), (order)) \ + : ((sizeof(*(obj)) == 2U) \ + ? atomic_fetch_xor_short((atomic_short*)(obj) \ + , (short)(intptr_t)(op), (order)) \ + : ((sizeof(*(obj)) == 4U) \ + ? atomic_fetch_xor_long((atomic_long*)(obj) \ + , (long)(intptr_t)(op), (order)) \ + : ((sizeof(*(obj)) == 8U) \ + ? atomic_fetch_xor_llong((atomic_llong*)(obj) \ + , (__int64)(intptr_t)(op), (order)) \ + : (assert(!"Invalid type"), 0))))) + +#define atomic_fetch_and_explicit(obj, op, order) \ + __pragma(warning(suppress: 4310)) \ + ((sizeof(*(obj)) == 1U) \ + ? atomic_fetch_and_char((atomic_char*)(obj) \ + , (char)(intptr_t)(op), (order)) \ + : ((sizeof(*(obj)) == 2U) \ + ? atomic_fetch_and_short((atomic_short*)(obj) \ + , (short)(intptr_t)(op), (order)) \ + : ((sizeof(*(obj)) == 4U) \ + ? atomic_fetch_and_long((atomic_long*)(obj) \ + , (long)(intptr_t)(op), (order)) \ + : ((sizeof(*(obj)) == 8U) \ + ? atomic_fetch_and_llong((atomic_llong*)(obj) \ + , (__int64)(intptr_t)(op), (order)) \ + : (assert(!"Invalid type"), 0))))) + +#endif /* (_MSC_VER >= 1928) */ + +/* + * 7.17.8 Atomic flag type and operations + */ + +typedef atomic_bool atomic_flag; + +#define ATOMIC_FLAG_INIT false + +#define atomic_flag_test_and_set(obj) \ + atomic_flag_test_and_set_explicit((obj), memory_order_seq_cst) + +#define atomic_flag_clear(obj) \ + atomic_flag_clear_explicit((obj), memory_order_seq_cst) + +#define atomic_flag_test_and_set_explicit(obj, order) \ + atomic_exchange_bool((obj), true, (order)) + +#define atomic_flag_clear_explicit(obj, order) \ + atomic_store_bool((obj), false, (order)) + +/* + * Microsoft Visual C++ (MSVC) specific operations + * + * Based on code by Helge Bahmann, Tim Blechmann and Andrey Semashev. + * See /boost/atomic/detail/ops_msvc_x86.hpp. + * + * Copyright (c) 2009 Helge Bahmann + * Copyright (c) 2012 Tim Blechmann + * Copyright (c) 2014 Andrey Semashev + * + * Distributed under the Boost Software License, Version 1.0. + * (http://www.boost.org/LICENSE_1_0.txt) + */ + +/* + * atomic_store_explicit + */ + +static __forceinline void +atomic_store_char(volatile atomic_char* obj, char desired + , memory_order order) +{ + if (order == memory_order_seq_cst) + { + _InterlockedExchange8(obj, desired); + } + else + { + _ReadWriteBarrier(); + *obj = desired; + _ReadWriteBarrier(); + } +} + +static __forceinline void +atomic_store_uchar(volatile atomic_uchar* obj, unsigned char desired + , memory_order order) +{ + atomic_store_char((volatile atomic_char*)obj, (char)desired, order); +} + +static __forceinline void +atomic_store_bool(volatile atomic_bool* obj, bool desired + , memory_order order) +{ + atomic_store_char((volatile atomic_char*)obj, !!desired, order); +} + +static __forceinline void +atomic_store_short(volatile atomic_short* obj, short desired + , memory_order order) +{ + if (order == memory_order_seq_cst) + { + _InterlockedExchange16(obj, desired); + } + else + { + _ReadWriteBarrier(); + *obj = desired; + _ReadWriteBarrier(); + } +} + +static __forceinline void +atomic_store_ushort(volatile atomic_ushort* obj, unsigned short desired + , memory_order order) +{ + atomic_store_short((volatile atomic_short*)obj, (short)desired, order); +} + +static __forceinline void +atomic_store_long(volatile atomic_long* obj, long desired + , memory_order order) +{ + if (order == memory_order_seq_cst) + { + _InterlockedExchange(obj, desired); + } + else + { + _ReadWriteBarrier(); + *obj = desired; + _ReadWriteBarrier(); + } +} + +static __forceinline void +atomic_store_ulong(volatile atomic_ulong* obj, unsigned long desired + , memory_order order) +{ + atomic_store_long((volatile atomic_long*)obj, (long)desired, order); +} + +static __forceinline void +atomic_store_int(volatile atomic_int* obj, int desired, memory_order order) +{ + atomic_store_long((volatile atomic_long*)obj, (long)desired, order); +} + +static __forceinline void +atomic_store_uint(volatile atomic_uint* obj, unsigned int desired + , memory_order order) +{ + atomic_store_long((volatile atomic_long*)obj, (long)desired, order); +} + +static __forceinline void +atomic_store_llong(volatile atomic_llong* obj, __int64 desired + , memory_order order) +{ +#if defined(_M_IX86) + (void)order; + _ReadWriteBarrier(); + volatile __int64* p = obj; + if (((unsigned int)p & 0x00000007) == 0) + { +#if defined(_M_IX86_FP) && (_M_IX86_FP >= 2) +#if defined(__AVX__) + __asm + { + mov edx, p + vmovq xmm4, desired + vmovq qword ptr[edx], xmm4 + }; +#else + __asm + { + mov edx, p + movq xmm4, desired + movq qword ptr[edx], xmm4 + }; +#endif /* defined(__AVX__) */ +#else + __asm + { + mov edx, p + fild desired + fistp qword ptr[edx] + }; +#endif /* defined(_M_IX86_FP) ... */ + } + else + { + unsigned int backup = 0; + __asm + { + mov backup, ebx + mov edi, p + mov ebx, dword ptr [desired] + mov ecx, dword ptr [desired + 4] + mov eax, dword ptr [edi] + mov edx, dword ptr [edi + 4] + align 16 + again: + lock cmpxchg8b qword ptr [edi] + jne again + mov ebx, backup + }; + } + _ReadWriteBarrier(); +#elif defined(_M_X64) + if (order == memory_order_seq_cst) + { + _InterlockedExchange64(obj, desired); + } + else + { + _ReadWriteBarrier(); + *obj = desired; + _ReadWriteBarrier(); + } +#endif /* defined(_M_IX86) */ +} + +static __forceinline void +atomic_store_ullong(volatile atomic_ullong* obj, unsigned __int64 desired + , memory_order order) +{ + atomic_store_llong((volatile atomic_llong*)obj, (__int64)desired + , order); +} + +static __forceinline void +atomic_store_ptr(volatile void* obj, void* desired, memory_order order) +{ +#if defined(_M_IX86) + atomic_store_long((volatile atomic_long*)obj, (long)desired, order); +#elif defined(_M_X64) + atomic_store_llong((volatile atomic_llong*)obj, (__int64)desired + , order); +#endif /* defined(_M_IX86) */ +} + +/* + * atomic_load_explicit + */ + +static __forceinline char +atomic_load_char(const volatile atomic_char* obj, memory_order order) +{ + (void)order; + char value = *obj; + _ReadWriteBarrier(); + return value; +} + +static __forceinline unsigned char +atomic_load_uchar(const volatile atomic_uchar* obj, memory_order order) +{ + return atomic_load_char((const volatile atomic_char*)obj, order); +} + +static __forceinline bool +atomic_load_bool(const volatile atomic_bool* obj, memory_order order) +{ + return !!atomic_load_char((const volatile atomic_char*)obj, order); +} + +static __forceinline short +atomic_load_short(const volatile atomic_short* obj, memory_order order) +{ + (void)order; + short value = *obj; + _ReadWriteBarrier(); + return value; +} + +static __forceinline unsigned short +atomic_load_ushort(const volatile atomic_ushort* obj, memory_order order) +{ + return atomic_load_short((const volatile atomic_short*)obj + , order); +} + +static __forceinline long +atomic_load_long(const volatile atomic_long* obj, memory_order order) +{ + (void)order; + int value = *obj; + _ReadWriteBarrier(); + return value; +} + +static __forceinline unsigned long +atomic_load_ulong(const volatile atomic_ulong* obj, memory_order order) +{ + return atomic_load_long((const volatile atomic_long*)obj, order); +} + +static __forceinline int +atomic_load_int(const volatile atomic_int* obj, memory_order order) +{ + return atomic_load_long((const volatile atomic_long*)obj, order); +} + +static __forceinline unsigned int +atomic_load_uint(const volatile atomic_uint* obj, memory_order order) +{ + return atomic_load_long((const volatile atomic_long*)obj, order); +} + +static __forceinline __int64 +atomic_load_llong(const volatile atomic_llong* obj, memory_order order) +{ + (void)order; +#if defined(_M_IX86) + _ReadWriteBarrier(); + const volatile __int64* p = obj; + __int64 value = 0; + if (((unsigned int)p & 0x00000007) == 0) + { +#if defined(_M_IX86_FP) && (_M_IX86_FP >= 2) +#if defined(__AVX__) + __asm + { + mov edx, p + vmovq xmm4, qword ptr[edx] + vmovq value, xmm4 + }; +#else + __asm + { + mov edx, p + movq xmm4, qword ptr[edx] + movq value, xmm4 + }; +#endif /* defined(__AVX__) */ +#else + __asm + { + mov edx, p + fild qword ptr[edx] + fistp value + }; +#endif /* defined(_M_IX86_FP) ... */ + } + else + { + __asm + { + mov edi, p + mov eax, ebx + mov edx, ecx + lock cmpxchg8b qword ptr[edi] + mov dword ptr[value], eax + mov dword ptr[value + 4], edx + }; + } + _ReadWriteBarrier(); +#elif defined(_M_X64) + __int64 value = *obj; + _ReadWriteBarrier(); +#endif /* defined(_M_IX86) */ + return value; +} + +static __forceinline unsigned __int64 +atomic_load_ullong(const volatile atomic_ullong* obj, memory_order order) +{ + return atomic_load_llong((const volatile atomic_llong*)obj, order); +} + +static __forceinline void* +atomic_load_ptr(const volatile void* obj, memory_order order) +{ +#if defined(_M_IX86) + return (void*)atomic_load_long((const volatile atomic_long*)obj + , order); +#elif defined(_M_X64) + return (void*)atomic_load_llong((const volatile atomic_llong*)obj + , order); +#endif /* defined(_M_IX86) */ +} + +/* + * atomic_exchange_explicit + */ + +static __forceinline char +atomic_exchange_char(volatile atomic_char* obj, char desired + , memory_order order) +{ + (void)order; + return _InterlockedExchange8(obj, desired); +} + +static __forceinline unsigned char +atomic_exchange_uchar(volatile atomic_uchar* obj, unsigned char desired + , memory_order order) +{ + return atomic_exchange_char((volatile atomic_char*)obj, (char)desired + , order); +} + +static __forceinline bool +atomic_exchange_bool(volatile atomic_uchar* obj, bool desired + , memory_order order) +{ + return (bool)atomic_exchange_char((volatile atomic_char*)obj + , (char)desired, order); +} + +static __forceinline short +atomic_exchange_short(volatile atomic_short* obj, short desired + , memory_order order) +{ + (void)order; + return _InterlockedExchange16(obj, desired); +} + +static __forceinline unsigned short +atomic_exchange_ushort(volatile atomic_ushort* obj, unsigned short desired + , memory_order order) +{ + return atomic_exchange_short((volatile atomic_short*)obj + , (short)desired, order); +} + +static __forceinline long +atomic_exchange_long(volatile atomic_long* obj, long desired + , memory_order order) +{ + (void)order; + return _InterlockedExchange(obj, desired); +} + +static __forceinline unsigned long +atomic_exchange_ulong(volatile atomic_ulong* obj, unsigned long desired + , memory_order order) +{ + return atomic_exchange_long((volatile atomic_long*)obj + , (long)desired, order); +} + +static __forceinline int +atomic_exchange_int(volatile atomic_int* obj, int desired + , memory_order order) +{ + return atomic_exchange_long((volatile atomic_long*)obj + , (long)desired, order); +} + +static __forceinline unsigned int +atomic_exchange_uint(volatile atomic_uint* obj, unsigned int desired + , memory_order order) +{ + return atomic_exchange_long((volatile atomic_long*)obj + , (long)desired, order); +} + +static __forceinline __int64 +atomic_exchange_llong(volatile atomic_llong* obj, __int64 desired + , memory_order order) +{ + (void)order; +#if defined(_M_IX86) + _ReadWriteBarrier(); + volatile __int64* p = obj; + unsigned int backup = 0; + __asm + { + mov backup, ebx + mov edi, p + mov ebx, dword ptr[desired] + mov ecx, dword ptr[desired + 4] + mov eax, dword ptr[edi] + mov edx, dword ptr[edi + 4] + align 16 + again: + lock cmpxchg8b qword ptr[edi] + jne again + mov ebx, backup + mov dword ptr[desired], eax + mov dword ptr[desired + 4], edx + }; + _ReadWriteBarrier(); + return desired; +#elif defined(_M_X64) + return _InterlockedExchange64(obj, desired); +#endif /* defined(_M_IX86) */ +} + +static __forceinline unsigned __int64 +atomic_exchange_ullong(volatile atomic_ullong* obj + , unsigned __int64 desired, memory_order order) +{ + return atomic_exchange_llong((volatile atomic_llong*)obj + , (__int64)desired, order); +} + +static __forceinline void* +atomic_exchange_ptr(volatile void* obj, void* desired, memory_order order) +{ +#if defined(_M_IX86) + return (void*)atomic_exchange_long((volatile atomic_long*)obj + , (long)desired, order); +#elif defined(_M_X64) + return (void*)atomic_exchange_llong((volatile atomic_llong*)obj + , (__int64)desired, order); +#endif /* defined(_M_IX86) */ +} + +/* + * atomic_compare_exchange_strong_explicit + */ + +static __forceinline bool +atomic_compare_exchange_char(volatile atomic_char* obj, char* expected + , char desired, memory_order success, memory_order failure) +{ + (void)success; + (void)failure; + char old = *expected; + *expected = _InterlockedCompareExchange8(obj, desired, old); + return (old == *expected); +} + +static __forceinline bool +atomic_compare_exchange_uchar(volatile atomic_uchar* obj + , unsigned char* expected, unsigned char desired, memory_order success + , memory_order failure) +{ + return atomic_compare_exchange_char((volatile atomic_char*)obj + , (char*)expected, (char)desired, success, failure); +} + +static __forceinline bool +atomic_compare_exchange_short(volatile atomic_short* obj, short* expected + , short desired, memory_order success, memory_order failure) +{ + (void)success; + (void)failure; + short old = *expected; + *expected = _InterlockedCompareExchange16(obj, desired, old); + return (old == *expected); +} + +static __forceinline bool +atomic_compare_exchange_ushort(volatile atomic_ushort* obj + , unsigned short* expected, unsigned short desired + , memory_order success, memory_order failure) +{ + return atomic_compare_exchange_short((volatile atomic_short*)obj + , (short*)expected, (short)desired, success, failure); +} + +static __forceinline bool +atomic_compare_exchange_long(volatile atomic_long* obj, long* expected + , long desired, memory_order success, memory_order failure) +{ + (void)success; + (void)failure; + long old = *expected; + *expected = _InterlockedCompareExchange(obj, desired, old); + return (old == *expected); +} + +static __forceinline bool +atomic_compare_exchange_ulong(volatile atomic_ulong* obj + , unsigned long* expected, unsigned long desired, memory_order success + , memory_order failure) +{ + return atomic_compare_exchange_long((volatile atomic_long*)obj + , (long*)expected, (long)desired, success, failure); +} + +static __forceinline bool +atomic_compare_exchange_int(volatile atomic_int* obj, int* expected + , int desired, memory_order success, memory_order failure) +{ + return atomic_compare_exchange_long((volatile atomic_long*)obj + , (long*)expected, (long)desired, success, failure); +} + +static __forceinline bool +atomic_compare_exchange_uint(volatile atomic_uint* obj + , unsigned int* expected, unsigned int desired, memory_order success + , memory_order failure) +{ + return atomic_compare_exchange_long((volatile atomic_long*)obj + , (long*)expected, (long)desired, success, failure); +} + +static __forceinline bool +atomic_compare_exchange_llong(volatile atomic_llong* obj + , __int64* expected, __int64 desired, memory_order success + , memory_order failure) +{ + (void)success; + (void)failure; + __int64 old = *expected; + *expected = _InterlockedCompareExchange64(obj, desired, old); + return (old == *expected); +} + +static __forceinline bool +atomic_compare_exchange_ullong(volatile atomic_ullong* obj + , unsigned __int64* expected, unsigned __int64 desired + , memory_order success, memory_order failure) +{ + return atomic_compare_exchange_llong((volatile atomic_llong*)obj + , (__int64*)expected, (__int64)desired, success, failure); +} + +static __forceinline bool +atomic_compare_exchange_ptr(volatile void* obj, void* expected + , void* desired, memory_order success, memory_order failure) +{ +#if defined(_M_IX86) + return atomic_compare_exchange_long((volatile atomic_long*)obj + , (long*)expected, (long)desired, success, failure); +#elif defined(_M_X64) + return atomic_compare_exchange_llong((volatile atomic_llong*)obj + , (__int64*)expected, (__int64)desired, success, failure); +#endif /* defined(_M_IX86) */ +} + +/* + * atomic_fetch_add_explicit + */ + +static __forceinline char +atomic_fetch_add_char(volatile atomic_char* obj, char op + , memory_order order) +{ + (void)order; + return _InterlockedExchangeAdd8(obj, op); +} + +static __forceinline unsigned char +atomic_fetch_add_uchar(volatile atomic_uchar* obj, unsigned char op +, memory_order order) +{ + return atomic_fetch_add_char((volatile atomic_char*)obj, (char)op + , order); +} + +static __forceinline short +atomic_fetch_add_short(volatile atomic_short* obj, short op + , memory_order order) +{ + (void)order; + return _InterlockedExchangeAdd16(obj, op); +} + +static __forceinline unsigned short +atomic_fetch_add_ushort(volatile atomic_ushort* obj, unsigned short op + , memory_order order) +{ + return atomic_fetch_add_short((volatile atomic_short*)obj, (short)op + , order); +} + +static __forceinline long +atomic_fetch_add_long(volatile atomic_long* obj, long op + , memory_order order) +{ + (void)order; + return _InterlockedExchangeAdd(obj, op); +} + +static __forceinline unsigned long +atomic_fetch_add_ulong(volatile atomic_ulong* obj, unsigned long op + , memory_order order) +{ + return atomic_fetch_add_long((volatile atomic_long*)obj, (long)op + , order); +} + +static __forceinline int +atomic_fetch_add_int(volatile atomic_int* obj, int op, memory_order order) +{ + return atomic_fetch_add_long((volatile atomic_long*)obj, (long)op + , order); +} + +static __forceinline unsigned int +atomic_fetch_add_uint(volatile atomic_uint* obj, unsigned int op + , memory_order order) +{ + return atomic_fetch_add_long((volatile atomic_long*)obj, (long)op + , order); +} + +static __forceinline __int64 +atomic_fetch_add_llong(volatile atomic_llong* obj, __int64 op + , memory_order order) +{ + (void)order; +#if defined(_M_IX86) + __int64 old = *obj; + while (!atomic_compare_exchange_weak(obj, &old, old + op)) {} + return old; +#elif defined(_M_X64) + return _InterlockedExchangeAdd64(obj, op); +#endif /* defined(_M_IX86) */ +} + +static __forceinline unsigned __int64 +atomic_fetch_add_ullong(volatile atomic_ullong* obj, unsigned __int64 op + , memory_order order) +{ + return atomic_fetch_add_llong((volatile atomic_llong*)obj, (__int64)op + , order); +} + +/* + * atomic_fetch_sub_explicit + */ + +static __forceinline char +atomic_fetch_sub_char(volatile atomic_char* obj, char op + , memory_order order) +{ + (void)order; + return _InterlockedExchangeAdd8(obj, -op); +} + +static __forceinline unsigned char +atomic_fetch_sub_uchar(volatile atomic_uchar* obj, unsigned char op + , memory_order order) +{ + return atomic_fetch_sub_char((volatile atomic_char*)obj, (char)op + , order); +} + +static __forceinline short +atomic_fetch_sub_short(volatile atomic_short* obj, short op + , memory_order order) +{ + (void)order; + return _InterlockedExchangeAdd16(obj, -op); +} + +static __forceinline unsigned short +atomic_fetch_sub_ushort(volatile atomic_ushort* obj, unsigned short op + , memory_order order) +{ + return atomic_fetch_sub_short((volatile atomic_short*)obj, (short)op + , order); +} + +static __forceinline long +atomic_fetch_sub_long(volatile atomic_long* obj, long op + , memory_order order) +{ + (void)order; + return _InterlockedExchangeAdd(obj, -op); +} + +static __forceinline unsigned long +atomic_fetch_sub_ulong(volatile atomic_ulong* obj, unsigned long op + , memory_order order) +{ + return atomic_fetch_sub_long((volatile atomic_long*)obj, (long)op + , order); +} + +static __forceinline int +atomic_fetch_sub_int(volatile atomic_int* obj, int op, memory_order order) +{ + return atomic_fetch_sub_long((volatile atomic_long*)obj, (long)op + , order); +} + +static __forceinline unsigned int +atomic_fetch_sub_uint(volatile atomic_uint* obj, unsigned int op + , memory_order order) +{ + return atomic_fetch_sub_long((volatile atomic_long*)obj, (long)op + , order); +} + +static __forceinline __int64 +atomic_fetch_sub_llong(volatile atomic_llong* obj, __int64 op + , memory_order order) +{ + (void)order; +#if defined(_M_IX86) + __int64 old = *obj; + while (!atomic_compare_exchange_weak(obj, &old, old - op)) {} + return old; +#elif defined(_M_X64) + return _InterlockedExchangeAdd64(obj, -op); +#endif /* defined(_M_IX86) */ +} + +static __forceinline unsigned __int64 +atomic_fetch_sub_ullong(volatile atomic_ullong* obj, unsigned __int64 op + , memory_order order) +{ + return atomic_fetch_sub_llong((volatile atomic_llong*)obj, (__int64)op + , order); +} + +/* + * atomic_fetch_or_explicit + */ + +static __forceinline char +atomic_fetch_or_char(volatile atomic_char* obj, char op + , memory_order order) +{ + (void)order; + return _InterlockedOr8(obj, op); +} + +static __forceinline unsigned char +atomic_fetch_or_uchar(volatile atomic_uchar* obj, unsigned char op + , memory_order order) +{ + return atomic_fetch_or_char((volatile atomic_char*)obj, (char)op + , order); +} + +static __forceinline short +atomic_fetch_or_short(volatile atomic_short* obj, short op + , memory_order order) +{ + (void)order; + return _InterlockedOr16(obj, op); +} + +static __forceinline unsigned short +atomic_fetch_or_ushort(volatile atomic_ushort* obj, unsigned short op + , memory_order order) +{ + return atomic_fetch_or_short((volatile atomic_short*)obj, (short)op + , order); +} + +static __forceinline long +atomic_fetch_or_long(volatile atomic_long* obj, long op + , memory_order order) +{ + (void)order; + return _InterlockedOr(obj, op); +} + +static __forceinline unsigned long +atomic_fetch_or_ulong(volatile atomic_ulong* obj, unsigned long op + , memory_order order) +{ + return atomic_fetch_or_long((volatile atomic_long*)obj, (long)op + , order); +} + +static __forceinline int +atomic_fetch_or_int(volatile atomic_int* obj, int op, memory_order order) +{ + return atomic_fetch_or_long((volatile atomic_long*)obj, (long)op + , order); +} + +static __forceinline unsigned int +atomic_fetch_or_uint(volatile atomic_uint* obj, unsigned int op + , memory_order order) +{ + return atomic_fetch_or_long((volatile atomic_long*)obj, (long)op + , order); +} + +static __forceinline __int64 +atomic_fetch_or_llong(volatile atomic_llong* obj, __int64 op + , memory_order order) +{ + (void)order; +#if defined(_M_IX86) + __int64 old = *obj; + while (!atomic_compare_exchange_weak(obj, &old, old | op)) {} + return old; +#elif defined(_M_X64) + return _InterlockedOr64(obj, op); +#endif /* defined(_M_IX86) */ +} + +static __forceinline unsigned __int64 +atomic_fetch_or_ullong(volatile atomic_ullong* obj, unsigned __int64 op + , memory_order order) +{ + return atomic_fetch_or_llong((volatile atomic_llong*)obj, (__int64)op + , order); +} + +/* + * atomic_fetch_xor_explicit + */ + +static __forceinline char +atomic_fetch_xor_char(volatile atomic_char* obj, char op + , memory_order order) +{ + (void)order; + return _InterlockedXor8(obj, op); +} + +static __forceinline unsigned char +atomic_fetch_xor_uchar(volatile atomic_uchar* obj, unsigned char op + , memory_order order) +{ + return atomic_fetch_xor_char((volatile atomic_char*)obj, (char)op + , order); +} + +static __forceinline short +atomic_fetch_xor_short(volatile atomic_short* obj, short op + , memory_order order) +{ + (void)order; + return _InterlockedXor16(obj, op); +} + +static __forceinline unsigned short +atomic_fetch_xor_ushort(volatile atomic_ushort* obj, unsigned short op + , memory_order order) +{ + return atomic_fetch_xor_short((volatile atomic_short*)obj, (short)op + , order); +} + +static __forceinline long +atomic_fetch_xor_long(volatile atomic_long* obj, long op + , memory_order order) +{ + (void)order; + return _InterlockedXor(obj, op); +} + +static __forceinline unsigned long +atomic_fetch_xor_ulong(volatile atomic_ulong* obj, unsigned long op + , memory_order order) +{ + return atomic_fetch_xor_long((volatile atomic_long*)obj, (long)op + , order); +} + +static __forceinline int +atomic_fetch_xor_int(volatile atomic_int* obj, int op, memory_order order) +{ + return atomic_fetch_xor_long((volatile atomic_long*)obj, (long)op + , order); +} + +static __forceinline unsigned int +atomic_fetch_xor_uint(volatile atomic_uint* obj, unsigned int op + , memory_order order) +{ + return atomic_fetch_xor_long((volatile atomic_long*)obj, (long)op + , order); +} + +static __forceinline __int64 +atomic_fetch_xor_llong(volatile atomic_llong* obj, __int64 op + , memory_order order) +{ + (void)order; +#if defined(_M_IX86) + __int64 old = *obj; + while (!atomic_compare_exchange_weak(obj, &old, old ^ op)) {} + return old; +#elif defined(_M_X64) + return _InterlockedXor64(obj, op); +#endif /* defined(_M_IX86) */ +} + +static __forceinline unsigned __int64 +atomic_fetch_xor_ullong(volatile atomic_ullong* obj, unsigned __int64 op + , memory_order order) +{ + return atomic_fetch_xor_llong((volatile atomic_llong*)obj, (__int64)op + , order); +} + +/* + * atomic_fetch_and_explicit + */ + +static __forceinline char +atomic_fetch_and_char(volatile atomic_char* obj, char op + , memory_order order) +{ + (void)order; + return _InterlockedAnd8(obj, op); +} + +static __forceinline unsigned char +atomic_fetch_and_uchar(volatile atomic_uchar* obj, unsigned char op + , memory_order order) +{ + return atomic_fetch_and_char((volatile atomic_char*)obj, (char)op + , order); +} + +static __forceinline short +atomic_fetch_and_short(volatile atomic_short* obj, short op + , memory_order order) +{ + (void)order; + return _InterlockedAnd16(obj, op); +} + +static __forceinline unsigned short +atomic_fetch_and_ushort(volatile atomic_ushort* obj, unsigned short op + , memory_order order) +{ + return atomic_fetch_and_short((volatile atomic_short*)obj, (short)op + , order); +} + +static __forceinline long +atomic_fetch_and_long(volatile atomic_long* obj, long op + , memory_order order) +{ + (void)order; + return _InterlockedAnd(obj, op); +} + +static __forceinline unsigned long +atomic_fetch_and_ulong(volatile atomic_ulong* obj, unsigned long op + , memory_order order) +{ + return atomic_fetch_and_long((volatile atomic_long*)obj, (long)op + , order); +} + +static __forceinline int +atomic_fetch_and_int(volatile atomic_int* obj, int op, memory_order order) +{ + return atomic_fetch_and_long((volatile atomic_long*)obj, (long)op + , order); +} + +static __forceinline unsigned int +atomic_fetch_and_uint(volatile atomic_uint* obj, unsigned int op + , memory_order order) +{ + return atomic_fetch_and_long((volatile atomic_long*)obj, (long)op + , order); +} + +static __forceinline __int64 +atomic_fetch_and_llong(volatile atomic_llong* obj, __int64 op + , memory_order order) +{ + (void)order; +#if defined(_M_IX86) + __int64 old = *obj; + while (!atomic_compare_exchange_weak(obj, &old, old & op)) {} + return old; +#elif defined(_M_X64) + return _InterlockedAnd64(obj, op); +#endif /* defined(_M_IX86) */ +} + +static __forceinline unsigned __int64 +atomic_fetch_and_ullong(volatile atomic_ullong* obj, unsigned __int64 op + , memory_order order) +{ + return atomic_fetch_and_llong((volatile atomic_llong*)obj, (__int64)op + , order); +} + +#endif /* defined(USE_TEMPORARY_MSVC_WORKAROUND) */ + +#undef USE_TEMPORARY_MSVC_WORKAROUND + +#endif /* __STDATOMIC_H__ */ \ No newline at end of file