diff --git a/.gitignore b/.gitignore
index f9f032e..1c88493 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,7 @@
*.pid
*.o
*.so
+*.a
*.lib
*.tar.gz
*.log
diff --git a/.gitmodules b/.gitmodules
index 6cc4605..044283d 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -13,3 +13,6 @@
[submodule "framework/3rd/moon"]
path = framework/3rd/moon
url = https://github.com/siffiejoe/lua-moon.git
+[submodule "framework/3rd/termbox"]
+ path = framework/3rd/termbox
+ url = https://github.com/nullgemm/termbox_next.git
diff --git a/framework/3rd/dmon/.clang-format b/framework/3rd/dmon/.clang-format
deleted file mode 100755
index 61a73e2..0000000
--- a/framework/3rd/dmon/.clang-format
+++ /dev/null
@@ -1,38 +0,0 @@
----
-AlignAfterOpenBracket: Align
-AlignConsecutiveAssignments: 'false'
-AlignConsecutiveDeclarations: 'false'
-AlignEscapedNewlines: Left
-AlignTrailingComments: 'true'
-AllowAllParametersOfDeclarationOnNextLine: 'true'
-AllowShortBlocksOnASingleLine: 'false'
-AllowShortCaseLabelsOnASingleLine: 'false'
-AllowShortFunctionsOnASingleLine: 'false'
-AllowShortIfStatementsOnASingleLine: 'false'
-AllowShortLoopsOnASingleLine: 'false'
-AlwaysBreakBeforeMultilineStrings: 'true'
-AlwaysBreakTemplateDeclarations: 'true'
-BreakBeforeBinaryOperators: None
-BreakBeforeBraces: Linux
-Cpp11BracedListStyle: 'false'
-IncludeBlocks: Preserve
-IndentCaseLabels: 'false'
-IndentPPDirectives: AfterHash
-IndentWidth: '4'
-IndentWrappedFunctionNames: 'true'
-Language: Cpp
-MaxEmptyLinesToKeep: '2'
-NamespaceIndentation: Inner
-PointerAlignment: Left
-SpaceBeforeAssignmentOperators: 'true'
-SpaceBeforeParens: ControlStatements
-SpaceInEmptyParentheses: 'false'
-SpacesBeforeTrailingComments: '4'
-SpacesInCStyleCastParentheses: 'false'
-SpacesInContainerLiterals: 'false'
-SpacesInParentheses: 'false'
-Standard: Cpp11
-TabWidth: '4'
-UseTab: Never
-
-...
diff --git a/framework/3rd/dmon/.gitignore b/framework/3rd/dmon/.gitignore
deleted file mode 100755
index 0308f50..0000000
--- a/framework/3rd/dmon/.gitignore
+++ /dev/null
@@ -1,72 +0,0 @@
-# External junk
-.DS_Store
-_ReSharper*
-*.opensdf
-*.sdf
-*.dir
-*.suo
-*.user
-*.bak
-Win32
-Win64
-Debug
-Release
-Profile
-Development
-Obj
-Bin
-Lib
-.tags
-.tags_sorted_by_file
-*.lnk
-ipch
-__pycache__
-Thumbs.db
-.build*
-
-# Gradle
-.gradle
-/local.properties
-/.idea/workspace.xml
-/.idea/libraries
-/build
-/build-*
-/captures
-
-# assets
-assets/shaders/*
-
-# Generated files
-bin
-docs/Doxyfile
-docs/html
-docs/warnings.txt
-imgui.ini
-install
-*.log
-.vscode/launch.json
-.vscode/c_cpp_properties.json
-shaders_h
-src/rizz/plugin_bundle.h
-src/rizz/plugin_bundle_native.h
-android
-assets-db.json
-compile_commands.json
-examples/assets/shaders/**
-
-# temp
-scripts/generators/fake_libc_include
-temp
-
-# Compiled binaries
-*.so
-*.dylib
-*.dll
-*.a
-*.lib
-*.exe
-.build
-*.fso
-*.vso
-*.pyc
-*.obj
diff --git a/framework/3rd/dmon/README.md b/framework/3rd/dmon/README.md
deleted file mode 100755
index 55465de..0000000
--- a/framework/3rd/dmon/README.md
+++ /dev/null
@@ -1,75 +0,0 @@
-## dmon
-[@septag](https://twitter.com/septagh)
-
-_dmon_ is a tiny C library that monitors changes in a directory.
-It provides a unified solution to multiple system APIs that exist for each OS. It can also monitor directories recursively.
-
-### Platforms
-- Windows: `ReadDirectoryChangesW` backend. Tested with Windows10 SDK + Visual Studio 2019
-- Linux: `inotify` backend. Tested with gcc-7.4/clang-6, ubuntu 18.04 LTS
-- MacOS: `FSEvents` backend. Tested with MacOS-10.14 clang 10
-
-### Usage
-
-You just have to include the file and use it's functions. It is also compatible with C++ code.
-Backslashes in Windows paths are also converted to '/' for portability.
-
-```c
-#define DMON_IMPL
-#include "dmon.h"
-
-static void watch_callback(dmon_watch_id watch_id, dmon_action action, const char* rootdir,
- const char* filepath, const char* oldfilepath, void* user)
-{
- // receive change events. type of event is stored in 'action' variable
-}
-
-int main()
-{
- dmon_init();
- dmon_watch("/path/to/directory", watch_callback, DMON_WATCHFLAGS_RECURSIVE, NULL);
- // wait ...
- dmon_deinit();
- return 0;
-}
-```
-
-For more information and how to customize functionality, see [dmon.h](dmon.h)
-
-To build on linux, link with `pthread`:
-```gcc test.c -lpthread -o test```
-
-To build on MacOS, link with `CoreServices` and `CoreFoundation`:
-```clang test.c -framework CoreFoundation -framework CoreServices -lpthread -o test```
-
-[License (BSD 2-clause)](https://github.com/septag/dmon/blob/master/LICENSE)
---------------------------------------------------------------------------
-
-
-
-
-
- Copyright 2019 Sepehr Taghdisian. All rights reserved.
-
- https://github.com/septag/dmon
-
- 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 COPYRIGHT HOLDER ``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 COPYRIGHT HOLDER 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.
diff --git a/framework/3rd/dmon/dmon.h b/framework/3rd/dmon/dmon.h
deleted file mode 100755
index 54928bc..0000000
--- a/framework/3rd/dmon/dmon.h
+++ /dev/null
@@ -1,1502 +0,0 @@
-//
-// Copyright 2021 Sepehr Taghdisian (septag@github). All rights reserved.
-// License: https://github.com/septag/dmon#license-bsd-2-clause
-//
-// Portable directory monitoring library
-// watches directories for file or directory changes.
-//
-// clang-format off
-// Usage:
-// define DMON_IMPL and include this file to use it:
-// #define DMON_IMPL
-// #include "dmon.h"
-//
-// dmon_init():
-// Call this once at the start of your program.
-// This will start a low-priority monitoring thread
-// dmon_deinit():
-// Call this when your work with dmon is finished, usually on program terminate
-// This will free resources and stop the monitoring thread
-// dmon_watch:
-// Watch for directories
-// You can watch multiple directories by calling this function multiple times
-// rootdir: root directory to monitor
-// watch_cb: callback function to receive events.
-// NOTE that this function is called from another thread, so you should
-// beware of data races in your application when accessing data within this
-// callback
-// flags: watch flags, see dmon_watch_flags_t
-// user_data: user pointer that is passed to callback function
-// Returns the Id of the watched directory after successful call, or returns Id=0 if error
-// dmon_unwatch:
-// Remove the directory from watch list
-//
-// see test.c for the basic example
-//
-// Configuration:
-// You can customize some low-level functionality like malloc and logging by overriding macros:
-//
-// DMON_MALLOC, DMON_FREE, DMON_REALLOC:
-// define these macros to override memory allocations
-// default is 'malloc', 'free' and 'realloc'
-// DMON_ASSERT:
-// define this to provide your own assert
-// default is 'assert'
-// DMON_LOG_ERROR:
-// define this to provide your own logging mechanism
-// default implementation logs to stdout and breaks the program
-// DMON_LOG_DEBUG
-// define this to provide your own extra debug logging mechanism
-// default implementation logs to stdout in DEBUG and does nothing in other builds
-// DMON_API_DECL, DMON_API_IMPL
-// define these to provide your own API declerations. (for example: static)
-// default is nothing (which is extern in C language )
-// DMON_MAX_PATH
-// Maximum size of path characters
-// default is 260 characters
-// DMON_MAX_WATCHES
-// Maximum number of watch directories
-// default is 64
-//
-// TODO:
-// - DMON_WATCHFLAGS_FOLLOW_SYMLINKS does not resolve files
-// - implement DMON_WATCHFLAGS_OUTOFSCOPE_LINKS
-// - implement DMON_WATCHFLAGS_IGNORE_DIRECTORIES
-//
-// History:
-// 1.0.0 First version. working Win32/Linux backends
-// 1.1.0 MacOS backend
-// 1.1.1 Minor fixes, eliminate gcc/clang warnings with -Wall
-// 1.1.2 Eliminate some win32 dead code
-// 1.1.3 Fixed select not resetting causing high cpu usage on linux
-//
-#ifndef __DMON_H__
-#define __DMON_H__
-
-#include
-#include
-
-#ifndef DMON_API_DECL
-# define DMON_API_DECL
-#endif
-
-#ifndef DMON_API_IMPL
-# define DMON_API_IMPL
-#endif
-
-typedef struct { uint32_t id; } dmon_watch_id;
-
-// Pass these flags to `dmon_watch`
-typedef enum dmon_watch_flags_t {
- DMON_WATCHFLAGS_RECURSIVE = 0x1, // monitor all child directories
- DMON_WATCHFLAGS_FOLLOW_SYMLINKS = 0x2, // resolve symlinks (linux only)
- DMON_WATCHFLAGS_OUTOFSCOPE_LINKS = 0x4, // TODO: not implemented yet
- DMON_WATCHFLAGS_IGNORE_DIRECTORIES = 0x8 // TODO: not implemented yet
-} dmon_watch_flags;
-
-// Action is what operation performed on the file. this value is provided by watch callback
-typedef enum dmon_action_t {
- DMON_ACTION_CREATE = 1,
- DMON_ACTION_DELETE,
- DMON_ACTION_MODIFY,
- DMON_ACTION_MOVE
-} dmon_action;
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-DMON_API_DECL void dmon_init(void);
-DMON_API_DECL void dmon_deinit(void);
-
-DMON_API_DECL dmon_watch_id dmon_watch(const char* rootdir,
- void (*watch_cb)(dmon_watch_id watch_id, dmon_action action,
- const char* rootdir, const char* filepath,
- const char* oldfilepath, void* user),
- uint32_t flags, void* user_data);
-DMON_API_DECL void dmon_unwatch(dmon_watch_id id);
-
-#ifdef __cplusplus
-}
-#endif
-
-#ifdef DMON_IMPL
-
-#define DMON_OS_WINDOWS 0
-#define DMON_OS_MACOS 0
-#define DMON_OS_LINUX 0
-
-#if defined(_WIN32) || defined(_WIN64)
-# undef DMON_OS_WINDOWS
-# define DMON_OS_WINDOWS 1
-#elif defined(__linux__)
-# undef DMON_OS_LINUX
-# define DMON_OS_LINUX 1
-#elif defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__)
-# undef DMON_OS_MACOS
-# define DMON_OS_MACOS __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__
-#else
-# define DMON_OS 0
-# error "unsupported platform"
-#endif
-
-#if DMON_OS_WINDOWS
-# ifndef WIN32_LEAN_AND_MEAN
-# define WIN32_LEAN_AND_MEAN
-# endif
-# ifndef NOMINMAX
-# define NOMINMAX
-# endif
-# include
-# include
-# ifdef _MSC_VER
-# pragma intrinsic(_InterlockedExchange)
-# endif
-#elif DMON_OS_LINUX
-# ifndef __USE_MISC
-# define __USE_MISC
-# endif
-# include
-# include
-# include
-# include
-# include
-# include
-# include
-# include
-# include
-# include
-# include
-#elif DMON_OS_MACOS
-# include
-# include
-# include
-# include
-# include
-#endif
-
-#ifndef DMON_MALLOC
-# include
-# define DMON_MALLOC(size) malloc(size)
-# define DMON_FREE(ptr) free(ptr)
-# define DMON_REALLOC(ptr, size) realloc(ptr, size)
-#endif
-
-#ifndef DMON_ASSERT
-# include
-# define DMON_ASSERT(e) assert(e)
-#endif
-
-#ifndef DMON_LOG_ERROR
-# include
-# define DMON_LOG_ERROR(s) do { puts(s); DMON_ASSERT(0); } while(0)
-#endif
-
-#ifndef DMON_LOG_DEBUG
-# ifndef NDEBUG
-# include
-# define DMON_LOG_DEBUG(s) do { puts(s); } while(0)
-# else
-# define DMON_LOG_DEBUG(s)
-# endif
-#endif
-
-#ifndef DMON_MAX_WATCHES
-# define DMON_MAX_WATCHES 64
-#endif
-
-#ifndef DMON_MAX_PATH
-# define DMON_MAX_PATH 260
-#endif
-
-#define _DMON_UNUSED(x) (void)(x)
-
-#ifndef _DMON_PRIVATE
-# if defined(__GNUC__) || defined(__clang__)
-# define _DMON_PRIVATE __attribute__((unused)) static
-# else
-# define _DMON_PRIVATE static
-# endif
-#endif
-
-#include
-
-#ifndef _DMON_LOG_ERRORF
-# define _DMON_LOG_ERRORF(str, ...) do { char msg[512]; snprintf(msg, sizeof(msg), str, __VA_ARGS__); DMON_LOG_ERROR(msg); } while(0);
-#endif
-
-#ifndef _DMON_LOG_DEBUGF
-# define _DMON_LOG_DEBUGF(str, ...) do { char msg[512]; snprintf(msg, sizeof(msg), str, __VA_ARGS__); DMON_LOG_DEBUG(msg); } while(0);
-#endif
-
-#ifndef dmon__min
-# define dmon__min(a, b) ((a) < (b) ? (a) : (b))
-#endif
-
-#ifndef dmon__max
-# define dmon__max(a, b) ((a) > (b) ? (a) : (b))
-#endif
-
-#ifndef dmon__swap
-# define dmon__swap(a, b, _type) \
- do { \
- _type tmp = a; \
- a = b; \
- b = tmp; \
- } while (0)
-#endif
-
-#ifndef dmon__make_id
-# ifdef __cplusplus
-# define dmon__make_id(id) {id}
-# else
-# define dmon__make_id(id) (dmon_watch_id) {id}
-# endif
-#endif // dmon__make_id
-
-_DMON_PRIVATE bool dmon__isrange(char ch, char from, char to)
-{
- return (uint8_t)(ch - from) <= (uint8_t)(to - from);
-}
-
-_DMON_PRIVATE bool dmon__isupperchar(char ch)
-{
- return dmon__isrange(ch, 'A', 'Z');
-}
-
-_DMON_PRIVATE char dmon__tolowerchar(char ch)
-{
- return ch + (dmon__isupperchar(ch) ? 0x20 : 0);
-}
-
-_DMON_PRIVATE char* dmon__tolower(char* dst, int dst_sz, const char* str)
-{
- int offset = 0;
- int dst_max = dst_sz - 1;
- while (*str && offset < dst_max) {
- dst[offset++] = dmon__tolowerchar(*str);
- ++str;
- }
- dst[offset] = '\0';
- return dst;
-}
-
-_DMON_PRIVATE char* dmon__strcpy(char* dst, int dst_sz, const char* src)
-{
- DMON_ASSERT(dst);
- DMON_ASSERT(src);
-
- const int32_t len = (int32_t)strlen(src);
- const int32_t _max = dst_sz - 1;
- const int32_t num = (len < _max ? len : _max);
- memcpy(dst, src, num);
- dst[num] = '\0';
-
- return dst;
-}
-
-_DMON_PRIVATE char* dmon__unixpath(char* dst, int size, const char* path)
-{
- size_t len = strlen(path);
- len = dmon__min(len, (size_t)size - 1);
-
- for (size_t i = 0; i < len; i++) {
- if (path[i] != '\\')
- dst[i] = path[i];
- else
- dst[i] = '/';
- }
- dst[len] = '\0';
- return dst;
-}
-
-#if DMON_OS_LINUX || DMON_OS_MACOS
-_DMON_PRIVATE char* dmon__strcat(char* dst, int dst_sz, const char* src)
-{
- int len = (int)strlen(dst);
- return dmon__strcpy(dst + len, dst_sz - len, src);
-}
-#endif // DMON_OS_LINUX || DMON_OS_MACOS
-
-// stretchy buffer: https://github.com/nothings/stb/blob/master/stretchy_buffer.h
-#define stb_sb_free(a) ((a) ? DMON_FREE(stb__sbraw(a)),0 : 0)
-#define stb_sb_push(a,v) (stb__sbmaybegrow(a,1), (a)[stb__sbn(a)++] = (v))
-#define stb_sb_count(a) ((a) ? stb__sbn(a) : 0)
-#define stb_sb_add(a,n) (stb__sbmaybegrow(a,n), stb__sbn(a)+=(n), &(a)[stb__sbn(a)-(n)])
-#define stb_sb_last(a) ((a)[stb__sbn(a)-1])
-#define stb_sb_reset(a) ((a) ? (stb__sbn(a) = 0) : 0)
-
-#define stb__sbraw(a) ((int *) (a) - 2)
-#define stb__sbm(a) stb__sbraw(a)[0]
-#define stb__sbn(a) stb__sbraw(a)[1]
-
-#define stb__sbneedgrow(a,n) ((a)==0 || stb__sbn(a)+(n) >= stb__sbm(a))
-#define stb__sbmaybegrow(a,n) (stb__sbneedgrow(a,(n)) ? stb__sbgrow(a,n) : 0)
-#define stb__sbgrow(a,n) (*((void **)&(a)) = stb__sbgrowf((a), (n), sizeof(*(a))))
-
-static void * stb__sbgrowf(void *arr, int increment, int itemsize)
-{
- int dbl_cur = arr ? 2*stb__sbm(arr) : 0;
- int min_needed = stb_sb_count(arr) + increment;
- int m = dbl_cur > min_needed ? dbl_cur : min_needed;
- int *p = (int *) DMON_REALLOC(arr ? stb__sbraw(arr) : 0, itemsize * m + sizeof(int)*2);
- if (p) {
- if (!arr)
- p[1] = 0;
- p[0] = m;
- return p+2;
- } else {
- return (void *) (2*sizeof(int)); // try to force a NULL pointer exception later
- }
-}
-
-// watcher callback (same as dmon.h's decleration)
-typedef void (dmon__watch_cb)(dmon_watch_id, dmon_action, const char*, const char*, const char*, void*);
-
-#if DMON_OS_WINDOWS
-// IOCP (windows)
-#ifdef UNICODE
-# define _DMON_WINAPI_STR(name, size) wchar_t _##name[size]; MultiByteToWideChar(CP_UTF8, 0, name, -1, _##name, size)
-#else
-# define _DMON_WINAPI_STR(name, size) const char* _##name = name
-#endif
-
-typedef struct dmon__win32_event {
- char filepath[DMON_MAX_PATH];
- DWORD action;
- dmon_watch_id watch_id;
- bool skip;
-} dmon__win32_event;
-
-typedef struct dmon__watch_state {
- dmon_watch_id id;
- OVERLAPPED overlapped;
- HANDLE dir_handle;
- uint8_t buffer[64512]; // http://msdn.microsoft.com/en-us/library/windows/desktop/aa365465(v=vs.85).aspx
- DWORD notify_filter;
- dmon__watch_cb* watch_cb;
- uint32_t watch_flags;
- void* user_data;
- char rootdir[DMON_MAX_PATH];
- char old_filepath[DMON_MAX_PATH];
-} dmon__watch_state;
-
-typedef struct dmon__state {
- int num_watches;
- dmon__watch_state watches[DMON_MAX_WATCHES];
- HANDLE thread_handle;
- CRITICAL_SECTION mutex;
- volatile LONG modify_watches;
- dmon__win32_event* events;
- bool quit;
-} dmon__state;
-
-static bool _dmon_init;
-static dmon__state _dmon;
-
-// clang-format on
-
-_DMON_PRIVATE bool dmon__refresh_watch(dmon__watch_state* watch)
-{
- return ReadDirectoryChangesW(watch->dir_handle, watch->buffer, sizeof(watch->buffer),
- (watch->watch_flags & DMON_WATCHFLAGS_RECURSIVE) ? TRUE : FALSE,
- watch->notify_filter, NULL, &watch->overlapped, NULL) != 0;
-}
-
-_DMON_PRIVATE void dmon__unwatch(dmon__watch_state* watch)
-{
- CancelIo(watch->dir_handle);
- CloseHandle(watch->overlapped.hEvent);
- CloseHandle(watch->dir_handle);
- memset(watch, 0x0, sizeof(dmon__watch_state));
-}
-
-_DMON_PRIVATE void dmon__win32_process_events(void)
-{
- for (int i = 0, c = stb_sb_count(_dmon.events); i < c; i++) {
- dmon__win32_event* ev = &_dmon.events[i];
- if (ev->skip) {
- continue;
- }
-
- if (ev->action == FILE_ACTION_MODIFIED || ev->action == FILE_ACTION_ADDED) {
- // remove duplicate modifies on a single file
- for (int j = i + 1; j < c; j++) {
- dmon__win32_event* check_ev = &_dmon.events[j];
- if (check_ev->action == FILE_ACTION_MODIFIED &&
- strcmp(ev->filepath, check_ev->filepath) == 0) {
- check_ev->skip = true;
- }
- }
- }
- }
-
- // trigger user callbacks
- for (int i = 0, c = stb_sb_count(_dmon.events); i < c; i++) {
- dmon__win32_event* ev = &_dmon.events[i];
- if (ev->skip) {
- continue;
- }
- dmon__watch_state* watch = &_dmon.watches[ev->watch_id.id - 1];
-
- if(watch == NULL || watch->watch_cb == NULL) {
- continue;
- }
-
- switch (ev->action) {
- case FILE_ACTION_ADDED:
- watch->watch_cb(ev->watch_id, DMON_ACTION_CREATE, watch->rootdir, ev->filepath, NULL,
- watch->user_data);
- break;
- case FILE_ACTION_MODIFIED:
- watch->watch_cb(ev->watch_id, DMON_ACTION_MODIFY, watch->rootdir, ev->filepath, NULL,
- watch->user_data);
- break;
- case FILE_ACTION_RENAMED_OLD_NAME: {
- // find the first occurance of the NEW_NAME
- // this is somewhat API flaw that we have no reference for relating old and new files
- for (int j = i + 1; j < c; j++) {
- dmon__win32_event* check_ev = &_dmon.events[j];
- if (check_ev->action == FILE_ACTION_RENAMED_NEW_NAME) {
- watch->watch_cb(check_ev->watch_id, DMON_ACTION_MOVE, watch->rootdir,
- check_ev->filepath, ev->filepath, watch->user_data);
- break;
- }
- }
- } break;
- case FILE_ACTION_REMOVED:
- watch->watch_cb(ev->watch_id, DMON_ACTION_DELETE, watch->rootdir, ev->filepath, NULL,
- watch->user_data);
- break;
- }
- }
- stb_sb_reset(_dmon.events);
-}
-
-_DMON_PRIVATE DWORD WINAPI dmon__thread(LPVOID arg)
-{
- _DMON_UNUSED(arg);
- HANDLE wait_handles[DMON_MAX_WATCHES];
-
- SYSTEMTIME starttm;
- GetSystemTime(&starttm);
- uint64_t msecs_elapsed = 0;
-
- while (!_dmon.quit) {
- if (_dmon.modify_watches || !TryEnterCriticalSection(&_dmon.mutex)) {
- Sleep(10);
- continue;
- }
-
- if (_dmon.num_watches == 0) {
- Sleep(10);
- LeaveCriticalSection(&_dmon.mutex);
- continue;
- }
-
- for (int i = 0; i < _dmon.num_watches; i++) {
- dmon__watch_state* watch = &_dmon.watches[i];
- wait_handles[i] = watch->overlapped.hEvent;
- }
-
- DWORD wait_result = WaitForMultipleObjects(_dmon.num_watches, wait_handles, FALSE, 10);
- DMON_ASSERT(wait_result != WAIT_FAILED);
- if (wait_result != WAIT_TIMEOUT) {
- dmon__watch_state* watch = &_dmon.watches[wait_result - WAIT_OBJECT_0];
- DMON_ASSERT(HasOverlappedIoCompleted(&watch->overlapped));
-
- DWORD bytes;
- if (GetOverlappedResult(watch->dir_handle, &watch->overlapped, &bytes, FALSE)) {
- char filepath[DMON_MAX_PATH];
- PFILE_NOTIFY_INFORMATION notify;
- size_t offset = 0;
-
- if (bytes == 0) {
- dmon__refresh_watch(watch);
- LeaveCriticalSection(&_dmon.mutex);
- continue;
- }
-
- do {
- notify = (PFILE_NOTIFY_INFORMATION)&watch->buffer[offset];
-
- int count = WideCharToMultiByte(CP_UTF8, 0, notify->FileName,
- notify->FileNameLength / sizeof(WCHAR),
- filepath, DMON_MAX_PATH - 1, NULL, NULL);
- filepath[count] = TEXT('\0');
- dmon__unixpath(filepath, sizeof(filepath), filepath);
-
- // TODO: ignore directories if flag is set
-
- if (stb_sb_count(_dmon.events) == 0) {
- msecs_elapsed = 0;
- }
- dmon__win32_event wev = { { 0 }, notify->Action, watch->id, false };
- dmon__strcpy(wev.filepath, sizeof(wev.filepath), filepath);
- stb_sb_push(_dmon.events, wev);
-
- offset += notify->NextEntryOffset;
- } while (notify->NextEntryOffset > 0);
-
- if (!_dmon.quit) {
- dmon__refresh_watch(watch);
- }
- }
- } // if (WaitForMultipleObjects)
-
- SYSTEMTIME tm;
- GetSystemTime(&tm);
- LONG dt =
- (tm.wSecond - starttm.wSecond) * 1000 + (tm.wMilliseconds - starttm.wMilliseconds);
- starttm = tm;
- msecs_elapsed += dt;
- if (msecs_elapsed > 100 && stb_sb_count(_dmon.events) > 0) {
- dmon__win32_process_events();
- msecs_elapsed = 0;
- }
-
- LeaveCriticalSection(&_dmon.mutex);
- }
- return 0;
-}
-
-
-DMON_API_IMPL void dmon_init(void)
-{
- DMON_ASSERT(!_dmon_init);
- InitializeCriticalSection(&_dmon.mutex);
-
- _dmon.thread_handle =
- CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)dmon__thread, NULL, 0, NULL);
- DMON_ASSERT(_dmon.thread_handle);
- _dmon_init = true;
-}
-
-
-DMON_API_IMPL void dmon_deinit(void)
-{
- DMON_ASSERT(_dmon_init);
- _dmon.quit = true;
- if (_dmon.thread_handle != INVALID_HANDLE_VALUE) {
- WaitForSingleObject(_dmon.thread_handle, INFINITE);
- CloseHandle(_dmon.thread_handle);
- }
-
- for (int i = 0; i < _dmon.num_watches; i++) {
- dmon__unwatch(&_dmon.watches[i]);
- }
-
- DeleteCriticalSection(&_dmon.mutex);
- stb_sb_free(_dmon.events);
- _dmon_init = false;
-}
-
-DMON_API_IMPL dmon_watch_id dmon_watch(const char* rootdir,
- void (*watch_cb)(dmon_watch_id watch_id, dmon_action action,
- const char* dirname, const char* filename,
- const char* oldname, void* user),
- uint32_t flags, void* user_data)
-{
- DMON_ASSERT(watch_cb);
- DMON_ASSERT(rootdir && rootdir[0]);
-
- _InterlockedExchange(&_dmon.modify_watches, 1);
- EnterCriticalSection(&_dmon.mutex);
-
- DMON_ASSERT(_dmon.num_watches < DMON_MAX_WATCHES);
-
- uint32_t id = ++_dmon.num_watches;
- dmon__watch_state* watch = &_dmon.watches[id - 1];
- watch->id = dmon__make_id(id);
- watch->watch_flags = flags;
- watch->watch_cb = watch_cb;
- watch->user_data = user_data;
-
- dmon__strcpy(watch->rootdir, sizeof(watch->rootdir) - 1, rootdir);
- dmon__unixpath(watch->rootdir, sizeof(watch->rootdir), rootdir);
- size_t rootdir_len = strlen(watch->rootdir);
- if (watch->rootdir[rootdir_len - 1] != '/') {
- watch->rootdir[rootdir_len] = '/';
- watch->rootdir[rootdir_len + 1] = '\0';
- }
-
- _DMON_WINAPI_STR(rootdir, DMON_MAX_PATH);
- watch->dir_handle =
- CreateFile(_rootdir, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
- NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL);
- if (watch->dir_handle != INVALID_HANDLE_VALUE) {
- watch->notify_filter = FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_LAST_WRITE |
- FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME |
- FILE_NOTIFY_CHANGE_SIZE;
- watch->overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
- DMON_ASSERT(watch->overlapped.hEvent != INVALID_HANDLE_VALUE);
-
- if (!dmon__refresh_watch(watch)) {
- dmon__unwatch(watch);
- DMON_LOG_ERROR("ReadDirectoryChanges failed");
- LeaveCriticalSection(&_dmon.mutex);
- _InterlockedExchange(&_dmon.modify_watches, 0);
- return dmon__make_id(0);
- }
- } else {
- _DMON_LOG_ERRORF("Could not open: %s", rootdir);
- LeaveCriticalSection(&_dmon.mutex);
- _InterlockedExchange(&_dmon.modify_watches, 0);
- return dmon__make_id(0);
- }
-
- LeaveCriticalSection(&_dmon.mutex);
- _InterlockedExchange(&_dmon.modify_watches, 0);
- return dmon__make_id(id);
-}
-
-DMON_API_IMPL void dmon_unwatch(dmon_watch_id id)
-{
- DMON_ASSERT(id.id > 0);
-
- _InterlockedExchange(&_dmon.modify_watches, 1);
- EnterCriticalSection(&_dmon.mutex);
-
- int index = id.id - 1;
- DMON_ASSERT(index < _dmon.num_watches);
-
- dmon__unwatch(&_dmon.watches[index]);
- if (index != _dmon.num_watches - 1) {
- dmon__swap(_dmon.watches[index], _dmon.watches[_dmon.num_watches - 1], dmon__watch_state);
- }
- --_dmon.num_watches;
-
- LeaveCriticalSection(&_dmon.mutex);
- _InterlockedExchange(&_dmon.modify_watches, 0);
-}
-
-// clang-format off
-#elif DMON_OS_LINUX
-// inotify linux backend
-#define _DMON_TEMP_BUFFSIZE ((sizeof(struct inotify_event) + PATH_MAX) * 1024)
-
-typedef struct dmon__watch_subdir {
- char rootdir[DMON_MAX_PATH];
-} dmon__watch_subdir;
-
-typedef struct dmon__inotify_event {
- char filepath[DMON_MAX_PATH];
- uint32_t mask;
- uint32_t cookie;
- dmon_watch_id watch_id;
- bool skip;
-} dmon__inotify_event;
-
-typedef struct dmon__watch_state {
- dmon_watch_id id;
- int fd;
- uint32_t watch_flags;
- dmon__watch_cb* watch_cb;
- void* user_data;
- char rootdir[DMON_MAX_PATH];
- dmon__watch_subdir* subdirs;
- int* wds;
-} dmon__watch_state;
-
-typedef struct dmon__state {
- dmon__watch_state watches[DMON_MAX_WATCHES];
- dmon__inotify_event* events;
- int num_watches;
- volatile int modify_watches;
- pthread_t thread_handle;
- pthread_mutex_t mutex;
- bool quit;
-} dmon__state;
-
-static bool _dmon_init;
-static dmon__state _dmon;
-// clang-format on
-
-_DMON_PRIVATE void dmon__watch_recursive(const char* dirname, int fd, uint32_t mask,
- bool followlinks, dmon__watch_state* watch)
-{
- struct dirent* entry;
- DIR* dir = opendir(dirname);
- DMON_ASSERT(dir);
-
- char watchdir[DMON_MAX_PATH];
-
- while ((entry = readdir(dir)) != NULL) {
- bool entry_valid = false;
- if (entry->d_type == DT_DIR) {
- if (strcmp(entry->d_name, "..") != 0 && strcmp(entry->d_name, ".") != 0) {
- dmon__strcpy(watchdir, sizeof(watchdir), dirname);
- dmon__strcat(watchdir, sizeof(watchdir), entry->d_name);
- entry_valid = true;
- }
- } else if (followlinks && entry->d_type == DT_LNK) {
- char linkpath[PATH_MAX];
- dmon__strcpy(watchdir, sizeof(watchdir), dirname);
- dmon__strcat(watchdir, sizeof(watchdir), entry->d_name);
- char* r = realpath(watchdir, linkpath);
- _DMON_UNUSED(r);
- DMON_ASSERT(r);
- dmon__strcpy(watchdir, sizeof(watchdir), linkpath);
- entry_valid = true;
- }
-
- // add sub-directory to watch dirs
- if (entry_valid) {
- int watchdir_len = strlen(watchdir);
- if (watchdir[watchdir_len - 1] != '/') {
- watchdir[watchdir_len] = '/';
- watchdir[watchdir_len + 1] = '\0';
- }
- int wd = inotify_add_watch(fd, watchdir, mask);
- _DMON_UNUSED(wd);
- DMON_ASSERT(wd != -1);
-
- dmon__watch_subdir subdir;
- dmon__strcpy(subdir.rootdir, sizeof(subdir.rootdir), watchdir);
- stb_sb_push(watch->subdirs, subdir);
- stb_sb_push(watch->wds, wd);
-
- // recurse
- dmon__watch_recursive(watchdir, fd, mask, followlinks, watch);
- }
- }
- closedir(dir);
-}
-
-_DMON_PRIVATE void dmon__inotify_process_events(void)
-{
- for (int i = 0, c = stb_sb_count(_dmon.events); i < c; i++) {
- dmon__inotify_event* ev = &_dmon.events[i];
- if (ev->skip) {
- continue;
- }
-
- // remove redundant modify events on a single file
- if (ev->mask == IN_MODIFY) {
- for (int j = i + 1; j < c; j++) {
- dmon__inotify_event* check_ev = &_dmon.events[j];
- if (check_ev->mask == IN_MODIFY && strcmp(ev->filepath, check_ev->filepath) == 0) {
- ev->skip = true;
- break;
- }
- }
- } else if (ev->mask == IN_CREATE) {
- bool loop_break = false;
- for (int j = i + 1; j < c && !loop_break; j++) {
- dmon__inotify_event* check_ev = &_dmon.events[j];
- if (check_ev->mask == IN_MOVED_FROM &&
- strcmp(ev->filepath, check_ev->filepath) == 0) {
- // there is a case where some programs (like gedit):
- // when we save, it creates a temp file, and moves it to the file being modified
- // search for these cases and remove all of them
- for (int k = j + 1; k < c; k++) {
- dmon__inotify_event* third_ev = &_dmon.events[k];
- if (third_ev->mask == IN_MOVED_TO && check_ev->cookie == third_ev->cookie) {
- third_ev->mask = IN_MODIFY; // change to modified
- ev->skip = check_ev->skip = true;
- loop_break = true;
- break;
- }
- }
- } else if (check_ev->mask == IN_MODIFY && strcmp(ev->filepath, check_ev->filepath) == 0) {
- // Another case is that file is copied. CREATE and MODIFY happens sequentially
- // so we ignore MODIFY event
- check_ev->skip = true;
- }
- }
- } else if (ev->mask == IN_MOVED_FROM) {
- bool move_valid = false;
- for (int j = i + 1; j < c; j++) {
- dmon__inotify_event* check_ev = &_dmon.events[j];
- if (check_ev->mask == IN_MOVED_TO && ev->cookie == check_ev->cookie) {
- move_valid = true;
- break;
- }
- }
-
- // in some environments like nautilus file explorer:
- // when a file is deleted, it is moved to recycle bin
- // so if the destination of the move is not valid, it's probably DELETE
- if (!move_valid) {
- ev->mask = IN_DELETE;
- }
- } else if (ev->mask == IN_MOVED_TO) {
- bool move_valid = false;
- for (int j = 0; j < i; j++) {
- dmon__inotify_event* check_ev = &_dmon.events[j];
- if (check_ev->mask == IN_MOVED_FROM && ev->cookie == check_ev->cookie) {
- move_valid = true;
- break;
- }
- }
-
- // in some environments like nautilus file explorer:
- // when a file is deleted, it is moved to recycle bin, on undo it is moved back it
- // so if the destination of the move is not valid, it's probably CREATE
- if (!move_valid) {
- ev->mask = IN_CREATE;
- }
- }
- }
-
- // trigger user callbacks
- for (int i = 0, c = stb_sb_count(_dmon.events); i < c; i++) {
- dmon__inotify_event* ev = &_dmon.events[i];
- if (ev->skip) {
- continue;
- }
- dmon__watch_state* watch = &_dmon.watches[ev->watch_id.id - 1];
-
- if(watch == NULL || watch->watch_cb == NULL) {
- continue;
- }
-
- switch (ev->mask) {
- case IN_CREATE:
- watch->watch_cb(ev->watch_id, DMON_ACTION_CREATE, watch->rootdir, ev->filepath, NULL, watch->user_data);
- break;
- case IN_MODIFY:
- watch->watch_cb(ev->watch_id, DMON_ACTION_MODIFY, watch->rootdir, ev->filepath, NULL, watch->user_data);
- break;
- case IN_MOVED_FROM: {
- for (int j = i + 1; j < c; j++) {
- dmon__inotify_event* check_ev = &_dmon.events[j];
- if (check_ev->mask == IN_MOVED_TO && ev->cookie == check_ev->cookie) {
- watch->watch_cb(check_ev->watch_id, DMON_ACTION_MOVE, watch->rootdir,
- check_ev->filepath, ev->filepath, watch->user_data);
- break;
- }
- }
- } break;
- case IN_DELETE:
- watch->watch_cb(ev->watch_id, DMON_ACTION_DELETE, watch->rootdir, ev->filepath, NULL, watch->user_data);
- break;
- }
- }
-
-
- stb_sb_reset(_dmon.events);
-}
-
-static void* dmon__thread(void* arg)
-{
- _DMON_UNUSED(arg);
-
- static uint8_t buff[_DMON_TEMP_BUFFSIZE];
- struct timespec req = { (time_t)10 / 1000, (long)(10 * 1000000) };
- struct timespec rem = { 0, 0 };
- struct timeval timeout;
- uint64_t usecs_elapsed = 0;
-
- struct timeval starttm;
- gettimeofday(&starttm, 0);
-
- while (!_dmon.quit) {
-
- if (_dmon.modify_watches || pthread_mutex_trylock(&_dmon.mutex) != 0) {
- nanosleep(&req, &rem);
- continue;
- }
-
- if (_dmon.num_watches == 0) {
- nanosleep(&req, &rem);
- pthread_mutex_unlock(&_dmon.mutex);
- continue;
- }
-
- // Create read FD set
- fd_set rfds;
- FD_ZERO(&rfds);
- for (int i = 0; i < _dmon.num_watches; i++) {
- dmon__watch_state* watch = &_dmon.watches[i];
- FD_SET(watch->fd, &rfds);
- }
-
- timeout.tv_sec = 0;
- timeout.tv_usec = 100000;
- if (select(FD_SETSIZE, &rfds, NULL, NULL, &timeout)) {
- for (int i = 0; i < _dmon.num_watches; i++) {
- dmon__watch_state* watch = &_dmon.watches[i];
- if (FD_ISSET(watch->fd, &rfds)) {
- ssize_t offset = 0;
- ssize_t len = read(watch->fd, buff, _DMON_TEMP_BUFFSIZE);
- if (len <= 0) {
- continue;
- }
-
- while (offset < len) {
- struct inotify_event* iev = (struct inotify_event*)&buff[offset];
-
- char filepath[DMON_MAX_PATH];
- dmon__strcpy(filepath, sizeof(filepath), iev->name);
-
- // TODO: ignore directories if flag is set
-
- if (stb_sb_count(_dmon.events) == 0) {
- usecs_elapsed = 0;
- }
- dmon__inotify_event dev = { { 0 }, iev->mask, iev->cookie, watch->id, false };
- dmon__strcpy(dev.filepath, sizeof(dev.filepath), filepath);
- stb_sb_push(_dmon.events, dev);
-
- offset += sizeof(struct inotify_event) + iev->len;
- }
- }
- }
- }
-
- struct timeval tm;
- gettimeofday(&tm, 0);
- long dt = (tm.tv_sec - starttm.tv_sec) * 1000000 + tm.tv_usec - starttm.tv_usec;
- starttm = tm;
- usecs_elapsed += dt;
- if (usecs_elapsed > 100000 && stb_sb_count(_dmon.events) > 0) {
- dmon__inotify_process_events();
- usecs_elapsed = 0;
- }
-
- pthread_mutex_unlock(&_dmon.mutex);
- }
- return 0x0;
-}
-
-_DMON_PRIVATE void dmon__unwatch(dmon__watch_state* watch)
-{
- close(watch->fd);
- stb_sb_free(watch->subdirs);
- stb_sb_free(watch->wds);
- memset(watch, 0x0, sizeof(dmon__watch_state));
-}
-
-DMON_API_IMPL void dmon_init(void)
-{
- DMON_ASSERT(!_dmon_init);
- pthread_mutex_init(&_dmon.mutex, NULL);
-
- int r = pthread_create(&_dmon.thread_handle, NULL, dmon__thread, NULL);
- _DMON_UNUSED(r);
- DMON_ASSERT(r == 0 && "pthread_create failed");
- _dmon_init = true;
-}
-
-DMON_API_IMPL void dmon_deinit(void)
-{
- DMON_ASSERT(_dmon_init);
- _dmon.quit = true;
- pthread_join(_dmon.thread_handle, NULL);
-
- for (int i = 0; i < _dmon.num_watches; i++) {
- dmon__unwatch(&_dmon.watches[i]);
- }
-
- pthread_mutex_destroy(&_dmon.mutex);
- stb_sb_free(_dmon.events);
- _dmon_init = false;
-}
-
-DMON_API_IMPL dmon_watch_id dmon_watch(const char* rootdir,
- void (*watch_cb)(dmon_watch_id watch_id, dmon_action action,
- const char* dirname, const char* filename,
- const char* oldname, void* user),
- uint32_t flags, void* user_data)
-{
- DMON_ASSERT(watch_cb);
- DMON_ASSERT(rootdir && rootdir[0]);
-
- __sync_lock_test_and_set(&_dmon.modify_watches, 1);
- pthread_mutex_lock(&_dmon.mutex);
-
- DMON_ASSERT(_dmon.num_watches < DMON_MAX_WATCHES);
-
- uint32_t id = ++_dmon.num_watches;
- dmon__watch_state* watch = &_dmon.watches[id - 1];
- watch->id = dmon__make_id(id);
- watch->watch_flags = flags;
- watch->watch_cb = watch_cb;
- watch->user_data = user_data;
-
- struct stat root_st;
- if (stat(rootdir, &root_st) != 0 || !S_ISDIR(root_st.st_mode) ||
- (root_st.st_mode & S_IRUSR) != S_IRUSR) {
- _DMON_LOG_ERRORF("Could not open/read directory: %s", rootdir);
- pthread_mutex_unlock(&_dmon.mutex);
- __sync_lock_test_and_set(&_dmon.modify_watches, 0);
- return dmon__make_id(0);
- }
-
-
- if (S_ISLNK(root_st.st_mode)) {
- if (flags & DMON_WATCHFLAGS_FOLLOW_SYMLINKS) {
- char linkpath[PATH_MAX];
- char* r = realpath(rootdir, linkpath);
- _DMON_UNUSED(r);
- DMON_ASSERT(r);
-
- dmon__strcpy(watch->rootdir, sizeof(watch->rootdir) - 1, linkpath);
- } else {
- _DMON_LOG_ERRORF("symlinks are unsupported: %s. use DMON_WATCHFLAGS_FOLLOW_SYMLINKS",
- rootdir);
- pthread_mutex_unlock(&_dmon.mutex);
- __sync_lock_test_and_set(&_dmon.modify_watches, 0);
- return dmon__make_id(0);
- }
- } else {
- dmon__strcpy(watch->rootdir, sizeof(watch->rootdir) - 1, rootdir);
- }
-
- // add trailing slash
- int rootdir_len = strlen(watch->rootdir);
- if (watch->rootdir[rootdir_len - 1] != '/') {
- watch->rootdir[rootdir_len] = '/';
- watch->rootdir[rootdir_len + 1] = '\0';
- }
-
- watch->fd = inotify_init();
- if (watch->fd < -1) {
- DMON_LOG_ERROR("could not create inotify instance");
- pthread_mutex_unlock(&_dmon.mutex);
- __sync_lock_test_and_set(&_dmon.modify_watches, 0);
- return dmon__make_id(0);
- }
-
- uint32_t inotify_mask = IN_MOVED_TO | IN_CREATE | IN_MOVED_FROM | IN_DELETE | IN_MODIFY;
- int wd = inotify_add_watch(watch->fd, watch->rootdir, inotify_mask);
- if (wd < 0) {
- _DMON_LOG_ERRORF("watch failed: %s", watch->rootdir);
- pthread_mutex_unlock(&_dmon.mutex);
- __sync_lock_test_and_set(&_dmon.modify_watches, 0);
- return dmon__make_id(0);
- }
- dmon__watch_subdir subdir;
- dmon__strcpy(subdir.rootdir, sizeof(subdir.rootdir), watch->rootdir);
- stb_sb_push(watch->subdirs, subdir);
- stb_sb_push(watch->wds, wd);
-
- // recursive mode: enumarate all child directories and add them to watch
- if (flags & DMON_WATCHFLAGS_RECURSIVE) {
- dmon__watch_recursive(watch->rootdir, watch->fd, inotify_mask,
- (flags & DMON_WATCHFLAGS_FOLLOW_SYMLINKS) ? true : false, watch);
- }
-
-
- pthread_mutex_unlock(&_dmon.mutex);
- __sync_lock_test_and_set(&_dmon.modify_watches, 0);
- return dmon__make_id(id);
-}
-
-DMON_API_IMPL void dmon_unwatch(dmon_watch_id id)
-{
- DMON_ASSERT(id.id > 0);
-
- __sync_lock_test_and_set(&_dmon.modify_watches, 1);
- pthread_mutex_lock(&_dmon.mutex);
-
- int index = id.id - 1;
- DMON_ASSERT(index < _dmon.num_watches);
-
- dmon__unwatch(&_dmon.watches[index]);
- if (index != _dmon.num_watches - 1) {
- dmon__swap(_dmon.watches[index], _dmon.watches[_dmon.num_watches - 1], dmon__watch_state);
- }
- --_dmon.num_watches;
-
- pthread_mutex_unlock(&_dmon.mutex);
- __sync_lock_test_and_set(&_dmon.modify_watches, 0);
-}
-// clang-format off
-#elif DMON_OS_MACOS
-// FSEvents MacOS backend
-typedef struct dmon__fsevent_event {
- char filepath[DMON_MAX_PATH];
- uint64_t event_id;
- long event_flags;
- dmon_watch_id watch_id;
- bool skip;
- bool move_valid;
-} dmon__fsevent_event;
-
-typedef struct dmon__watch_state {
- dmon_watch_id id;
- uint32_t watch_flags;
- FSEventStreamRef fsev_stream_ref;
- dmon__watch_cb* watch_cb;
- void* user_data;
- char rootdir[DMON_MAX_PATH];
- bool init;
-} dmon__watch_state;
-
-typedef struct dmon__state {
- dmon__watch_state watches[DMON_MAX_WATCHES];
- dmon__fsevent_event* events;
- int num_watches;
- volatile int modify_watches;
- pthread_t thread_handle;
- dispatch_semaphore_t thread_sem;
- pthread_mutex_t mutex;
- CFRunLoopRef cf_loop_ref;
- CFAllocatorRef cf_alloc_ref;
- bool quit;
-} dmon__state;
-
-union dmon__cast_userdata {
- void* ptr;
- uint32_t id;
-};
-
-static bool _dmon_init;
-static dmon__state _dmon;
-// clang-format on
-
-_DMON_PRIVATE void* dmon__cf_malloc(CFIndex size, CFOptionFlags hints, void* info)
-{
- _DMON_UNUSED(hints);
- _DMON_UNUSED(info);
- return DMON_MALLOC(size);
-}
-
-_DMON_PRIVATE void dmon__cf_free(void* ptr, void* info)
-{
- _DMON_UNUSED(info);
- DMON_FREE(ptr);
-}
-
-_DMON_PRIVATE void* dmon__cf_realloc(void* ptr, CFIndex newsize, CFOptionFlags hints, void* info)
-{
- _DMON_UNUSED(hints);
- _DMON_UNUSED(info);
- return DMON_REALLOC(ptr, (size_t)newsize);
-}
-
-_DMON_PRIVATE void dmon__fsevent_process_events(void)
-{
- for (int i = 0, c = stb_sb_count(_dmon.events); i < c; i++) {
- dmon__fsevent_event* ev = &_dmon.events[i];
- if (ev->skip) {
- continue;
- }
-
- // remove redundant modify events on a single file
- if (ev->event_flags & kFSEventStreamEventFlagItemModified) {
- for (int j = i + 1; j < c; j++) {
- dmon__fsevent_event* check_ev = &_dmon.events[j];
- if ((check_ev->event_flags & kFSEventStreamEventFlagItemModified) &&
- strcmp(ev->filepath, check_ev->filepath) == 0) {
- ev->skip = true;
- break;
- }
- }
- } else if ((ev->event_flags & kFSEventStreamEventFlagItemRenamed) && !ev->move_valid) {
- for (int j = i + 1; j < c; j++) {
- dmon__fsevent_event* check_ev = &_dmon.events[j];
- if ((check_ev->event_flags & kFSEventStreamEventFlagItemRenamed) &&
- check_ev->event_id == (ev->event_id + 1)) {
- ev->move_valid = check_ev->move_valid = true;
- break;
- }
- }
-
- // in some environments like finder file explorer:
- // when a file is deleted, it is moved to recycle bin
- // so if the destination of the move is not valid, it's probably DELETE or CREATE
- // decide CREATE if file exists
- if (!ev->move_valid) {
- ev->event_flags &= ~kFSEventStreamEventFlagItemRenamed;
-
- char abs_filepath[DMON_MAX_PATH];
- dmon__watch_state* watch = &_dmon.watches[ev->watch_id.id-1];
- dmon__strcpy(abs_filepath, sizeof(abs_filepath), watch->rootdir);
- dmon__strcat(abs_filepath, sizeof(abs_filepath), ev->filepath);
-
- struct stat root_st;
- if (stat(abs_filepath, &root_st) != 0) {
- ev->event_flags |= kFSEventStreamEventFlagItemRemoved;
- } else {
- ev->event_flags |= kFSEventStreamEventFlagItemCreated;
- }
- }
- }
- }
-
- // trigger user callbacks
- for (int i = 0, c = stb_sb_count(_dmon.events); i < c; i++) {
- dmon__fsevent_event* ev = &_dmon.events[i];
- if (ev->skip) {
- continue;
- }
- dmon__watch_state* watch = &_dmon.watches[ev->watch_id.id - 1];
-
- if(watch == NULL || watch->watch_cb == NULL) {
- continue;
- }
-
- if (ev->event_flags & kFSEventStreamEventFlagItemCreated) {
- watch->watch_cb(ev->watch_id, DMON_ACTION_CREATE, watch->rootdir, ev->filepath, NULL,
- watch->user_data);
- } else if (ev->event_flags & kFSEventStreamEventFlagItemModified) {
- watch->watch_cb(ev->watch_id, DMON_ACTION_MODIFY, watch->rootdir, ev->filepath, NULL,
- watch->user_data);
- } else if (ev->event_flags & kFSEventStreamEventFlagItemRenamed) {
- for (int j = i + 1; j < c; j++) {
- dmon__fsevent_event* check_ev = &_dmon.events[j];
- if (check_ev->event_flags & kFSEventStreamEventFlagItemRenamed) {
- watch->watch_cb(check_ev->watch_id, DMON_ACTION_MOVE, watch->rootdir,
- check_ev->filepath, ev->filepath, watch->user_data);
- break;
- }
- }
- } else if (ev->event_flags & kFSEventStreamEventFlagItemRemoved) {
- watch->watch_cb(ev->watch_id, DMON_ACTION_DELETE, watch->rootdir, ev->filepath, NULL,
- watch->user_data);
- }
- }
-
- stb_sb_reset(_dmon.events);
-}
-
-static void* dmon__thread(void* arg)
-{
- _DMON_UNUSED(arg);
-
- struct timespec req = { (time_t)10 / 1000, (long)(10 * 1000000) };
- struct timespec rem = { 0, 0 };
-
- _dmon.cf_loop_ref = CFRunLoopGetCurrent();
- dispatch_semaphore_signal(_dmon.thread_sem);
-
- while (!_dmon.quit) {
- if (_dmon.modify_watches || pthread_mutex_trylock(&_dmon.mutex) != 0) {
- nanosleep(&req, &rem);
- continue;
- }
-
- if (_dmon.num_watches == 0) {
- nanosleep(&req, &rem);
- pthread_mutex_unlock(&_dmon.mutex);
- continue;
- }
-
- for (int i = 0; i < _dmon.num_watches; i++) {
- dmon__watch_state* watch = &_dmon.watches[i];
- if (!watch->init) {
- DMON_ASSERT(watch->fsev_stream_ref);
- FSEventStreamScheduleWithRunLoop(watch->fsev_stream_ref, _dmon.cf_loop_ref,
- kCFRunLoopDefaultMode);
- FSEventStreamStart(watch->fsev_stream_ref);
-
- watch->init = true;
- }
- }
-
- CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.5, kCFRunLoopRunTimedOut);
- dmon__fsevent_process_events();
-
- pthread_mutex_unlock(&_dmon.mutex);
- }
-
- CFRunLoopStop(_dmon.cf_loop_ref);
- _dmon.cf_loop_ref = NULL;
- return 0x0;
-}
-
-_DMON_PRIVATE void dmon__unwatch(dmon__watch_state* watch)
-{
- if (watch->fsev_stream_ref) {
- FSEventStreamStop(watch->fsev_stream_ref);
- FSEventStreamInvalidate(watch->fsev_stream_ref);
- FSEventStreamRelease(watch->fsev_stream_ref);
- watch->fsev_stream_ref = NULL;
- }
-
- memset(watch, 0x0, sizeof(dmon__watch_state));
-}
-
-DMON_API_IMPL void dmon_init(void)
-{
- DMON_ASSERT(!_dmon_init);
- pthread_mutex_init(&_dmon.mutex, NULL);
-
- CFAllocatorContext cf_alloc_ctx = { 0 };
- cf_alloc_ctx.allocate = dmon__cf_malloc;
- cf_alloc_ctx.deallocate = dmon__cf_free;
- cf_alloc_ctx.reallocate = dmon__cf_realloc;
- _dmon.cf_alloc_ref = CFAllocatorCreate(NULL, &cf_alloc_ctx);
-
- _dmon.thread_sem = dispatch_semaphore_create(0);
- DMON_ASSERT(_dmon.thread_sem);
-
- int r = pthread_create(&_dmon.thread_handle, NULL, dmon__thread, NULL);
- _DMON_UNUSED(r);
- DMON_ASSERT(r == 0 && "pthread_create failed");
-
- // wait for thread to initialize loop object
- dispatch_semaphore_wait(_dmon.thread_sem, DISPATCH_TIME_FOREVER);
-
- _dmon_init = true;
-}
-
-DMON_API_IMPL void dmon_deinit(void)
-{
- DMON_ASSERT(_dmon_init);
- _dmon.quit = true;
- pthread_join(_dmon.thread_handle, NULL);
-
- dispatch_release(_dmon.thread_sem);
-
- for (int i = 0; i < _dmon.num_watches; i++) {
- dmon__unwatch(&_dmon.watches[i]);
- }
-
- pthread_mutex_destroy(&_dmon.mutex);
- stb_sb_free(_dmon.events);
- if (_dmon.cf_alloc_ref) {
- CFRelease(_dmon.cf_alloc_ref);
- }
-
- _dmon_init = false;
-}
-
-_DMON_PRIVATE void dmon__fsevent_callback(ConstFSEventStreamRef stream_ref, void* user_data,
- size_t num_events, void* event_paths,
- const FSEventStreamEventFlags event_flags[],
- const FSEventStreamEventId event_ids[])
-{
- _DMON_UNUSED(stream_ref);
-
- union dmon__cast_userdata _userdata;
- _userdata.ptr = user_data;
- dmon_watch_id watch_id = dmon__make_id(_userdata.id);
- DMON_ASSERT(watch_id.id > 0);
- dmon__watch_state* watch = &_dmon.watches[watch_id.id - 1];
- char abs_filepath[DMON_MAX_PATH];
-
- for (size_t i = 0; i < num_events; i++) {
- const char* filepath = ((const char**)event_paths)[i];
- long flags = (long)event_flags[i];
- uint64_t event_id = (uint64_t)event_ids[i];
- dmon__fsevent_event ev;
- memset(&ev, 0x0, sizeof(ev));
-
- dmon__strcpy(abs_filepath, sizeof(abs_filepath), filepath);
-
- // normalize path (TODO: have to recheck this to be consistent with other platforms)
- dmon__tolower(abs_filepath, sizeof(abs_filepath),
- dmon__unixpath(abs_filepath, sizeof(abs_filepath), abs_filepath));
-
- // strip the root dir
- DMON_ASSERT(strstr(abs_filepath, watch->rootdir) == abs_filepath);
- dmon__strcpy(ev.filepath, sizeof(ev.filepath), abs_filepath + strlen(watch->rootdir));
-
- ev.event_flags = flags;
- ev.event_id = event_id;
- ev.watch_id = watch_id;
- stb_sb_push(_dmon.events, ev);
- }
-}
-
-DMON_API_IMPL dmon_watch_id dmon_watch(const char* rootdir,
- void (*watch_cb)(dmon_watch_id watch_id, dmon_action action,
- const char* dirname, const char* filename,
- const char* oldname, void* user),
- uint32_t flags, void* user_data)
-{
- DMON_ASSERT(watch_cb);
- DMON_ASSERT(rootdir && rootdir[0]);
-
- __sync_lock_test_and_set(&_dmon.modify_watches, 1);
- pthread_mutex_lock(&_dmon.mutex);
-
- DMON_ASSERT(_dmon.num_watches < DMON_MAX_WATCHES);
-
- uint32_t id = ++_dmon.num_watches;
- dmon__watch_state* watch = &_dmon.watches[id - 1];
- watch->id = dmon__make_id(id);
- watch->watch_flags = flags;
- watch->watch_cb = watch_cb;
- watch->user_data = user_data;
-
- struct stat root_st;
- if (stat(rootdir, &root_st) != 0 || !S_ISDIR(root_st.st_mode) ||
- (root_st.st_mode & S_IRUSR) != S_IRUSR) {
- _DMON_LOG_ERRORF("Could not open/read directory: %s", rootdir);
- pthread_mutex_unlock(&_dmon.mutex);
- __sync_lock_test_and_set(&_dmon.modify_watches, 0);
- return dmon__make_id(0);
- }
-
- if (S_ISLNK(root_st.st_mode)) {
- if (flags & DMON_WATCHFLAGS_FOLLOW_SYMLINKS) {
- char linkpath[PATH_MAX];
- char* r = realpath(rootdir, linkpath);
- _DMON_UNUSED(r);
- DMON_ASSERT(r);
-
- dmon__strcpy(watch->rootdir, sizeof(watch->rootdir) - 1, linkpath);
- } else {
- _DMON_LOG_ERRORF("symlinks are unsupported: %s. use DMON_WATCHFLAGS_FOLLOW_SYMLINKS",
- rootdir);
- pthread_mutex_unlock(&_dmon.mutex);
- __sync_lock_test_and_set(&_dmon.modify_watches, 0);
- return dmon__make_id(0);
- }
- } else {
- dmon__strcpy(watch->rootdir, sizeof(watch->rootdir) - 1, rootdir);
- }
-
- // add trailing slash
- int rootdir_len = strlen(watch->rootdir);
- if (watch->rootdir[rootdir_len - 1] != '/') {
- watch->rootdir[rootdir_len] = '/';
- watch->rootdir[rootdir_len + 1] = '\0';
- }
-
- // create FS objects
- CFStringRef cf_dir = CFStringCreateWithCString(NULL, watch->rootdir, kCFStringEncodingUTF8);
- CFArrayRef cf_dirarr = CFArrayCreate(NULL, (const void**)&cf_dir, 1, NULL);
-
- FSEventStreamContext ctx;
- union dmon__cast_userdata userdata;
- userdata.id = id;
- ctx.version = 0;
- ctx.info = userdata.ptr;
- ctx.retain = NULL;
- ctx.release = NULL;
- ctx.copyDescription = NULL;
- watch->fsev_stream_ref = FSEventStreamCreate(_dmon.cf_alloc_ref, dmon__fsevent_callback, &ctx,
- cf_dirarr, kFSEventStreamEventIdSinceNow, 0.25,
- kFSEventStreamCreateFlagFileEvents);
-
-
- CFRelease(cf_dirarr);
- CFRelease(cf_dir);
-
- pthread_mutex_unlock(&_dmon.mutex);
- __sync_lock_test_and_set(&_dmon.modify_watches, 0);
- return dmon__make_id(id);
-}
-
-DMON_API_IMPL void dmon_unwatch(dmon_watch_id id)
-{
- DMON_ASSERT(id.id > 0);
-
- __sync_lock_test_and_set(&_dmon.modify_watches, 1);
- pthread_mutex_lock(&_dmon.mutex);
-
- int index = id.id - 1;
- DMON_ASSERT(index < _dmon.num_watches);
-
- dmon__unwatch(&_dmon.watches[index]);
- if (index != _dmon.num_watches - 1) {
- dmon__swap(_dmon.watches[index], _dmon.watches[_dmon.num_watches - 1], dmon__watch_state);
- }
- --_dmon.num_watches;
-
- pthread_mutex_unlock(&_dmon.mutex);
- __sync_lock_test_and_set(&_dmon.modify_watches, 0);
-}
-
-// clang-format off
-#endif
-
-#endif // DMON_IMPL
-#endif // __DMON_H__
-// clang-format on
diff --git a/framework/3rd/dmon/test.c b/framework/3rd/dmon/test.c
deleted file mode 100755
index dabc631..0000000
--- a/framework/3rd/dmon/test.c
+++ /dev/null
@@ -1,40 +0,0 @@
-#include
-
-#define DMON_IMPL
-#include "dmon.h"
-
-static void watch_callback(dmon_watch_id watch_id, dmon_action action, const char* rootdir,
- const char* filepath, const char* oldfilepath, void* user)
-{
- (void)(user);
- (void)(watch_id);
-
- switch (action) {
- case DMON_ACTION_CREATE:
- printf("CREATE: [%s]%s\n", rootdir, filepath);
- break;
- case DMON_ACTION_DELETE:
- printf("DELETE: [%s]%s\n", rootdir, filepath);
- break;
- case DMON_ACTION_MODIFY:
- printf("MODIFY: [%s]%s\n", rootdir, filepath);
- break;
- case DMON_ACTION_MOVE:
- printf("MOVE: [%s]%s -> [%s]%s\n", rootdir, oldfilepath, rootdir, filepath);
- break;
- }
-}
-
-int main(int argc, char* argv[])
-{
- if (argc > 1) {
- dmon_init();
- puts("waiting for changes ..");
- dmon_watch(argv[1], watch_callback, DMON_WATCHFLAGS_RECURSIVE, NULL);
- getchar();
- dmon_deinit();
- } else {
- puts("usage: test dirname");
- }
- return 0;
-}
diff --git a/framework/3rd/greatest/greatest.h b/framework/3rd/greatest/greatest.h
deleted file mode 100644
index 9022c95..0000000
--- a/framework/3rd/greatest/greatest.h
+++ /dev/null
@@ -1,1266 +0,0 @@
-/*
- * Copyright (c) 2011-2021 Scott Vokes
- *
- * Permission to use, copy, modify, and/or 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.
- */
-
-#ifndef GREATEST_H
-#define GREATEST_H
-
-#if defined(__cplusplus) && !defined(GREATEST_NO_EXTERN_CPLUSPLUS)
-extern "C" {
-#endif
-
-/* 1.5.0 */
-#define GREATEST_VERSION_MAJOR 1
-#define GREATEST_VERSION_MINOR 5
-#define GREATEST_VERSION_PATCH 0
-
-/* A unit testing system for C, contained in 1 file.
- * It doesn't use dynamic allocation or depend on anything
- * beyond ANSI C89.
- *
- * An up-to-date version can be found at:
- * https://github.com/silentbicycle/greatest/
- */
-
-
-/*********************************************************************
- * Minimal test runner template
- *********************************************************************/
-#if 0
-
-#include "greatest.h"
-
-TEST foo_should_foo(void) {
- PASS();
-}
-
-static void setup_cb(void *data) {
- printf("setup callback for each test case\n");
-}
-
-static void teardown_cb(void *data) {
- printf("teardown callback for each test case\n");
-}
-
-SUITE(suite) {
- /* Optional setup/teardown callbacks which will be run before/after
- * every test case. If using a test suite, they will be cleared when
- * the suite finishes. */
- SET_SETUP(setup_cb, voidp_to_callback_data);
- SET_TEARDOWN(teardown_cb, voidp_to_callback_data);
-
- RUN_TEST(foo_should_foo);
-}
-
-/* Add definitions that need to be in the test runner's main file. */
-GREATEST_MAIN_DEFS();
-
-/* Set up, run suite(s) of tests, report pass/fail/skip stats. */
-int run_tests(void) {
- GREATEST_INIT(); /* init. greatest internals */
- /* List of suites to run (if any). */
- RUN_SUITE(suite);
-
- /* Tests can also be run directly, without using test suites. */
- RUN_TEST(foo_should_foo);
-
- GREATEST_PRINT_REPORT(); /* display results */
- return greatest_all_passed();
-}
-
-/* main(), for a standalone command-line test runner.
- * This replaces run_tests above, and adds command line option
- * handling and exiting with a pass/fail status. */
-int main(int argc, char **argv) {
- GREATEST_MAIN_BEGIN(); /* init & parse command-line args */
- RUN_SUITE(suite);
- GREATEST_MAIN_END(); /* display results */
-}
-
-#endif
-/*********************************************************************/
-
-
-#include
-#include
-#include
-#include
-
-/***********
- * Options *
- ***********/
-
-/* Default column width for non-verbose output. */
-#ifndef GREATEST_DEFAULT_WIDTH
-#define GREATEST_DEFAULT_WIDTH 72
-#endif
-
-/* FILE *, for test logging. */
-#ifndef GREATEST_STDOUT
-#define GREATEST_STDOUT stdout
-#endif
-
-/* Remove GREATEST_ prefix from most commonly used symbols? */
-#ifndef GREATEST_USE_ABBREVS
-#define GREATEST_USE_ABBREVS 1
-#endif
-
-/* Set to 0 to disable all use of setjmp/longjmp. */
-#ifndef GREATEST_USE_LONGJMP
-#define GREATEST_USE_LONGJMP 0
-#endif
-
-/* Make it possible to replace fprintf with another
- * function with the same interface. */
-#ifndef GREATEST_FPRINTF
-#define GREATEST_FPRINTF fprintf
-#endif
-
-#if GREATEST_USE_LONGJMP
-#include
-#endif
-
-/* Set to 0 to disable all use of time.h / clock(). */
-#ifndef GREATEST_USE_TIME
-#define GREATEST_USE_TIME 1
-#endif
-
-#if GREATEST_USE_TIME
-#include
-#endif
-
-/* Floating point type, for ASSERT_IN_RANGE. */
-#ifndef GREATEST_FLOAT
-#define GREATEST_FLOAT double
-#define GREATEST_FLOAT_FMT "%g"
-#endif
-
-/* Size of buffer for test name + optional '_' separator and suffix */
-#ifndef GREATEST_TESTNAME_BUF_SIZE
-#define GREATEST_TESTNAME_BUF_SIZE 128
-#endif
-
-
-/*********
- * Types *
- *********/
-
-/* Info for the current running suite. */
-typedef struct greatest_suite_info {
- unsigned int tests_run;
- unsigned int passed;
- unsigned int failed;
- unsigned int skipped;
-
-#if GREATEST_USE_TIME
- /* timers, pre/post running suite and individual tests */
- clock_t pre_suite;
- clock_t post_suite;
- clock_t pre_test;
- clock_t post_test;
-#endif
-} greatest_suite_info;
-
-/* Type for a suite function. */
-typedef void greatest_suite_cb(void);
-
-/* Types for setup/teardown callbacks. If non-NULL, these will be run
- * and passed the pointer to their additional data. */
-typedef void greatest_setup_cb(void *udata);
-typedef void greatest_teardown_cb(void *udata);
-
-/* Type for an equality comparison between two pointers of the same type.
- * Should return non-0 if equal, otherwise 0.
- * UDATA is a closure value, passed through from ASSERT_EQUAL_T[m]. */
-typedef int greatest_equal_cb(const void *expd, const void *got, void *udata);
-
-/* Type for a callback that prints a value pointed to by T.
- * Return value has the same meaning as printf's.
- * UDATA is a closure value, passed through from ASSERT_EQUAL_T[m]. */
-typedef int greatest_printf_cb(const void *t, void *udata);
-
-/* Callbacks for an arbitrary type; needed for type-specific
- * comparisons via GREATEST_ASSERT_EQUAL_T[m].*/
-typedef struct greatest_type_info {
- greatest_equal_cb *equal;
- greatest_printf_cb *print;
-} greatest_type_info;
-
-typedef struct greatest_memory_cmp_env {
- const unsigned char *exp;
- const unsigned char *got;
- size_t size;
-} greatest_memory_cmp_env;
-
-/* Callbacks for string and raw memory types. */
-extern greatest_type_info greatest_type_info_string;
-extern greatest_type_info greatest_type_info_memory;
-
-typedef enum {
- GREATEST_FLAG_FIRST_FAIL = 0x01,
- GREATEST_FLAG_LIST_ONLY = 0x02,
- GREATEST_FLAG_ABORT_ON_FAIL = 0x04
-} greatest_flag_t;
-
-/* Internal state for a PRNG, used to shuffle test order. */
-struct greatest_prng {
- unsigned char random_order; /* use random ordering? */
- unsigned char initialized; /* is random ordering initialized? */
- unsigned char pad_0[6];
- unsigned long state; /* PRNG state */
- unsigned long count; /* how many tests, this pass */
- unsigned long count_ceil; /* total number of tests */
- unsigned long count_run; /* total tests run */
- unsigned long a; /* LCG multiplier */
- unsigned long c; /* LCG increment */
- unsigned long m; /* LCG modulus, based on count_ceil */
-};
-
-/* Struct containing all test runner state. */
-typedef struct greatest_run_info {
- unsigned char flags;
- unsigned char verbosity;
- unsigned char running_test; /* guard for nested RUN_TEST calls */
- unsigned char exact_name_match;
-
- unsigned int tests_run; /* total test count */
-
- /* currently running test suite */
- greatest_suite_info suite;
-
- /* overall pass/fail/skip counts */
- unsigned int passed;
- unsigned int failed;
- unsigned int skipped;
- unsigned int assertions;
-
- /* info to print about the most recent failure */
- unsigned int fail_line;
- unsigned int pad_1;
- const char *fail_file;
- const char *msg;
-
- /* current setup/teardown hooks and userdata */
- greatest_setup_cb *setup;
- void *setup_udata;
- greatest_teardown_cb *teardown;
- void *teardown_udata;
-
- /* formatting info for ".....s...F"-style output */
- unsigned int col;
- unsigned int width;
-
- /* only run a specific suite or test */
- const char *suite_filter;
- const char *test_filter;
- const char *test_exclude;
- const char *name_suffix; /* print suffix with test name */
- char name_buf[GREATEST_TESTNAME_BUF_SIZE];
-
- struct greatest_prng prng[2]; /* 0: suites, 1: tests */
-
-#if GREATEST_USE_TIME
- /* overall timers */
- clock_t begin;
- clock_t end;
-#endif
-
-#if GREATEST_USE_LONGJMP
- int pad_jmp_buf;
- unsigned char pad_2[4];
- jmp_buf jump_dest;
-#endif
-} greatest_run_info;
-
-struct greatest_report_t {
- /* overall pass/fail/skip counts */
- unsigned int passed;
- unsigned int failed;
- unsigned int skipped;
- unsigned int assertions;
-};
-
-/* Global var for the current testing context.
- * Initialized by GREATEST_MAIN_DEFS(). */
-extern greatest_run_info greatest_info;
-
-/* Type for ASSERT_ENUM_EQ's ENUM_STR argument. */
-typedef const char *greatest_enum_str_fun(int value);
-
-
-/**********************
- * Exported functions *
- **********************/
-
-/* These are used internally by greatest macros. */
-int greatest_test_pre(const char *name);
-void greatest_test_post(int res);
-int greatest_do_assert_equal_t(const void *expd, const void *got,
- greatest_type_info *type_info, void *udata);
-void greatest_prng_init_first_pass(int id);
-int greatest_prng_init_second_pass(int id, unsigned long seed);
-void greatest_prng_step(int id);
-
-/* These are part of the public greatest API. */
-void GREATEST_SET_SETUP_CB(greatest_setup_cb *cb, void *udata);
-void GREATEST_SET_TEARDOWN_CB(greatest_teardown_cb *cb, void *udata);
-void GREATEST_INIT(void);
-void GREATEST_PRINT_REPORT(void);
-int greatest_all_passed(void);
-void greatest_set_suite_filter(const char *filter);
-void greatest_set_test_filter(const char *filter);
-void greatest_set_test_exclude(const char *filter);
-void greatest_set_exact_name_match(void);
-void greatest_stop_at_first_fail(void);
-void greatest_abort_on_fail(void);
-void greatest_list_only(void);
-void greatest_get_report(struct greatest_report_t *report);
-unsigned int greatest_get_verbosity(void);
-void greatest_set_verbosity(unsigned int verbosity);
-void greatest_set_flag(greatest_flag_t flag);
-void greatest_set_test_suffix(const char *suffix);
-
-
-/********************
-* Language Support *
-********************/
-
-/* If __VA_ARGS__ (C99) is supported, allow parametric testing
-* without needing to manually manage the argument struct. */
-#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 19901L) || \
- (defined(_MSC_VER) && _MSC_VER >= 1800)
-#define GREATEST_VA_ARGS
-#endif
-
-
-/**********
- * Macros *
- **********/
-
-/* Define a suite. (The duplication is intentional -- it eliminates
- * a warning from -Wmissing-declarations.) */
-#define GREATEST_SUITE(NAME) void NAME(void); void NAME(void)
-
-/* Declare a suite, provided by another compilation unit. */
-#define GREATEST_SUITE_EXTERN(NAME) void NAME(void)
-
-/* Start defining a test function.
- * The arguments are not included, to allow parametric testing. */
-#define GREATEST_TEST static enum greatest_test_res
-
-/* PASS/FAIL/SKIP result from a test. Used internally. */
-typedef enum greatest_test_res {
- GREATEST_TEST_RES_PASS = 0,
- GREATEST_TEST_RES_FAIL = -1,
- GREATEST_TEST_RES_SKIP = 1
-} greatest_test_res;
-
-/* Run a suite. */
-#define GREATEST_RUN_SUITE(S_NAME) greatest_run_suite(S_NAME, #S_NAME)
-
-/* Run a test in the current suite. */
-#define GREATEST_RUN_TEST(TEST) \
- do { \
- if (greatest_test_pre(#TEST) == 1) { \
- enum greatest_test_res res = GREATEST_SAVE_CONTEXT(); \
- if (res == GREATEST_TEST_RES_PASS) { \
- res = TEST(); \
- } \
- greatest_test_post(res); \
- } \
- } while (0)
-
-/* Ignore a test, don't warn about it being unused. */
-#define GREATEST_IGNORE_TEST(TEST) (void)TEST
-
-/* Run a test in the current suite with one void * argument,
- * which can be a pointer to a struct with multiple arguments. */
-#define GREATEST_RUN_TEST1(TEST, ENV) \
- do { \
- if (greatest_test_pre(#TEST) == 1) { \
- enum greatest_test_res res = GREATEST_SAVE_CONTEXT(); \
- if (res == GREATEST_TEST_RES_PASS) { \
- res = TEST(ENV); \
- } \
- greatest_test_post(res); \
- } \
- } while (0)
-
-#ifdef GREATEST_VA_ARGS
-#define GREATEST_RUN_TESTp(TEST, ...) \
- do { \
- if (greatest_test_pre(#TEST) == 1) { \
- enum greatest_test_res res = GREATEST_SAVE_CONTEXT(); \
- if (res == GREATEST_TEST_RES_PASS) { \
- res = TEST(__VA_ARGS__); \
- } \
- greatest_test_post(res); \
- } \
- } while (0)
-#endif
-
-
-/* Check if the test runner is in verbose mode. */
-#define GREATEST_IS_VERBOSE() ((greatest_info.verbosity) > 0)
-#define GREATEST_LIST_ONLY() \
- (greatest_info.flags & GREATEST_FLAG_LIST_ONLY)
-#define GREATEST_FIRST_FAIL() \
- (greatest_info.flags & GREATEST_FLAG_FIRST_FAIL)
-#define GREATEST_ABORT_ON_FAIL() \
- (greatest_info.flags & GREATEST_FLAG_ABORT_ON_FAIL)
-#define GREATEST_FAILURE_ABORT() \
- (GREATEST_FIRST_FAIL() && \
- (greatest_info.suite.failed > 0 || greatest_info.failed > 0))
-
-/* Message-less forms of tests defined below. */
-#define GREATEST_PASS() GREATEST_PASSm(NULL)
-#define GREATEST_FAIL() GREATEST_FAILm(NULL)
-#define GREATEST_SKIP() GREATEST_SKIPm(NULL)
-#define GREATEST_ASSERT(COND) \
- GREATEST_ASSERTm(#COND, COND)
-#define GREATEST_ASSERT_OR_LONGJMP(COND) \
- GREATEST_ASSERT_OR_LONGJMPm(#COND, COND)
-#define GREATEST_ASSERT_FALSE(COND) \
- GREATEST_ASSERT_FALSEm(#COND, COND)
-#define GREATEST_ASSERT_EQ(EXP, GOT) \
- GREATEST_ASSERT_EQm(#EXP " != " #GOT, EXP, GOT)
-#define GREATEST_ASSERT_NEQ(EXP, GOT) \
- GREATEST_ASSERT_NEQm(#EXP " == " #GOT, EXP, GOT)
-#define GREATEST_ASSERT_GT(EXP, GOT) \
- GREATEST_ASSERT_GTm(#EXP " <= " #GOT, EXP, GOT)
-#define GREATEST_ASSERT_GTE(EXP, GOT) \
- GREATEST_ASSERT_GTEm(#EXP " < " #GOT, EXP, GOT)
-#define GREATEST_ASSERT_LT(EXP, GOT) \
- GREATEST_ASSERT_LTm(#EXP " >= " #GOT, EXP, GOT)
-#define GREATEST_ASSERT_LTE(EXP, GOT) \
- GREATEST_ASSERT_LTEm(#EXP " > " #GOT, EXP, GOT)
-#define GREATEST_ASSERT_EQ_FMT(EXP, GOT, FMT) \
- GREATEST_ASSERT_EQ_FMTm(#EXP " != " #GOT, EXP, GOT, FMT)
-#define GREATEST_ASSERT_IN_RANGE(EXP, GOT, TOL) \
- GREATEST_ASSERT_IN_RANGEm(#EXP " != " #GOT " +/- " #TOL, EXP, GOT, TOL)
-#define GREATEST_ASSERT_EQUAL_T(EXP, GOT, TYPE_INFO, UDATA) \
- GREATEST_ASSERT_EQUAL_Tm(#EXP " != " #GOT, EXP, GOT, TYPE_INFO, UDATA)
-#define GREATEST_ASSERT_STR_EQ(EXP, GOT) \
- GREATEST_ASSERT_STR_EQm(#EXP " != " #GOT, EXP, GOT)
-#define GREATEST_ASSERT_STRN_EQ(EXP, GOT, SIZE) \
- GREATEST_ASSERT_STRN_EQm(#EXP " != " #GOT, EXP, GOT, SIZE)
-#define GREATEST_ASSERT_MEM_EQ(EXP, GOT, SIZE) \
- GREATEST_ASSERT_MEM_EQm(#EXP " != " #GOT, EXP, GOT, SIZE)
-#define GREATEST_ASSERT_ENUM_EQ(EXP, GOT, ENUM_STR) \
- GREATEST_ASSERT_ENUM_EQm(#EXP " != " #GOT, EXP, GOT, ENUM_STR)
-
-/* The following forms take an additional message argument first,
- * to be displayed by the test runner. */
-
-/* Fail if a condition is not true, with message. */
-#define GREATEST_ASSERTm(MSG, COND) \
- do { \
- greatest_info.assertions++; \
- if (!(COND)) { GREATEST_FAILm(MSG); } \
- } while (0)
-
-/* Fail if a condition is not true, longjmping out of test. */
-#define GREATEST_ASSERT_OR_LONGJMPm(MSG, COND) \
- do { \
- greatest_info.assertions++; \
- if (!(COND)) { GREATEST_FAIL_WITH_LONGJMPm(MSG); } \
- } while (0)
-
-/* Fail if a condition is not false, with message. */
-#define GREATEST_ASSERT_FALSEm(MSG, COND) \
- do { \
- greatest_info.assertions++; \
- if ((COND)) { GREATEST_FAILm(MSG); } \
- } while (0)
-
-/* Internal macro for relational assertions */
-#define GREATEST__REL(REL, MSG, EXP, GOT) \
- do { \
- greatest_info.assertions++; \
- if (!((EXP) REL (GOT))) { GREATEST_FAILm(MSG); } \
- } while (0)
-
-/* Fail if EXP is not ==, !=, >, <, >=, or <= to GOT. */
-#define GREATEST_ASSERT_EQm(MSG,E,G) GREATEST__REL(==, MSG,E,G)
-#define GREATEST_ASSERT_NEQm(MSG,E,G) GREATEST__REL(!=, MSG,E,G)
-#define GREATEST_ASSERT_GTm(MSG,E,G) GREATEST__REL(>, MSG,E,G)
-#define GREATEST_ASSERT_GTEm(MSG,E,G) GREATEST__REL(>=, MSG,E,G)
-#define GREATEST_ASSERT_LTm(MSG,E,G) GREATEST__REL(<, MSG,E,G)
-#define GREATEST_ASSERT_LTEm(MSG,E,G) GREATEST__REL(<=, MSG,E,G)
-
-/* Fail if EXP != GOT (equality comparison by ==).
- * Warning: FMT, EXP, and GOT will be evaluated more
- * than once on failure. */
-#define GREATEST_ASSERT_EQ_FMTm(MSG, EXP, GOT, FMT) \
- do { \
- greatest_info.assertions++; \
- if ((EXP) != (GOT)) { \
- GREATEST_FPRINTF(GREATEST_STDOUT, "\nExpected: "); \
- GREATEST_FPRINTF(GREATEST_STDOUT, FMT, EXP); \
- GREATEST_FPRINTF(GREATEST_STDOUT, "\n Got: "); \
- GREATEST_FPRINTF(GREATEST_STDOUT, FMT, GOT); \
- GREATEST_FPRINTF(GREATEST_STDOUT, "\n"); \
- GREATEST_FAILm(MSG); \
- } \
- } while (0)
-
-/* Fail if EXP is not equal to GOT, printing enum IDs. */
-#define GREATEST_ASSERT_ENUM_EQm(MSG, EXP, GOT, ENUM_STR) \
- do { \
- int greatest_EXP = (int)(EXP); \
- int greatest_GOT = (int)(GOT); \
- greatest_enum_str_fun *greatest_ENUM_STR = ENUM_STR; \
- if (greatest_EXP != greatest_GOT) { \
- GREATEST_FPRINTF(GREATEST_STDOUT, "\nExpected: %s", \
- greatest_ENUM_STR(greatest_EXP)); \
- GREATEST_FPRINTF(GREATEST_STDOUT, "\n Got: %s\n", \
- greatest_ENUM_STR(greatest_GOT)); \
- GREATEST_FAILm(MSG); \
- } \
- } while (0) \
-
-/* Fail if GOT not in range of EXP +|- TOL. */
-#define GREATEST_ASSERT_IN_RANGEm(MSG, EXP, GOT, TOL) \
- do { \
- GREATEST_FLOAT greatest_EXP = (EXP); \
- GREATEST_FLOAT greatest_GOT = (GOT); \
- GREATEST_FLOAT greatest_TOL = (TOL); \
- greatest_info.assertions++; \
- if ((greatest_EXP > greatest_GOT && \
- greatest_EXP - greatest_GOT > greatest_TOL) || \
- (greatest_EXP < greatest_GOT && \
- greatest_GOT - greatest_EXP > greatest_TOL)) { \
- GREATEST_FPRINTF(GREATEST_STDOUT, \
- "\nExpected: " GREATEST_FLOAT_FMT \
- " +/- " GREATEST_FLOAT_FMT \
- "\n Got: " GREATEST_FLOAT_FMT \
- "\n", \
- greatest_EXP, greatest_TOL, greatest_GOT); \
- GREATEST_FAILm(MSG); \
- } \
- } while (0)
-
-/* Fail if EXP is not equal to GOT, according to strcmp. */
-#define GREATEST_ASSERT_STR_EQm(MSG, EXP, GOT) \
- do { \
- GREATEST_ASSERT_EQUAL_Tm(MSG, EXP, GOT, \
- &greatest_type_info_string, NULL); \
- } while (0) \
-
-/* Fail if EXP is not equal to GOT, according to strncmp. */
-#define GREATEST_ASSERT_STRN_EQm(MSG, EXP, GOT, SIZE) \
- do { \
- size_t size = SIZE; \
- GREATEST_ASSERT_EQUAL_Tm(MSG, EXP, GOT, \
- &greatest_type_info_string, &size); \
- } while (0) \
-
-/* Fail if EXP is not equal to GOT, according to memcmp. */
-#define GREATEST_ASSERT_MEM_EQm(MSG, EXP, GOT, SIZE) \
- do { \
- greatest_memory_cmp_env env; \
- env.exp = (const unsigned char *)EXP; \
- env.got = (const unsigned char *)GOT; \
- env.size = SIZE; \
- GREATEST_ASSERT_EQUAL_Tm(MSG, env.exp, env.got, \
- &greatest_type_info_memory, &env); \
- } while (0) \
-
-/* Fail if EXP is not equal to GOT, according to a comparison
- * callback in TYPE_INFO. If they are not equal, optionally use a
- * print callback in TYPE_INFO to print them. */
-#define GREATEST_ASSERT_EQUAL_Tm(MSG, EXP, GOT, TYPE_INFO, UDATA) \
- do { \
- greatest_type_info *type_info = (TYPE_INFO); \
- greatest_info.assertions++; \
- if (!greatest_do_assert_equal_t(EXP, GOT, \
- type_info, UDATA)) { \
- if (type_info == NULL || type_info->equal == NULL) { \
- GREATEST_FAILm("type_info->equal callback missing!"); \
- } else { \
- GREATEST_FAILm(MSG); \
- } \
- } \
- } while (0) \
-
-/* Pass. */
-#define GREATEST_PASSm(MSG) \
- do { \
- greatest_info.msg = MSG; \
- return GREATEST_TEST_RES_PASS; \
- } while (0)
-
-/* Fail. */
-#define GREATEST_FAILm(MSG) \
- do { \
- greatest_info.fail_file = __FILE__; \
- greatest_info.fail_line = __LINE__; \
- greatest_info.msg = MSG; \
- if (GREATEST_ABORT_ON_FAIL()) { abort(); } \
- return GREATEST_TEST_RES_FAIL; \
- } while (0)
-
-/* Optional GREATEST_FAILm variant that longjmps. */
-#if GREATEST_USE_LONGJMP
-#define GREATEST_FAIL_WITH_LONGJMP() GREATEST_FAIL_WITH_LONGJMPm(NULL)
-#define GREATEST_FAIL_WITH_LONGJMPm(MSG) \
- do { \
- greatest_info.fail_file = __FILE__; \
- greatest_info.fail_line = __LINE__; \
- greatest_info.msg = MSG; \
- longjmp(greatest_info.jump_dest, GREATEST_TEST_RES_FAIL); \
- } while (0)
-#endif
-
-/* Skip the current test. */
-#define GREATEST_SKIPm(MSG) \
- do { \
- greatest_info.msg = MSG; \
- return GREATEST_TEST_RES_SKIP; \
- } while (0)
-
-/* Check the result of a subfunction using ASSERT, etc. */
-#define GREATEST_CHECK_CALL(RES) \
- do { \
- enum greatest_test_res greatest_RES = RES; \
- if (greatest_RES != GREATEST_TEST_RES_PASS) { \
- return greatest_RES; \
- } \
- } while (0) \
-
-#if GREATEST_USE_TIME
-#define GREATEST_SET_TIME(NAME) \
- NAME = clock(); \
- if (NAME == (clock_t) -1) { \
- GREATEST_FPRINTF(GREATEST_STDOUT, \
- "clock error: %s\n", #NAME); \
- exit(EXIT_FAILURE); \
- }
-
-#define GREATEST_CLOCK_DIFF(C1, C2) \
- GREATEST_FPRINTF(GREATEST_STDOUT, " (%lu ticks, %.3f sec)", \
- (long unsigned int) (C2) - (long unsigned int)(C1), \
- (double)((C2) - (C1)) / (1.0 * (double)CLOCKS_PER_SEC))
-#else
-#define GREATEST_SET_TIME(UNUSED)
-#define GREATEST_CLOCK_DIFF(UNUSED1, UNUSED2)
-#endif
-
-#if GREATEST_USE_LONGJMP
-#define GREATEST_SAVE_CONTEXT() \
- /* setjmp returns 0 (GREATEST_TEST_RES_PASS) on first call * \
- * so the test runs, then RES_FAIL from FAIL_WITH_LONGJMP. */ \
- ((enum greatest_test_res)(setjmp(greatest_info.jump_dest)))
-#else
-#define GREATEST_SAVE_CONTEXT() \
- /*a no-op, since setjmp/longjmp aren't being used */ \
- GREATEST_TEST_RES_PASS
-#endif
-
-/* Run every suite / test function run within BODY in pseudo-random
- * order, seeded by SEED. (The top 3 bits of the seed are ignored.)
- *
- * This should be called like:
- * GREATEST_SHUFFLE_TESTS(seed, {
- * GREATEST_RUN_TEST(some_test);
- * GREATEST_RUN_TEST(some_other_test);
- * GREATEST_RUN_TEST(yet_another_test);
- * });
- *
- * Note that the body of the second argument will be evaluated
- * multiple times. */
-#define GREATEST_SHUFFLE_SUITES(SD, BODY) GREATEST_SHUFFLE(0, SD, BODY)
-#define GREATEST_SHUFFLE_TESTS(SD, BODY) GREATEST_SHUFFLE(1, SD, BODY)
-#define GREATEST_SHUFFLE(ID, SD, BODY) \
- do { \
- struct greatest_prng *prng = &greatest_info.prng[ID]; \
- greatest_prng_init_first_pass(ID); \
- do { \
- prng->count = 0; \
- if (prng->initialized) { greatest_prng_step(ID); } \
- BODY; \
- if (!prng->initialized) { \
- if (!greatest_prng_init_second_pass(ID, SD)) { break; } \
- } else if (prng->count_run == prng->count_ceil) { \
- break; \
- } \
- } while (!GREATEST_FAILURE_ABORT()); \
- prng->count_run = prng->random_order = prng->initialized = 0; \
- } while(0)
-
-/* Include several function definitions in the main test file. */
-#define GREATEST_MAIN_DEFS() \
- \
-/* Is FILTER a subset of NAME? */ \
-static int greatest_name_match(const char *name, const char *filter, \
- int res_if_none) { \
- size_t offset = 0; \
- size_t filter_len = filter ? strlen(filter) : 0; \
- if (filter_len == 0) { return res_if_none; } /* no filter */ \
- if (greatest_info.exact_name_match && strlen(name) != filter_len) { \
- return 0; /* ignore substring matches */ \
- } \
- while (name[offset] != '\0') { \
- if (name[offset] == filter[0]) { \
- if (0 == strncmp(&name[offset], filter, filter_len)) { \
- return 1; \
- } \
- } \
- offset++; \
- } \
- \
- return 0; \
-} \
- \
-static void greatest_buffer_test_name(const char *name) { \
- struct greatest_run_info *g = &greatest_info; \
- size_t len = strlen(name), size = sizeof(g->name_buf); \
- memset(g->name_buf, 0x00, size); \
- (void)strncat(g->name_buf, name, size - 1); \
- if (g->name_suffix && (len + 1 < size)) { \
- g->name_buf[len] = '_'; \
- strncat(&g->name_buf[len+1], g->name_suffix, size-(len+2)); \
- } \
-} \
- \
-/* Before running a test, check the name filtering and \
- * test shuffling state, if applicable, and then call setup hooks. */ \
-int greatest_test_pre(const char *name) { \
- struct greatest_run_info *g = &greatest_info; \
- int match; \
- greatest_buffer_test_name(name); \
- match = greatest_name_match(g->name_buf, g->test_filter, 1) && \
- !greatest_name_match(g->name_buf, g->test_exclude, 0); \
- if (GREATEST_LIST_ONLY()) { /* just listing test names */ \
- if (match) { \
- GREATEST_FPRINTF(GREATEST_STDOUT, " %s\n", g->name_buf); \
- } \
- goto clear; \
- } \
- if (match && (!GREATEST_FIRST_FAIL() || g->suite.failed == 0)) { \
- struct greatest_prng *p = &g->prng[1]; \
- if (p->random_order) { \
- p->count++; \
- if (!p->initialized || ((p->count - 1) != p->state)) { \
- goto clear; /* don't run this test yet */ \
- } \
- } \
- if (g->running_test) { \
- fprintf(stderr, "Error: Test run inside another test.\n"); \
- return 0; \
- } \
- GREATEST_SET_TIME(g->suite.pre_test); \
- if (g->setup) { g->setup(g->setup_udata); } \
- p->count_run++; \
- g->running_test = 1; \
- return 1; /* test should be run */ \
- } else { \
- goto clear; /* skipped */ \
- } \
-clear: \
- g->name_suffix = NULL; \
- return 0; \
-} \
- \
-static void greatest_do_pass(void) { \
- struct greatest_run_info *g = &greatest_info; \
- if (GREATEST_IS_VERBOSE()) { \
- GREATEST_FPRINTF(GREATEST_STDOUT, "PASS %s: %s", \
- g->name_buf, g->msg ? g->msg : ""); \
- } else { \
- GREATEST_FPRINTF(GREATEST_STDOUT, "."); \
- } \
- g->suite.passed++; \
-} \
- \
-static void greatest_do_fail(void) { \
- struct greatest_run_info *g = &greatest_info; \
- if (GREATEST_IS_VERBOSE()) { \
- GREATEST_FPRINTF(GREATEST_STDOUT, \
- "FAIL %s: %s (%s:%u)", g->name_buf, \
- g->msg ? g->msg : "", g->fail_file, g->fail_line); \
- } else { \
- GREATEST_FPRINTF(GREATEST_STDOUT, "F"); \
- g->col++; /* add linebreak if in line of '.'s */ \
- if (g->col != 0) { \
- GREATEST_FPRINTF(GREATEST_STDOUT, "\n"); \
- g->col = 0; \
- } \
- GREATEST_FPRINTF(GREATEST_STDOUT, "FAIL %s: %s (%s:%u)\n", \
- g->name_buf, g->msg ? g->msg : "", \
- g->fail_file, g->fail_line); \
- } \
- g->suite.failed++; \
-} \
- \
-static void greatest_do_skip(void) { \
- struct greatest_run_info *g = &greatest_info; \
- if (GREATEST_IS_VERBOSE()) { \
- GREATEST_FPRINTF(GREATEST_STDOUT, "SKIP %s: %s", \
- g->name_buf, g->msg ? g->msg : ""); \
- } else { \
- GREATEST_FPRINTF(GREATEST_STDOUT, "s"); \
- } \
- g->suite.skipped++; \
-} \
- \
-void greatest_test_post(int res) { \
- GREATEST_SET_TIME(greatest_info.suite.post_test); \
- if (greatest_info.teardown) { \
- void *udata = greatest_info.teardown_udata; \
- greatest_info.teardown(udata); \
- } \
- \
- greatest_info.running_test = 0; \
- if (res <= GREATEST_TEST_RES_FAIL) { \
- greatest_do_fail(); \
- } else if (res >= GREATEST_TEST_RES_SKIP) { \
- greatest_do_skip(); \
- } else if (res == GREATEST_TEST_RES_PASS) { \
- greatest_do_pass(); \
- } \
- greatest_info.name_suffix = NULL; \
- greatest_info.suite.tests_run++; \
- greatest_info.col++; \
- if (GREATEST_IS_VERBOSE()) { \
- GREATEST_CLOCK_DIFF(greatest_info.suite.pre_test, \
- greatest_info.suite.post_test); \
- GREATEST_FPRINTF(GREATEST_STDOUT, "\n"); \
- } else if (greatest_info.col % greatest_info.width == 0) { \
- GREATEST_FPRINTF(GREATEST_STDOUT, "\n"); \
- greatest_info.col = 0; \
- } \
- fflush(GREATEST_STDOUT); \
-} \
- \
-static void report_suite(void) { \
- if (greatest_info.suite.tests_run > 0) { \
- GREATEST_FPRINTF(GREATEST_STDOUT, \
- "\n%u test%s - %u passed, %u failed, %u skipped", \
- greatest_info.suite.tests_run, \
- greatest_info.suite.tests_run == 1 ? "" : "s", \
- greatest_info.suite.passed, \
- greatest_info.suite.failed, \
- greatest_info.suite.skipped); \
- GREATEST_CLOCK_DIFF(greatest_info.suite.pre_suite, \
- greatest_info.suite.post_suite); \
- GREATEST_FPRINTF(GREATEST_STDOUT, "\n"); \
- } \
-} \
- \
-static void update_counts_and_reset_suite(void) { \
- greatest_info.setup = NULL; \
- greatest_info.setup_udata = NULL; \
- greatest_info.teardown = NULL; \
- greatest_info.teardown_udata = NULL; \
- greatest_info.passed += greatest_info.suite.passed; \
- greatest_info.failed += greatest_info.suite.failed; \
- greatest_info.skipped += greatest_info.suite.skipped; \
- greatest_info.tests_run += greatest_info.suite.tests_run; \
- memset(&greatest_info.suite, 0, sizeof(greatest_info.suite)); \
- greatest_info.col = 0; \
-} \
- \
-static int greatest_suite_pre(const char *suite_name) { \
- struct greatest_prng *p = &greatest_info.prng[0]; \
- if (!greatest_name_match(suite_name, greatest_info.suite_filter, 1) \
- || (GREATEST_FAILURE_ABORT())) { return 0; } \
- if (p->random_order) { \
- p->count++; \
- if (!p->initialized || ((p->count - 1) != p->state)) { \
- return 0; /* don't run this suite yet */ \
- } \
- } \
- p->count_run++; \
- update_counts_and_reset_suite(); \
- GREATEST_FPRINTF(GREATEST_STDOUT, "\n* Suite %s:\n", suite_name); \
- GREATEST_SET_TIME(greatest_info.suite.pre_suite); \
- return 1; \
-} \
- \
-static void greatest_suite_post(void) { \
- GREATEST_SET_TIME(greatest_info.suite.post_suite); \
- report_suite(); \
-} \
- \
-static void greatest_run_suite(greatest_suite_cb *suite_cb, \
- const char *suite_name) { \
- if (greatest_suite_pre(suite_name)) { \
- suite_cb(); \
- greatest_suite_post(); \
- } \
-} \
- \
-int greatest_do_assert_equal_t(const void *expd, const void *got, \
- greatest_type_info *type_info, void *udata) { \
- int eq = 0; \
- if (type_info == NULL || type_info->equal == NULL) { return 0; } \
- eq = type_info->equal(expd, got, udata); \
- if (!eq) { \
- if (type_info->print != NULL) { \
- GREATEST_FPRINTF(GREATEST_STDOUT, "\nExpected: "); \
- (void)type_info->print(expd, udata); \
- GREATEST_FPRINTF(GREATEST_STDOUT, "\n Got: "); \
- (void)type_info->print(got, udata); \
- GREATEST_FPRINTF(GREATEST_STDOUT, "\n"); \
- } \
- } \
- return eq; \
-} \
- \
-static void greatest_usage(const char *name) { \
- GREATEST_FPRINTF(GREATEST_STDOUT, \
- "Usage: %s [-hlfavex] [-s SUITE] [-t TEST] [-x EXCLUDE]\n" \
- " -h, --help print this Help\n" \
- " -l List suites and tests, then exit (dry run)\n" \
- " -f Stop runner after first failure\n" \
- " -a Abort on first failure (implies -f)\n" \
- " -v Verbose output\n" \
- " -s SUITE only run suites containing substring SUITE\n" \
- " -t TEST only run tests containing substring TEST\n" \
- " -e only run exact name match for -s or -t\n" \
- " -x EXCLUDE exclude tests containing substring EXCLUDE\n", \
- name); \
-} \
- \
-static void greatest_parse_options(int argc, char **argv) { \
- int i = 0; \
- for (i = 1; i < argc; i++) { \
- if (argv[i][0] == '-') { \
- char f = argv[i][1]; \
- if ((f == 's' || f == 't' || f == 'x') && argc <= i + 1) { \
- greatest_usage(argv[0]); exit(EXIT_FAILURE); \
- } \
- switch (f) { \
- case 's': /* suite name filter */ \
- greatest_set_suite_filter(argv[i + 1]); i++; break; \
- case 't': /* test name filter */ \
- greatest_set_test_filter(argv[i + 1]); i++; break; \
- case 'x': /* test name exclusion */ \
- greatest_set_test_exclude(argv[i + 1]); i++; break; \
- case 'e': /* exact name match */ \
- greatest_set_exact_name_match(); break; \
- case 'f': /* first fail flag */ \
- greatest_stop_at_first_fail(); break; \
- case 'a': /* abort() on fail flag */ \
- greatest_abort_on_fail(); break; \
- case 'l': /* list only (dry run) */ \
- greatest_list_only(); break; \
- case 'v': /* first fail flag */ \
- greatest_info.verbosity++; break; \
- case 'h': /* help */ \
- greatest_usage(argv[0]); exit(EXIT_SUCCESS); \
- default: \
- case '-': \
- if (0 == strncmp("--help", argv[i], 6)) { \
- greatest_usage(argv[0]); exit(EXIT_SUCCESS); \
- } else if (0 == strcmp("--", argv[i])) { \
- return; /* ignore following arguments */ \
- } \
- GREATEST_FPRINTF(GREATEST_STDOUT, \
- "Unknown argument '%s'\n", argv[i]); \
- greatest_usage(argv[0]); \
- exit(EXIT_FAILURE); \
- } \
- } \
- } \
-} \
- \
-int greatest_all_passed(void) { return (greatest_info.failed == 0); } \
- \
-void greatest_set_test_filter(const char *filter) { \
- greatest_info.test_filter = filter; \
-} \
- \
-void greatest_set_test_exclude(const char *filter) { \
- greatest_info.test_exclude = filter; \
-} \
- \
-void greatest_set_suite_filter(const char *filter) { \
- greatest_info.suite_filter = filter; \
-} \
- \
-void greatest_set_exact_name_match(void) { \
- greatest_info.exact_name_match = 1; \
-} \
- \
-void greatest_stop_at_first_fail(void) { \
- greatest_set_flag(GREATEST_FLAG_FIRST_FAIL); \
-} \
- \
-void greatest_abort_on_fail(void) { \
- greatest_set_flag(GREATEST_FLAG_ABORT_ON_FAIL); \
-} \
- \
-void greatest_list_only(void) { \
- greatest_set_flag(GREATEST_FLAG_LIST_ONLY); \
-} \
- \
-void greatest_get_report(struct greatest_report_t *report) { \
- if (report) { \
- report->passed = greatest_info.passed; \
- report->failed = greatest_info.failed; \
- report->skipped = greatest_info.skipped; \
- report->assertions = greatest_info.assertions; \
- } \
-} \
- \
-unsigned int greatest_get_verbosity(void) { \
- return greatest_info.verbosity; \
-} \
- \
-void greatest_set_verbosity(unsigned int verbosity) { \
- greatest_info.verbosity = (unsigned char)verbosity; \
-} \
- \
-void greatest_set_flag(greatest_flag_t flag) { \
- greatest_info.flags = (unsigned char)(greatest_info.flags | flag); \
-} \
- \
-void greatest_set_test_suffix(const char *suffix) { \
- greatest_info.name_suffix = suffix; \
-} \
- \
-void GREATEST_SET_SETUP_CB(greatest_setup_cb *cb, void *udata) { \
- greatest_info.setup = cb; \
- greatest_info.setup_udata = udata; \
-} \
- \
-void GREATEST_SET_TEARDOWN_CB(greatest_teardown_cb *cb, void *udata) { \
- greatest_info.teardown = cb; \
- greatest_info.teardown_udata = udata; \
-} \
- \
-static int greatest_string_equal_cb(const void *expd, const void *got, \
- void *udata) { \
- size_t *size = (size_t *)udata; \
- return (size != NULL \
- ? (0 == strncmp((const char *)expd, (const char *)got, *size)) \
- : (0 == strcmp((const char *)expd, (const char *)got))); \
-} \
- \
-static int greatest_string_printf_cb(const void *t, void *udata) { \
- (void)udata; /* note: does not check \0 termination. */ \
- return GREATEST_FPRINTF(GREATEST_STDOUT, "%s", (const char *)t); \
-} \
- \
-greatest_type_info greatest_type_info_string = { \
- greatest_string_equal_cb, greatest_string_printf_cb, \
-}; \
- \
-static int greatest_memory_equal_cb(const void *expd, const void *got, \
- void *udata) { \
- greatest_memory_cmp_env *env = (greatest_memory_cmp_env *)udata; \
- return (0 == memcmp(expd, got, env->size)); \
-} \
- \
-/* Hexdump raw memory, with differences highlighted */ \
-static int greatest_memory_printf_cb(const void *t, void *udata) { \
- greatest_memory_cmp_env *env = (greatest_memory_cmp_env *)udata; \
- const unsigned char *buf = (const unsigned char *)t; \
- unsigned char diff_mark = ' '; \
- FILE *out = GREATEST_STDOUT; \
- size_t i, line_i, line_len = 0; \
- int len = 0; /* format hexdump with differences highlighted */ \
- for (i = 0; i < env->size; i+= line_len) { \
- diff_mark = ' '; \
- line_len = env->size - i; \
- if (line_len > 16) { line_len = 16; } \
- for (line_i = i; line_i < i + line_len; line_i++) { \
- if (env->exp[line_i] != env->got[line_i]) diff_mark = 'X'; \
- } \
- len += GREATEST_FPRINTF(out, "\n%04x %c ", \
- (unsigned int)i, diff_mark); \
- for (line_i = i; line_i < i + line_len; line_i++) { \
- int m = env->exp[line_i] == env->got[line_i]; /* match? */ \
- len += GREATEST_FPRINTF(out, "%02x%c", \
- buf[line_i], m ? ' ' : '<'); \
- } \
- for (line_i = 0; line_i < 16 - line_len; line_i++) { \
- len += GREATEST_FPRINTF(out, " "); \
- } \
- GREATEST_FPRINTF(out, " "); \
- for (line_i = i; line_i < i + line_len; line_i++) { \
- unsigned char c = buf[line_i]; \
- len += GREATEST_FPRINTF(out, "%c", isprint(c) ? c : '.'); \
- } \
- } \
- len += GREATEST_FPRINTF(out, "\n"); \
- return len; \
-} \
- \
-void greatest_prng_init_first_pass(int id) { \
- greatest_info.prng[id].random_order = 1; \
- greatest_info.prng[id].count_run = 0; \
-} \
- \
-int greatest_prng_init_second_pass(int id, unsigned long seed) { \
- struct greatest_prng *p = &greatest_info.prng[id]; \
- if (p->count == 0) { return 0; } \
- p->count_ceil = p->count; \
- for (p->m = 1; p->m < p->count; p->m <<= 1) {} \
- p->state = seed & 0x1fffffff; /* only use lower 29 bits */ \
- p->a = 4LU * p->state; /* to avoid overflow when */ \
- p->a = (p->a ? p->a : 4) | 1; /* multiplied by 4 */ \
- p->c = 2147483647; /* and so p->c ((2 ** 31) - 1) is */ \
- p->initialized = 1; /* always relatively prime to p->a. */ \
- fprintf(stderr, "init_second_pass: a %lu, c %lu, state %lu\n", \
- p->a, p->c, p->state); \
- return 1; \
-} \
- \
-/* Step the pseudorandom number generator until its state reaches \
- * another test ID between 0 and the test count. \
- * This use a linear congruential pseudorandom number generator, \
- * with the power-of-two ceiling of the test count as the modulus, the \
- * masked seed as the multiplier, and a prime as the increment. For \
- * each generated value < the test count, run the corresponding test. \
- * This will visit all IDs 0 <= X < mod once before repeating, \
- * with a starting position chosen based on the initial seed. \
- * For details, see: Knuth, The Art of Computer Programming \
- * Volume. 2, section 3.2.1. */ \
-void greatest_prng_step(int id) { \
- struct greatest_prng *p = &greatest_info.prng[id]; \
- do { \
- p->state = ((p->a * p->state) + p->c) & (p->m - 1); \
- } while (p->state >= p->count_ceil); \
-} \
- \
-void GREATEST_INIT(void) { \
- /* Suppress unused function warning if features aren't used */ \
- (void)greatest_run_suite; \
- (void)greatest_parse_options; \
- (void)greatest_prng_step; \
- (void)greatest_prng_init_first_pass; \
- (void)greatest_prng_init_second_pass; \
- (void)greatest_set_test_suffix; \
- \
- memset(&greatest_info, 0, sizeof(greatest_info)); \
- greatest_info.width = GREATEST_DEFAULT_WIDTH; \
- GREATEST_SET_TIME(greatest_info.begin); \
-} \
- \
-/* Report passes, failures, skipped tests, the number of \
- * assertions, and the overall run time. */ \
-void GREATEST_PRINT_REPORT(void) { \
- if (!GREATEST_LIST_ONLY()) { \
- update_counts_and_reset_suite(); \
- GREATEST_SET_TIME(greatest_info.end); \
- GREATEST_FPRINTF(GREATEST_STDOUT, \
- "\nTotal: %u test%s", \
- greatest_info.tests_run, \
- greatest_info.tests_run == 1 ? "" : "s"); \
- GREATEST_CLOCK_DIFF(greatest_info.begin, \
- greatest_info.end); \
- GREATEST_FPRINTF(GREATEST_STDOUT, ", %u assertion%s\n", \
- greatest_info.assertions, \
- greatest_info.assertions == 1 ? "" : "s"); \
- GREATEST_FPRINTF(GREATEST_STDOUT, \
- "Pass: %u, fail: %u, skip: %u.\n", \
- greatest_info.passed, \
- greatest_info.failed, greatest_info.skipped); \
- } \
-} \
- \
-greatest_type_info greatest_type_info_memory = { \
- greatest_memory_equal_cb, greatest_memory_printf_cb, \
-}; \
- \
-greatest_run_info greatest_info
-
-/* Handle command-line arguments, etc. */
-#define GREATEST_MAIN_BEGIN() \
- do { \
- GREATEST_INIT(); \
- greatest_parse_options(argc, argv); \
- } while (0)
-
-/* Report results, exit with exit status based on results. */
-#define GREATEST_MAIN_END() \
- do { \
- GREATEST_PRINT_REPORT(); \
- return (greatest_all_passed() ? EXIT_SUCCESS : EXIT_FAILURE); \
- } while (0)
-
-/* Make abbreviations without the GREATEST_ prefix for the
- * most commonly used symbols. */
-#if GREATEST_USE_ABBREVS
-#define TEST GREATEST_TEST
-#define SUITE GREATEST_SUITE
-#define SUITE_EXTERN GREATEST_SUITE_EXTERN
-#define RUN_TEST GREATEST_RUN_TEST
-#define RUN_TEST1 GREATEST_RUN_TEST1
-#define RUN_SUITE GREATEST_RUN_SUITE
-#define IGNORE_TEST GREATEST_IGNORE_TEST
-#define ASSERT GREATEST_ASSERT
-#define ASSERTm GREATEST_ASSERTm
-#define ASSERT_FALSE GREATEST_ASSERT_FALSE
-#define ASSERT_EQ GREATEST_ASSERT_EQ
-#define ASSERT_NEQ GREATEST_ASSERT_NEQ
-#define ASSERT_GT GREATEST_ASSERT_GT
-#define ASSERT_GTE GREATEST_ASSERT_GTE
-#define ASSERT_LT GREATEST_ASSERT_LT
-#define ASSERT_LTE GREATEST_ASSERT_LTE
-#define ASSERT_EQ_FMT GREATEST_ASSERT_EQ_FMT
-#define ASSERT_IN_RANGE GREATEST_ASSERT_IN_RANGE
-#define ASSERT_EQUAL_T GREATEST_ASSERT_EQUAL_T
-#define ASSERT_STR_EQ GREATEST_ASSERT_STR_EQ
-#define ASSERT_STRN_EQ GREATEST_ASSERT_STRN_EQ
-#define ASSERT_MEM_EQ GREATEST_ASSERT_MEM_EQ
-#define ASSERT_ENUM_EQ GREATEST_ASSERT_ENUM_EQ
-#define ASSERT_FALSEm GREATEST_ASSERT_FALSEm
-#define ASSERT_EQm GREATEST_ASSERT_EQm
-#define ASSERT_NEQm GREATEST_ASSERT_NEQm
-#define ASSERT_GTm GREATEST_ASSERT_GTm
-#define ASSERT_GTEm GREATEST_ASSERT_GTEm
-#define ASSERT_LTm GREATEST_ASSERT_LTm
-#define ASSERT_LTEm GREATEST_ASSERT_LTEm
-#define ASSERT_EQ_FMTm GREATEST_ASSERT_EQ_FMTm
-#define ASSERT_IN_RANGEm GREATEST_ASSERT_IN_RANGEm
-#define ASSERT_EQUAL_Tm GREATEST_ASSERT_EQUAL_Tm
-#define ASSERT_STR_EQm GREATEST_ASSERT_STR_EQm
-#define ASSERT_STRN_EQm GREATEST_ASSERT_STRN_EQm
-#define ASSERT_MEM_EQm GREATEST_ASSERT_MEM_EQm
-#define ASSERT_ENUM_EQm GREATEST_ASSERT_ENUM_EQm
-#define PASS GREATEST_PASS
-#define FAIL GREATEST_FAIL
-#define SKIP GREATEST_SKIP
-#define PASSm GREATEST_PASSm
-#define FAILm GREATEST_FAILm
-#define SKIPm GREATEST_SKIPm
-#define SET_SETUP GREATEST_SET_SETUP_CB
-#define SET_TEARDOWN GREATEST_SET_TEARDOWN_CB
-#define CHECK_CALL GREATEST_CHECK_CALL
-#define SHUFFLE_TESTS GREATEST_SHUFFLE_TESTS
-#define SHUFFLE_SUITES GREATEST_SHUFFLE_SUITES
-
-#ifdef GREATEST_VA_ARGS
-#define RUN_TESTp GREATEST_RUN_TESTp
-#endif
-
-#if GREATEST_USE_LONGJMP
-#define ASSERT_OR_LONGJMP GREATEST_ASSERT_OR_LONGJMP
-#define ASSERT_OR_LONGJMPm GREATEST_ASSERT_OR_LONGJMPm
-#define FAIL_WITH_LONGJMP GREATEST_FAIL_WITH_LONGJMP
-#define FAIL_WITH_LONGJMPm GREATEST_FAIL_WITH_LONGJMPm
-#endif
-
-#endif /* USE_ABBREVS */
-
-#if defined(__cplusplus) && !defined(GREATEST_NO_EXTERN_CPLUSPLUS)
-}
-#endif
-
-#endif
\ No newline at end of file
diff --git a/framework/3rd/minicoro/minicoro.h b/framework/3rd/minicoro/minicoro.h
deleted file mode 100644
index 9403a2d..0000000
--- a/framework/3rd/minicoro/minicoro.h
+++ /dev/null
@@ -1,1789 +0,0 @@
-/*
-Minimal asymmetric stackful cross-platform coroutine library in pure C.
-minicoro - v0.1.2 - 13/Feb/2021
-Eduardo Bart - edub4rt@gmail.com
-https://github.com/edubart/minicoro
-
-Minicoro is single file library for using asymmetric coroutines in C.
-The API is inspired by Lua coroutines but with C use in mind.
-
-# Features
-
-- Stackful asymmetric coroutines.
-- Supports nesting coroutines (resuming a coroutine from another coroutine).
-- Supports custom allocators.
-- Storage system to allow passing values between yield and resume.
-- Customizable stack size.
-- Coroutine API design inspired by Lua with C use in mind.
-- Yield across any C function.
-- Made to work in multithread applications.
-- Cross platform.
-- Minimal, self contained and no external dependencies.
-- Readable sources and documented.
-- Implemented via assembly, ucontext or fibers.
-- Lightweight and very efficient.
-- Works in most C89 compilers.
-- Error prone API, returning proper error codes on misuse.
-- Support running with Valgrind, ASan (AddressSanitizer) and TSan (ThreadSanitizer).
-
-# Supported Platforms
-
-Most platforms are supported through different methods:
-
-| Platform | Assembly Method | Fallback Method |
-|--------------|------------------|-------------------|
-| Android | ARM/ARM64 | N/A |
-| Windows | x86_64 | Windows fibers |
-| Linux | x86_64/i686 | ucontext |
-| Mac OS X | x86_64 | ucontext |
-| Browser | N/A | Emscripten fibers |
-| Raspberry Pi | ARM | ucontext |
-| RISC-V | rv64/rv32 | ucontext |
-
-The assembly method is used by default if supported by the compiler and CPU,
-otherwise ucontext or fiber method is used as a fallback.
-
-The assembly method is very efficient, it just take a few cycles
-to create, resume, yield or destroy a coroutine.
-
-# Caveats
-
-- Don't use coroutines with C++ exceptions, this is not supported.
-- When using C++ RAII (i.e. destructors) you must resume the coroutine until it dies to properly execute all destructors.
-- To use in multithread applications, you must compile with C compiler that supports `thread_local` qualifier.
-- Some unsupported sanitizers for C may trigger false warnings when using coroutines.
-- The `mco_coro` object is not thread safe, you should lock each coroutine into a thread.
-- Stack space is fixed, it cannot grow. By default it has about 56KB of space, this can be changed on coroutine creation.
-- Take care to not cause stack overflows (run out of stack space), otherwise your program may crash or not, the behavior is undefined.
-- On WebAssembly you must compile with emscripten flag `-s ASYNCIFY=1`.
-
-# Introduction
-
-A coroutine represents an independent "green" thread of execution.
-Unlike threads in multithread systems, however,
-a coroutine only suspends its execution by explicitly calling a yield function.
-
-You create a coroutine by calling `mco_create`.
-Its sole argument is a `mco_desc` structure with a description for the coroutine.
-The `mco_create` function only creates a new coroutine and returns a handle to it, it does not start the coroutine.
-
-You execute a coroutine by calling `mco_resume`.
-When calling a resume function the coroutine starts its execution by calling its body function.
-After the coroutine starts running, it runs until it terminates or yields.
-
-A coroutine yields by calling `mco_yield`.
-When a coroutine yields, the corresponding resume returns immediately,
-even if the yield happens inside nested function calls (that is, not in the main function).
-The next time you resume the same coroutine, it continues its execution from the point where it yielded.
-
-To associate a persistent value with the coroutine,
-you can optionally set `user_data` on its creation and later retrieve with `mco_get_user_data`.
-
-To pass values between resume and yield,
-you can optionally use `mco_push` and `mco_pop` APIs,
-they are intended to pass temporary values using a LIFO style buffer.
-The storage system can also be used to send and receive initial values on coroutine creation or before it finishes.
-
-# Usage
-
-To use minicoro, do the following in one .c file:
-
-```c
-#define MINICORO_IMPL
-#include "minicoro.h"
-```
-
-You can do `#include "minicoro.h"` in other parts of the program just like any other header.
-
-## Minimal Example
-
-The following simple example demonstrates on how to use the library:
-
-```c
-#define MINICORO_IMPL
-#include "minicoro.h"
-#include
-
-// Coroutine entry function.
-void coro_entry(mco_coro* co) {
- printf("coroutine 1\n");
- mco_yield(co);
- printf("coroutine 2\n");
-}
-
-int main() {
- // First initialize a `desc` object through `mco_desc_init`.
- mco_desc desc = mco_desc_init(coro_entry, 0);
- // Configure `desc` fields when needed (e.g. customize user_data or allocation functions).
- desc.user_data = NULL;
- // Call `mco_create` with the output coroutine pointer and `desc` pointer.
- mco_coro* co;
- mco_result res = mco_create(&co, &desc);
- assert(res == MCO_SUCCESS);
- // The coroutine should be now in suspended state.
- assert(mco_status(co) == MCO_SUSPENDED);
- // Call `mco_resume` to start for the first time, switching to its context.
- res = mco_resume(co); // Should print "coroutine 1".
- assert(res == MCO_SUCCESS);
- // We get back from coroutine context in suspended state (because it's unfinished).
- assert(mco_status(co) == MCO_SUSPENDED);
- // Call `mco_resume` to resume for a second time.
- res = mco_resume(co); // Should print "coroutine 2".
- assert(res == MCO_SUCCESS);
- // The coroutine finished and should be now dead.
- assert(mco_status(co) == MCO_DEAD);
- // Call `mco_destroy` to destroy the coroutine.
- res = mco_destroy(co);
- assert(res == MCO_SUCCESS);
- return 0;
-}
-```
-
-_NOTE_: In case you don't want to use the minicoro allocator system you should
-allocate a coroutine object yourself using `mco_desc.coro_size` and call `mco_init`,
-then later to destroy call `mco_deinit` and deallocate it.
-
-## Yielding from anywhere
-
-You can yield the current running coroutine from anywhere
-without having to pass `mco_coro` pointers around,
-to this just use `mco_yield(mco_running())`.
-
-## Passing data between yield and resume
-
-The library has the storage interface to assist passing data between yield and resume.
-It's usage is straightforward,
-use `mco_push` to send data before a `mco_resume` or `mco_yield`,
-then later use `mco_pop` after a `mco_resume` or `mco_yield` to receive data.
-Take care to not mismatch a push and pop, otherwise these functions will return
-an error.
-
-## Error handling
-
-The library return error codes in most of its API in case of misuse or system error,
-the user is encouraged to handle them properly.
-
-## Library customization
-
-The following can be defined to change the library behavior:
-
-- `MCO_API` - Public API qualifier. Default is `extern`.
-- `MCO_MIN_STACK_SIZE` - Minimum stack size when creating a coroutine. Default is 32768.
-- `MCO_DEFAULT_STORAGE_SIZE` - Size of coroutine storage buffer. Default is 1024.
-- `MCO_DEFAULT_STACK_SIZE` - Default stack size when creating a coroutine. Default is 57344.
-- `MCO_MALLOC` - Default allocation function. Default is `malloc`.
-- `MCO_FREE` - Default deallocation function. Default is `free`.
-- `MCO_DEBUG` - Enable debug mode, logging any runtime error to stdout. Defined automatically unless `NDEBUG` or `MCO_NO_DEBUG` is defined.
-- `MCO_NO_DEBUG` - Disable debug mode.
-- `MCO_NO_MULTITHREAD` - Disable multithread usage. Multithread is supported when `thread_local` is supported.
-- `MCO_NO_DEFAULT_ALLOCATORS` - Disable the default allocator using `MCO_MALLOC` and `MCO_FREE`.
-- `MCO_ZERO_MEMORY` - Zero memory of stack for new coroutines and when poping storage, intended for garbage collected environments.
-- `MCO_USE_ASM` - Force use of assembly context switch implementation.
-- `MCO_USE_UCONTEXT` - Force use of ucontext context switch implementation.
-- `MCO_USE_FIBERS` - Force use of fibers context switch implementation.
-- `MCO_USE_VALGRIND` - Define if you want run with valgrind to fix accessing memory errors.
-
-# License
-
-Your choice of either Public Domain or MIT No Attribution, see end of file.
-*/
-
-
-#ifndef MINICORO_H
-#define MINICORO_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* Public API qualifier. */
-#ifndef MCO_API
-#define MCO_API extern
-#endif
-
-/* Size of coroutine storage buffer. */
-#ifndef MCO_DEFAULT_STORAGE_SIZE
-#define MCO_DEFAULT_STORAGE_SIZE 1024
-#endif
-
-#include /* for size_t */
-
-/* ---------------------------------------------------------------------------------------------- */
-
-/* Coroutine states. */
-typedef enum mco_state {
- MCO_DEAD = 0, /* The coroutine has finished normally or was uninitialized before finishing. */
- MCO_NORMAL, /* The coroutine is active but not running (that is, it has resumed another coroutine). */
- MCO_RUNNING, /* The coroutine is active and running. */
- MCO_SUSPENDED, /* The coroutine is suspended (in a call to yield, or it has not started running yet). */
-} mco_state;
-
-/* Coroutine result codes. */
-typedef enum mco_result {
- MCO_SUCCESS = 0,
- MCO_GENERIC_ERROR,
- MCO_INVALID_POINTER,
- MCO_INVALID_COROUTINE,
- MCO_NOT_SUSPENDED,
- MCO_NOT_RUNNING,
- MCO_MAKE_CONTEXT_ERROR,
- MCO_SWITCH_CONTEXT_ERROR,
- MCO_NOT_ENOUGH_SPACE,
- MCO_OUT_OF_MEMORY,
- MCO_INVALID_ARGUMENTS,
- MCO_INVALID_OPERATION,
-} mco_result;
-
-/* Coroutine structure. */
-typedef struct mco_coro mco_coro;
-struct mco_coro {
- void* context;
- mco_state state;
- void (*func)(mco_coro* co);
- mco_coro* prev_co;
- void* user_data;
- void* allocator_data;
- void (*free_cb)(void* ptr, void* allocator_data);
- void* stack_base; /* Stack base address, can be used to scan memory in a garbage collector. */
- size_t stack_size;
- unsigned char* storage;
- size_t bytes_stored;
- size_t storage_size;
- void* asan_prev_stack; /* Used by address sanitizer. */
- void* tsan_prev_fiber; /* Used by thread sanitizer. */
- void* tsan_fiber; /* Used by thread sanitizer. */
-};
-
-/* Structure used to initialize a coroutine. */
-typedef struct mco_desc {
- void (*func)(mco_coro* co); /* Entry point function for the coroutine. */
- void* user_data; /* Coroutine user data, can be get with `mco_get_user_data`. */
- /* Custom allocation interface. */
- void* (*malloc_cb)(size_t size, void* allocator_data); /* Custom allocation function. */
- void (*free_cb)(void* ptr, void* allocator_data); /* Custom deallocation function. */
- void* allocator_data; /* User data pointer passed to `malloc`/`free` allocation functions. */
- size_t storage_size; /* Coroutine storage size, to be used with the storage APIs. */
- /* These must be initialized only through `mco_init_desc`. */
- size_t coro_size; /* Coroutine structure size. */
- size_t stack_size; /* Coroutine stack size. */
-} mco_desc;
-
-/* Coroutine functions. */
-MCO_API mco_desc mco_desc_init(void (*func)(mco_coro* co), size_t stack_size); /* Initialize description of a coroutine. When stack size is 0 then MCO_DEFAULT_STACK_SIZE is used. */
-MCO_API mco_result mco_init(mco_coro* co, mco_desc* desc); /* Initialize the coroutine. */
-MCO_API mco_result mco_uninit(mco_coro* co); /* Uninitialize the coroutine, may fail if it's not dead or suspended. */
-MCO_API mco_result mco_create(mco_coro** out_co, mco_desc* desc); /* Allocates and initializes a new coroutine. */
-MCO_API mco_result mco_destroy(mco_coro* co); /* Uninitialize and deallocate the coroutine, may fail if it's not dead or suspended. */
-MCO_API mco_result mco_resume(mco_coro* co); /* Starts or continues the execution of the coroutine. */
-MCO_API mco_result mco_yield(mco_coro* co); /* Suspends the execution of a coroutine. */
-MCO_API mco_state mco_status(mco_coro* co); /* Returns the status of the coroutine. */
-MCO_API void* mco_get_user_data(mco_coro* co); /* Get coroutine user data supplied on coroutine creation. */
-
-/* Storage interface functions, used to pass values between yield and resume. */
-MCO_API mco_result mco_push(mco_coro* co, const void* src, size_t len); /* Push bytes to the coroutine storage. Use to send values between yield and resume. */
-MCO_API mco_result mco_pop(mco_coro* co, void* dest, size_t len); /* Pop bytes from the coroutine storage. Use to get values between yield and resume. */
-MCO_API mco_result mco_peek(mco_coro* co, void* dest, size_t len); /* Like `mco_pop` but it does not consumes the storage. */
-MCO_API size_t mco_get_bytes_stored(mco_coro* co); /* Get the available bytes that can be retrieved with a `mco_pop`. */
-MCO_API size_t mco_get_storage_size(mco_coro* co); /* Get the total storage size. */
-
-/* Misc functions. */
-MCO_API mco_coro* mco_running(void); /* Returns the running coroutine for the current thread. */
-MCO_API const char* mco_result_description(mco_result res); /* Get the description of a result. */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* MINICORO_H */
-
-#ifdef MINICORO_IMPL
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* ---------------------------------------------------------------------------------------------- */
-
-/* Minimum stack size when creating a coroutine. */
-#ifndef MCO_MIN_STACK_SIZE
-#define MCO_MIN_STACK_SIZE 32768
-#endif
-
-/* Default stack size when creating a coroutine. */
-#ifndef MCO_DEFAULT_STACK_SIZE
-#define MCO_DEFAULT_STACK_SIZE 57344 /* Don't use multiples of 64K to avoid D-cache aliasing conflicts. */
-#endif
-
-/* Detect implementation based on OS, arch and compiler. */
-#if !defined(MCO_USE_UCONTEXT) && !defined(MCO_USE_FIBERS) && !defined(MCO_USE_ASM)
- #if defined(_WIN32)
- #if (defined(__GNUC__) && defined(__x86_64__)) || (defined(_MSC_VER) && defined(_M_X64))
- #define MCO_USE_ASM
- #else
- #define MCO_USE_FIBERS
- #endif
- #elif defined(__EMSCRIPTEN__)
- #define MCO_USE_FIBERS
- #else
- #if __GNUC__ >= 3 /* Assembly extension supported. */
- #if defined(__x86_64__) || \
- defined(__i386) || defined(__i386__) || \
- defined(__ARM_EABI__) || defined(__aarch64__) || \
- defined(__riscv)
- #define MCO_USE_ASM
- #else
- #define MCO_USE_UCONTEXT
- #endif
- #else
- #define MCO_USE_UCONTEXT
- #endif
- #endif
-#endif
-
-#define _MCO_UNUSED(x) (void)(x)
-
-#if !defined(MCO_NO_DEBUG) && !defined(NDEBUG) && !defined(MCO_DEBUG)
-#define MCO_DEBUG
-#endif
-
-#ifndef MCO_LOG
- #ifdef MCO_DEBUG
- #include
- #define MCO_LOG(s) puts(s)
- #else
- #define MCO_LOG(s)
- #endif
-#endif
-
-#ifndef MCO_ASSERT
- #ifdef MCO_DEBUG
- #include
- #define MCO_ASSERT(c) assert(c)
- #else
- #define MCO_ASSERT(c)
- #endif
-#endif
-
-#ifndef MCO_THREAD_LOCAL
- #ifdef MCO_NO_MULTITHREAD
- #define MCO_THREAD_LOCAL
- #else
- #ifdef thread_local
- #define MCO_THREAD_LOCAL thread_local
- #elif __STDC_VERSION__ >= 201112 && !defined(__STDC_NO_THREADS__)
- #define MCO_THREAD_LOCAL _Thread_local
- #elif defined(_WIN32) && (defined(_MSC_VER) || defined(__ICL) || defined(__DMC__) || defined(__BORLANDC__))
- #define MCO_THREAD_LOCAL __declspec(thread)
- #elif defined(__GNUC__) || defined(__SUNPRO_C) || defined(__xlC__)
- #define MCO_THREAD_LOCAL __thread
- #else /* No thread local support, `mco_running` will be thread unsafe. */
- #define MCO_THREAD_LOCAL
- #define MCO_NO_MULTITHREAD
- #endif
- #endif
-#endif
-
-#ifndef MCO_FORCE_INLINE
- #ifdef _MSC_VER
- #define MCO_FORCE_INLINE __forceinline
- #elif defined(__GNUC__)
- #if defined(__STRICT_ANSI__)
- #define MCO_FORCE_INLINE __inline__ __attribute__((always_inline))
- #else
- #define MCO_FORCE_INLINE inline __attribute__((always_inline))
- #endif
- #elif defined(__BORLANDC__) || defined(__DMC__) || defined(__SC__) || defined(__WATCOMC__) || defined(__LCC__) || defined(__DECC)
- #define MCO_FORCE_INLINE __inline
- #else /* No inline support. */
- #define MCO_FORCE_INLINE
- #endif
-#endif
-
-#ifndef MCO_NO_DEFAULT_ALLOCATORS
-#ifndef MCO_MALLOC
- #include
- #define MCO_MALLOC malloc
- #define MCO_FREE free
-#endif
-static void* mco_malloc(size_t size, void* allocator_data) {
- _MCO_UNUSED(allocator_data);
- return MCO_MALLOC(size);
-}
-static void mco_free(void* ptr, void* allocator_data) {
- _MCO_UNUSED(allocator_data);
- MCO_FREE(ptr);
-}
-#endif /* MCO_NO_DEFAULT_ALLOCATORS */
-
-#if defined(__has_feature)
- #if __has_feature(address_sanitizer)
- #define _MCO_USE_ASAN
- #endif
- #if __has_feature(thread_sanitizer)
- #define _MCO_USE_TSAN
- #endif
-#endif
-#if defined(__SANITIZE_ADDRESS__)
- #define _MCO_USE_ASAN
-#endif
-#if defined(__SANITIZE_THREAD__)
- #define _MCO_USE_TSAN
-#endif
-#ifdef _MCO_USE_ASAN
-void __sanitizer_start_switch_fiber(void** fake_stack_save, const void *bottom, size_t size);
-void __sanitizer_finish_switch_fiber(void* fake_stack_save, const void **bottom_old, size_t *size_old);
-#endif
-#ifdef _MCO_USE_TSAN
-void* __tsan_get_current_fiber(void);
-void* __tsan_create_fiber(unsigned flags);
-void __tsan_destroy_fiber(void* fiber);
-void __tsan_switch_to_fiber(void* fiber, unsigned flags);
-#endif
-
-#include /* For memcpy and memset. */
-
-/* Utility for aligning addresses. */
-static MCO_FORCE_INLINE size_t _mco_align_forward(size_t addr, size_t align) {
- return (addr + (align-1)) & ~(align-1);
-}
-
-/* Variable holding the current running coroutine per thread. */
-static MCO_THREAD_LOCAL mco_coro* mco_current_co = NULL;
-
-static MCO_FORCE_INLINE void _mco_prepare_jumpin(mco_coro* co) {
- /* Set the old coroutine to normal state and update it. */
- mco_coro* prev_co = mco_running(); /* Must access through `mco_running`. */
- MCO_ASSERT(co->prev_co == NULL);
- co->prev_co = prev_co;
- if(prev_co) {
- MCO_ASSERT(prev_co->state == MCO_RUNNING);
- prev_co->state = MCO_NORMAL;
- }
- mco_current_co = co;
-#ifdef _MCO_USE_ASAN
- if(prev_co) {
- void* bottom_old = NULL;
- size_t size_old = 0;
- __sanitizer_finish_switch_fiber(prev_co->asan_prev_stack, (const void**)&bottom_old, &size_old);
- prev_co->asan_prev_stack = NULL;
- }
- __sanitizer_start_switch_fiber(&co->asan_prev_stack, co->stack_base, co->stack_size);
-#endif
-#ifdef _MCO_USE_TSAN
- co->tsan_prev_fiber = __tsan_get_current_fiber();
- __tsan_switch_to_fiber(co->tsan_fiber, 0);
-#endif
-}
-
-static MCO_FORCE_INLINE void _mco_prepare_jumpout(mco_coro* co) {
- /* Switch back to the previous running coroutine. */
- MCO_ASSERT(mco_running() == co);
- mco_coro* prev_co = co->prev_co;
- co->prev_co = NULL;
- if(prev_co) {
- MCO_ASSERT(prev_co->state == MCO_NORMAL);
- prev_co->state = MCO_RUNNING;
- }
- mco_current_co = prev_co;
-#ifdef _MCO_USE_ASAN
- void* bottom_old = NULL;
- size_t size_old = 0;
- __sanitizer_finish_switch_fiber(co->asan_prev_stack, (const void**)&bottom_old, &size_old);
- co->asan_prev_stack = NULL;
- if(prev_co) {
- __sanitizer_start_switch_fiber(&prev_co->asan_prev_stack, bottom_old, size_old);
- }
-#endif
-#ifdef _MCO_USE_TSAN
- void* tsan_prev_fiber = co->tsan_prev_fiber;
- co->tsan_prev_fiber = NULL;
- __tsan_switch_to_fiber(tsan_prev_fiber, 0);
-#endif
-}
-
-static void _mco_jumpin(mco_coro* co);
-static void _mco_jumpout(mco_coro* co);
-
-static void _mco_main(mco_coro* co) {
- co->func(co); /* Run the coroutine function. */
- co->state = MCO_DEAD; /* Coroutine finished successfully, set state to dead. */
- _mco_jumpout(co); /* Jump back to the old context .*/
-}
-
-/* ---------------------------------------------------------------------------------------------- */
-
-#if defined(MCO_USE_UCONTEXT) || defined(MCO_USE_ASM)
-
-/*
-Some of the following assembly code is taken from LuaCoco by Mike Pall.
-See https://coco.luajit.org/index.html
-
-MIT license
-
-Copyright (C) 2004-2016 Mike Pall. All rights reserved.
-
-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.
-*/
-
-#ifdef MCO_USE_ASM
-
-#if defined(__x86_64__) || defined(_M_X64)
-
-#ifdef _WIN32
-
-typedef struct _mco_ctxbuf {
- void *rip, *rsp, *rbp, *rbx, *r12, *r13, *r14, *r15, *rdi, *rsi;
- void* xmm[20]; /* xmm6, xmm7, xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15 */
- void* fiber_storage;
- void* dealloc_stack;
- void* stack_limit;
- void* stack_base;
-} _mco_ctxbuf;
-
-#ifdef __GNUC__
-#define _MCO_ASM_BLOB __attribute__((section(".text#")))
-#elif defined(_MSC_VER)
-#define _MCO_ASM_BLOB __declspec(allocate(".text"))
-#pragma section(".text")
-#endif
-
-_MCO_ASM_BLOB static unsigned char _mco_wrap_main_code[] = {
- 0x4c, 0x89, 0xe9, /* mov %r13,%rcx */
- 0x41, 0xff, 0xe4, /* jmpq *%r12 */
- 0xc3, /* retq */
- 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 /* nop */
-};
-
-_MCO_ASM_BLOB static unsigned char _mco_switch_code[] = {
- 0x48, 0x8d, 0x05, 0x52, 0x01, 0x00, 0x00, /* lea 0x152(%rip),%rax */
- 0x48, 0x89, 0x01, /* mov %rax,(%rcx) */
- 0x48, 0x89, 0x61, 0x08, /* mov %rsp,0x8(%rcx) */
- 0x48, 0x89, 0x69, 0x10, /* mov %rbp,0x10(%rcx) */
- 0x48, 0x89, 0x59, 0x18, /* mov %rbx,0x18(%rcx) */
- 0x4c, 0x89, 0x61, 0x20, /* mov %r12,0x20(%rcx) */
- 0x4c, 0x89, 0x69, 0x28, /* mov %r13,0x28(%rcx) */
- 0x4c, 0x89, 0x71, 0x30, /* mov %r14,0x30(%rcx) */
- 0x4c, 0x89, 0x79, 0x38, /* mov %r15,0x38(%rcx) */
- 0x48, 0x89, 0x79, 0x40, /* mov %rdi,0x40(%rcx) */
- 0x48, 0x89, 0x71, 0x48, /* mov %rsi,0x48(%rcx) */
- 0x66, 0x0f, 0xd6, 0x71, 0x50, /* movq %xmm6,0x50(%rcx) */
- 0x66, 0x0f, 0xd6, 0x79, 0x60, /* movq %xmm7,0x60(%rcx) */
- 0x66, 0x44, 0x0f, 0xd6, 0x41, 0x70, /* movq %xmm8,0x70(%rcx) */
- 0x66, 0x44, 0x0f, 0xd6, 0x89, 0x80, 0x00, 0x00, 0x00, /* movq %xmm9,0x80(%rcx) */
- 0x66, 0x44, 0x0f, 0xd6, 0x91, 0x90, 0x00, 0x00, 0x00, /* movq %xmm10,0x90(%rcx) */
- 0x66, 0x44, 0x0f, 0xd6, 0x99, 0xa0, 0x00, 0x00, 0x00, /* movq %xmm11,0xa0(%rcx) */
- 0x66, 0x44, 0x0f, 0xd6, 0xa1, 0xb0, 0x00, 0x00, 0x00, /* movq %xmm12,0xb0(%rcx) */
- 0x66, 0x44, 0x0f, 0xd6, 0xa9, 0xc0, 0x00, 0x00, 0x00, /* movq %xmm13,0xc0(%rcx) */
- 0x66, 0x44, 0x0f, 0xd6, 0xb1, 0xd0, 0x00, 0x00, 0x00, /* movq %xmm14,0xd0(%rcx) */
- 0x66, 0x44, 0x0f, 0xd6, 0xb9, 0xe0, 0x00, 0x00, 0x00, /* movq %xmm15,0xe0(%rcx) */
- 0x65, 0x4c, 0x8b, 0x14, 0x25, 0x30, 0x00, 0x00, 0x00, /* mov %gs:0x30,%r10 */
- 0x49, 0x8b, 0x42, 0x20, /* mov 0x20(%r10),%rax */
- 0x48, 0x89, 0x81, 0xf0, 0x00, 0x00, 0x00, /* mov %rax,0xf0(%rcx) */
- 0x49, 0x8b, 0x82, 0x78, 0x14, 0x00, 0x00, /* mov 0x1478(%r10),%rax */
- 0x48, 0x89, 0x81, 0xf8, 0x00, 0x00, 0x00, /* mov %rax,0xf8(%rcx) */
- 0x49, 0x8b, 0x42, 0x10, /* mov 0x10(%r10),%rax */
- 0x48, 0x89, 0x81, 0x00, 0x01, 0x00, 0x00, /* mov %rax,0x100(%rcx) */
- 0x49, 0x8b, 0x42, 0x08, /* mov 0x8(%r10),%rax */
- 0x48, 0x89, 0x81, 0x08, 0x01, 0x00, 0x00, /* mov %rax,0x108(%rcx) */
- 0x48, 0x8b, 0x82, 0x08, 0x01, 0x00, 0x00, /* mov 0x108(%rdx),%rax */
- 0x49, 0x89, 0x42, 0x08, /* mov %rax,0x8(%r10) */
- 0x48, 0x8b, 0x82, 0x00, 0x01, 0x00, 0x00, /* mov 0x100(%rdx),%rax */
- 0x49, 0x89, 0x42, 0x10, /* mov %rax,0x10(%r10) */
- 0x48, 0x8b, 0x82, 0xf8, 0x00, 0x00, 0x00, /* mov 0xf8(%rdx),%rax */
- 0x49, 0x89, 0x82, 0x78, 0x14, 0x00, 0x00, /* mov %rax,0x1478(%r10) */
- 0x48, 0x8b, 0x82, 0xf0, 0x00, 0x00, 0x00, /* mov 0xf0(%rdx),%rax */
- 0x49, 0x89, 0x42, 0x20, /* mov %rax,0x20(%r10) */
- 0xf3, 0x44, 0x0f, 0x7e, 0xba, 0xe0, 0x00, 0x00, 0x00, /* movq 0xe0(%rdx),%xmm15 */
- 0xf3, 0x44, 0x0f, 0x7e, 0xb2, 0xd0, 0x00, 0x00, 0x00, /* movq 0xd0(%rdx),%xmm14 */
- 0xf3, 0x44, 0x0f, 0x7e, 0xaa, 0xc0, 0x00, 0x00, 0x00, /* movq 0xc0(%rdx),%xmm13 */
- 0xf3, 0x44, 0x0f, 0x7e, 0xa2, 0xb0, 0x00, 0x00, 0x00, /* movq 0xb0(%rdx),%xmm12 */
- 0xf3, 0x44, 0x0f, 0x7e, 0x9a, 0xa0, 0x00, 0x00, 0x00, /* movq 0xa0(%rdx),%xmm11 */
- 0xf3, 0x44, 0x0f, 0x7e, 0x92, 0x90, 0x00, 0x00, 0x00, /* movq 0x90(%rdx),%xmm10 */
- 0xf3, 0x44, 0x0f, 0x7e, 0x8a, 0x80, 0x00, 0x00, 0x00, /* movq 0x80(%rdx),%xmm9 */
- 0xf3, 0x44, 0x0f, 0x7e, 0x42, 0x70, /* movq 0x70(%rdx),%xmm8 */
- 0xf3, 0x0f, 0x7e, 0x7a, 0x60, /* movq 0x60(%rdx),%xmm7 */
- 0xf3, 0x0f, 0x7e, 0x72, 0x50, /* movq 0x50(%rdx),%xmm6 */
- 0x48, 0x8b, 0x72, 0x48, /* mov 0x48(%rdx),%rsi */
- 0x48, 0x8b, 0x7a, 0x40, /* mov 0x40(%rdx),%rdi */
- 0x4c, 0x8b, 0x7a, 0x38, /* mov 0x38(%rdx),%r15 */
- 0x4c, 0x8b, 0x72, 0x30, /* mov 0x30(%rdx),%r14 */
- 0x4c, 0x8b, 0x6a, 0x28, /* mov 0x28(%rdx),%r13 */
- 0x4c, 0x8b, 0x62, 0x20, /* mov 0x20(%rdx),%r12 */
- 0x48, 0x8b, 0x5a, 0x18, /* mov 0x18(%rdx),%rbx */
- 0x48, 0x8b, 0x6a, 0x10, /* mov 0x10(%rdx),%rbp */
- 0x48, 0x8b, 0x62, 0x08, /* mov 0x8(%rdx),%rsp */
- 0xff, 0x22, /* jmpq *(%rdx) */
- 0xc3, /* retq */
- 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, /* nop */
-};
-
-void (*_mco_wrap_main)(void) = (void(*)(void))(void*)_mco_wrap_main_code;
-void (*_mco_switch)(_mco_ctxbuf* from, _mco_ctxbuf* to) = (void(*)(_mco_ctxbuf* from, _mco_ctxbuf* to))(void*)_mco_switch_code;
-
-static mco_result _mco_makectx(mco_coro* co, _mco_ctxbuf* ctx, void* stack_base, size_t stack_size) {
- stack_size = stack_size - 32; /* Reserve 32 bytes for the shadow space. */
- void** stack_high_ptr = (void**)((size_t)stack_base + stack_size - sizeof(size_t));
- stack_high_ptr[0] = (void*)(0xdeaddeaddeaddead); /* Dummy return address. */
- ctx->rip = (void*)(_mco_wrap_main);
- ctx->rsp = (void*)(stack_high_ptr);
- ctx->r12 = (void*)(_mco_main);
- ctx->r13 = (void*)(co);
- void* stack_top = (void*)((size_t)stack_base + stack_size);
- ctx->stack_base = stack_top;
- ctx->stack_limit = stack_base;
- ctx->dealloc_stack = stack_base;
- return MCO_SUCCESS;
-}
-
-#else /* not _WIN32 */
-
-typedef struct _mco_ctxbuf {
- void *rip, *rsp, *rbp, *rbx, *r12, *r13, *r14, *r15;
-} _mco_ctxbuf;
-
-void _mco_wrap_main(void);
-int _mco_switch(_mco_ctxbuf* from, _mco_ctxbuf* to);
-
-__asm__(
- ".text\n"
-#ifdef __MACH__ /* Mac OS X assembler */
- ".globl __mco_wrap_main\n"
- "__mco_wrap_main:\n"
-#else /* Linux assembler */
- ".globl _mco_wrap_main\n"
- ".type _mco_wrap_main @function\n"
- ".hidden _mco_wrap_main\n"
- "_mco_wrap_main:\n"
-#endif
- " movq %r13, %rdi\n"
- " jmpq *%r12\n"
-#ifndef __MACH__
- ".size _mco_wrap_main, .-_mco_wrap_main\n"
-#endif
-);
-
-__asm__(
- ".text\n"
-#ifdef __MACH__ /* Mac OS assembler */
- ".globl __mco_switch\n"
- "__mco_switch:\n"
-#else /* Linux assembler */
- ".globl _mco_switch\n"
- ".type _mco_switch @function\n"
- ".hidden _mco_switch\n"
- "_mco_switch:\n"
-#endif
- " leaq 0x3d(%rip), %rax\n"
- " movq %rax, (%rdi)\n"
- " movq %rsp, 8(%rdi)\n"
- " movq %rbp, 16(%rdi)\n"
- " movq %rbx, 24(%rdi)\n"
- " movq %r12, 32(%rdi)\n"
- " movq %r13, 40(%rdi)\n"
- " movq %r14, 48(%rdi)\n"
- " movq %r15, 56(%rdi)\n"
- " movq 56(%rsi), %r15\n"
- " movq 48(%rsi), %r14\n"
- " movq 40(%rsi), %r13\n"
- " movq 32(%rsi), %r12\n"
- " movq 24(%rsi), %rbx\n"
- " movq 16(%rsi), %rbp\n"
- " movq 8(%rsi), %rsp\n"
- " jmpq *(%rsi)\n"
- " ret\n"
-#ifndef __MACH__
- ".size _mco_switch, .-_mco_switch\n"
-#endif
-);
-
-static mco_result _mco_makectx(mco_coro* co, _mco_ctxbuf* ctx, void* stack_base, size_t stack_size) {
- stack_size = stack_size - 128; /* Reserve 128 bytes for the Red Zone space (System V AMD64 ABI). */
- void** stack_high_ptr = (void**)((size_t)stack_base + stack_size - sizeof(size_t));
- stack_high_ptr[0] = (void*)(0xdeaddeaddeaddead); /* Dummy return address. */
- ctx->rip = (void*)(_mco_wrap_main);
- ctx->rsp = (void*)(stack_high_ptr);
- ctx->r12 = (void*)(_mco_main);
- ctx->r13 = (void*)(co);
- return MCO_SUCCESS;
-}
-
-#endif /* not _WIN32 */
-
-#elif defined(__riscv)
-
-typedef struct _mco_ctxbuf {
- void* s[12]; /* s0-s11 */
- void* ra;
- void* pc;
- void* sp;
-#ifdef __riscv_flen
-#if __riscv_flen == 64
- double fs[12]; /* fs0-fs11 */
-#elif __riscv_flen == 32
- float fs[12]; /* fs0-fs11 */
-#endif
-#endif /* __riscv_flen */
-} _mco_ctxbuf;
-
-void _mco_wrap_main(void);
-int _mco_switch(_mco_ctxbuf* from, _mco_ctxbuf* to);
-
-__asm__(
- ".text\n"
- ".globl _mco_wrap_main\n"
- ".type _mco_wrap_main @function\n"
- ".hidden _mco_wrap_main\n"
- "_mco_wrap_main:\n"
- " mv a0, s0\n"
- " jr s1\n"
- ".size _mco_wrap_main, .-_mco_wrap_main\n"
-);
-
-__asm__(
- ".text\n"
- ".globl _mco_switch\n"
- ".type _mco_switch @function\n"
- ".hidden _mco_switch\n"
- "_mco_switch:\n"
- #if __riscv_xlen == 64
- " sd s0, 0x00(a0)\n"
- " sd s1, 0x08(a0)\n"
- " sd s2, 0x10(a0)\n"
- " sd s3, 0x18(a0)\n"
- " sd s4, 0x20(a0)\n"
- " sd s5, 0x28(a0)\n"
- " sd s6, 0x30(a0)\n"
- " sd s7, 0x38(a0)\n"
- " sd s8, 0x40(a0)\n"
- " sd s9, 0x48(a0)\n"
- " sd s10, 0x50(a0)\n"
- " sd s11, 0x58(a0)\n"
- " sd ra, 0x60(a0)\n"
- " sd ra, 0x68(a0)\n" /* pc */
- " sd sp, 0x70(a0)\n"
- #ifdef __riscv_flen
- #if __riscv_flen == 64
- " fsd fs0, 0x78(a0)\n"
- " fsd fs1, 0x80(a0)\n"
- " fsd fs2, 0x88(a0)\n"
- " fsd fs3, 0x90(a0)\n"
- " fsd fs4, 0x98(a0)\n"
- " fsd fs5, 0xa0(a0)\n"
- " fsd fs6, 0xa8(a0)\n"
- " fsd fs7, 0xb0(a0)\n"
- " fsd fs8, 0xb8(a0)\n"
- " fsd fs9, 0xc0(a0)\n"
- " fsd fs10, 0xc8(a0)\n"
- " fsd fs11, 0xd0(a0)\n"
- " fld fs0, 0x78(a1)\n"
- " fld fs1, 0x80(a1)\n"
- " fld fs2, 0x88(a1)\n"
- " fld fs3, 0x90(a1)\n"
- " fld fs4, 0x98(a1)\n"
- " fld fs5, 0xa0(a1)\n"
- " fld fs6, 0xa8(a1)\n"
- " fld fs7, 0xb0(a1)\n"
- " fld fs8, 0xb8(a1)\n"
- " fld fs9, 0xc0(a1)\n"
- " fld fs10, 0xc8(a1)\n"
- " fld fs11, 0xd0(a1)\n"
- #else
- #error "Unsupported RISC-V FLEN"
- #endif
- #endif /* __riscv_flen */
- " ld s0, 0x00(a1)\n"
- " ld s1, 0x08(a1)\n"
- " ld s2, 0x10(a1)\n"
- " ld s3, 0x18(a1)\n"
- " ld s4, 0x20(a1)\n"
- " ld s5, 0x28(a1)\n"
- " ld s6, 0x30(a1)\n"
- " ld s7, 0x38(a1)\n"
- " ld s8, 0x40(a1)\n"
- " ld s9, 0x48(a1)\n"
- " ld s10, 0x50(a1)\n"
- " ld s11, 0x58(a1)\n"
- " ld ra, 0x60(a1)\n"
- " ld a2, 0x68(a1)\n" /* pc */
- " ld sp, 0x70(a1)\n"
- " jr a2\n"
- #elif __riscv_xlen == 32
- " sw s0, 0x00(a0)\n"
- " sw s1, 0x04(a0)\n"
- " sw s2, 0x08(a0)\n"
- " sw s3, 0x0c(a0)\n"
- " sw s4, 0x10(a0)\n"
- " sw s5, 0x14(a0)\n"
- " sw s6, 0x18(a0)\n"
- " sw s7, 0x1c(a0)\n"
- " sw s8, 0x20(a0)\n"
- " sw s9, 0x24(a0)\n"
- " sw s10, 0x28(a0)\n"
- " sw s11, 0x2c(a0)\n"
- " sw ra, 0x30(a0)\n"
- " sw ra, 0x34(a0)\n" /* pc */
- " sw sp, 0x38(a0)\n"
- #ifdef __riscv_flen
- #if __riscv_flen == 64
- " fsd fs0, 0x3c(a0)\n"
- " fsd fs1, 0x44(a0)\n"
- " fsd fs2, 0x4c(a0)\n"
- " fsd fs3, 0x54(a0)\n"
- " fsd fs4, 0x5c(a0)\n"
- " fsd fs5, 0x64(a0)\n"
- " fsd fs6, 0x6c(a0)\n"
- " fsd fs7, 0x74(a0)\n"
- " fsd fs8, 0x7c(a0)\n"
- " fsd fs9, 0x84(a0)\n"
- " fsd fs10, 0x8c(a0)\n"
- " fsd fs11, 0x94(a0)\n"
- " fld fs0, 0x3c(a1)\n"
- " fld fs1, 0x44(a1)\n"
- " fld fs2, 0x4c(a1)\n"
- " fld fs3, 0x54(a1)\n"
- " fld fs4, 0x5c(a1)\n"
- " fld fs5, 0x64(a1)\n"
- " fld fs6, 0x6c(a1)\n"
- " fld fs7, 0x74(a1)\n"
- " fld fs8, 0x7c(a1)\n"
- " fld fs9, 0x84(a1)\n"
- " fld fs10, 0x8c(a1)\n"
- " fld fs11, 0x94(a1)\n"
- #elif __riscv_flen == 32
- " fsw fs0, 0x3c(a0)\n"
- " fsw fs1, 0x40(a0)\n"
- " fsw fs2, 0x44(a0)\n"
- " fsw fs3, 0x48(a0)\n"
- " fsw fs4, 0x4c(a0)\n"
- " fsw fs5, 0x50(a0)\n"
- " fsw fs6, 0x54(a0)\n"
- " fsw fs7, 0x58(a0)\n"
- " fsw fs8, 0x5c(a0)\n"
- " fsw fs9, 0x60(a0)\n"
- " fsw fs10, 0x64(a0)\n"
- " fsw fs11, 0x68(a0)\n"
- " flw fs0, 0x3c(a1)\n"
- " flw fs1, 0x40(a1)\n"
- " flw fs2, 0x44(a1)\n"
- " flw fs3, 0x48(a1)\n"
- " flw fs4, 0x4c(a1)\n"
- " flw fs5, 0x50(a1)\n"
- " flw fs6, 0x54(a1)\n"
- " flw fs7, 0x58(a1)\n"
- " flw fs8, 0x5c(a1)\n"
- " flw fs9, 0x60(a1)\n"
- " flw fs10, 0x64(a1)\n"
- " flw fs11, 0x68(a1)\n"
- #else
- #error "Unsupported RISC-V FLEN"
- #endif
- #endif /* __riscv_flen */
- " lw s0, 0x00(a1)\n"
- " lw s1, 0x04(a1)\n"
- " lw s2, 0x08(a1)\n"
- " lw s3, 0x0c(a1)\n"
- " lw s4, 0x10(a1)\n"
- " lw s5, 0x14(a1)\n"
- " lw s6, 0x18(a1)\n"
- " lw s7, 0x1c(a1)\n"
- " lw s8, 0x20(a1)\n"
- " lw s9, 0x24(a1)\n"
- " lw s10, 0x28(a1)\n"
- " lw s11, 0x2c(a1)\n"
- " lw ra, 0x30(a1)\n"
- " lw a2, 0x34(a1)\n" /* pc */
- " lw sp, 0x38(a1)\n"
- " jr a2\n"
- #else
- #error "Unsupported RISC-V XLEN"
- #endif /* __riscv_xlen */
- ".size _mco_switch, .-_mco_switch\n"
-);
-
-static mco_result _mco_makectx(mco_coro* co, _mco_ctxbuf* ctx, void* stack_base, size_t stack_size) {
- ctx->s[0] = (void*)(co);
- ctx->s[1] = (void*)(_mco_main);
- ctx->pc = (void*)(_mco_wrap_main);
-#if __riscv_xlen == 64
- ctx->ra = (void*)(0xdeaddeaddeaddead);
-#elif __riscv_xlen == 32
- ctx->ra = (void*)(0xdeaddead);
-#endif
- ctx->sp = (void*)((size_t)stack_base + stack_size);
- return MCO_SUCCESS;
-}
-
-#elif defined(__i386) || defined(__i386__)
-
-typedef struct _mco_ctxbuf {
- void *eip, *esp, *ebp, *ebx, *esi, *edi;
-} _mco_ctxbuf;
-
-void _mco_switch(_mco_ctxbuf* from, _mco_ctxbuf* to);
-
-__asm__(
- ".text\n"
- ".globl _mco_switch\n"
- ".type _mco_switch @function\n"
- ".hidden _mco_switch\n"
- "_mco_switch:\n"
- " call 1f\n"
- " 1:\n"
- " popl %ecx\n"
- " addl $(2f-1b), %ecx\n"
- " movl 4(%esp), %eax\n"
- " movl 8(%esp), %edx\n"
- " movl %ecx, (%eax)\n"
- " movl %esp, 4(%eax)\n"
- " movl %ebp, 8(%eax)\n"
- " movl %ebx, 12(%eax)\n"
- " movl %esi, 16(%eax)\n"
- " movl %edi, 20(%eax)\n"
- " movl 20(%edx), %edi\n"
- " movl 16(%edx), %esi\n"
- " movl 12(%edx), %ebx\n"
- " movl 8(%edx), %ebp\n"
- " movl 4(%edx), %esp\n"
- " jmp *(%edx)\n"
- " 2:\n"
- " ret\n"
- ".size _mco_switch, .-_mco_switch\n"
-);
-
-static mco_result _mco_makectx(mco_coro* co, _mco_ctxbuf* ctx, void* stack_base, size_t stack_size) {
- void** stack_high_ptr = (void**)((size_t)stack_base + stack_size - 16 - 1*sizeof(size_t));
- stack_high_ptr[0] = (void*)(0xdeaddead); /* Dummy return address. */
- stack_high_ptr[1] = (void*)(co);
- ctx->eip = (void*)(_mco_main);
- ctx->esp = (void*)(stack_high_ptr);
- return MCO_SUCCESS;
-}
-
-#elif defined(__ARM_EABI__)
-
-typedef struct _mco_ctxbuf {
-#ifndef __SOFTFP__
- void* f[16];
-#endif
- void *d[4]; /* d8-d15 */
- void *r[4]; /* r4-r11 */
- void *lr;
- void *sp;
-} _mco_ctxbuf;
-
-void _mco_wrap_main(void);
-int _mco_switch(_mco_ctxbuf* from, _mco_ctxbuf* to);
-
-__asm__(
- ".text\n"
- ".globl _mco_switch\n"
- ".type _mco_switch #function\n"
- ".hidden _mco_switch\n"
- "_mco_switch:\n"
-#ifndef __SOFTFP__
- " vstmia r0!, {d8-d15}\n"
-#endif
- " stmia r0, {r4-r11, lr}\n"
- " str sp, [r0, #9*4]\n"
-#ifndef __SOFTFP__
- " vldmia r1!, {d8-d15}\n"
-#endif
- " ldr sp, [r1, #9*4]\n"
- " ldmia r1, {r4-r11, pc}\n"
- ".size _mco_switch, .-_mco_switch\n"
-);
-
-__asm__(
- ".text\n"
- ".globl _mco_wrap_main\n"
- ".type _mco_wrap_main #function\n"
- ".hidden _mco_wrap_main\n"
- "_mco_wrap_main:\n"
- " mov r0, r4\n"
- " mov ip, r5\n"
- " mov lr, r6\n"
- " bx ip\n"
- ".size _mco_wrap_main, .-_mco_wrap_main\n"
-);
-
-static mco_result _mco_makectx(mco_coro* co, _mco_ctxbuf* ctx, void* stack_base, size_t stack_size) {
- ctx->d[0] = (void*)(co);
- ctx->d[1] = (void*)(_mco_main);
- ctx->d[2] = (void*)(0xdeaddead); /* Dummy return address. */
- ctx->lr = (void*)(_mco_wrap_main);
- ctx->sp = (void*)((size_t)stack_base + stack_size);
- return MCO_SUCCESS;
-}
-
-#elif defined(__aarch64__)
-
-typedef struct _mco_ctxbuf {
- void *x[12]; /* x19-x30 */
- void *sp;
- void *lr;
- void *d[8]; /* d8-d15 */
-} _mco_ctxbuf;
-
-void _mco_wrap_main(void);
-int _mco_switch(_mco_ctxbuf* from, _mco_ctxbuf* to);
-
-__asm__(
- ".text\n"
- ".globl _mco_switch\n"
- ".type _mco_switch #function\n"
- ".hidden _mco_switch\n"
- "_mco_switch:\n"
- " mov x10, sp\n"
- " mov x11, x30\n"
- " stp x19, x20, [x0, #(0*16)]\n"
- " stp x21, x22, [x0, #(1*16)]\n"
- " stp d8, d9, [x0, #(7*16)]\n"
- " stp x23, x24, [x0, #(2*16)]\n"
- " stp d10, d11, [x0, #(8*16)]\n"
- " stp x25, x26, [x0, #(3*16)]\n"
- " stp d12, d13, [x0, #(9*16)]\n"
- " stp x27, x28, [x0, #(4*16)]\n"
- " stp d14, d15, [x0, #(10*16)]\n"
- " stp x29, x30, [x0, #(5*16)]\n"
- " stp x10, x11, [x0, #(6*16)]\n"
- " ldp x19, x20, [x1, #(0*16)]\n"
- " ldp x21, x22, [x1, #(1*16)]\n"
- " ldp d8, d9, [x1, #(7*16)]\n"
- " ldp x23, x24, [x1, #(2*16)]\n"
- " ldp d10, d11, [x1, #(8*16)]\n"
- " ldp x25, x26, [x1, #(3*16)]\n"
- " ldp d12, d13, [x1, #(9*16)]\n"
- " ldp x27, x28, [x1, #(4*16)]\n"
- " ldp d14, d15, [x1, #(10*16)]\n"
- " ldp x29, x30, [x1, #(5*16)]\n"
- " ldp x10, x11, [x1, #(6*16)]\n"
- " mov sp, x10\n"
- " br x11\n"
- ".size _mco_switch, .-_mco_switch\n"
-);
-
-__asm__(
- ".text\n"
- ".globl _mco_wrap_main\n"
- ".type _mco_wrap_main #function\n"
- ".hidden _mco_wrap_main\n"
- "_mco_wrap_main:\n"
- " mov x0, x19\n"
- " mov x30, x21\n"
- " br x20\n"
- ".size _mco_wrap_main, .-_mco_wrap_main\n"
-);
-
-static mco_result _mco_makectx(mco_coro* co, _mco_ctxbuf* ctx, void* stack_base, size_t stack_size) {
- ctx->x[0] = (void*)(co);
- ctx->x[1] = (void*)(_mco_main);
- ctx->x[2] = (void*)(0xdeaddeaddeaddead); /* Dummy return address. */
- ctx->sp = (void*)((size_t)stack_base + stack_size);
- ctx->lr = (void*)(_mco_wrap_main);
- return MCO_SUCCESS;
-}
-
-#else
-
-#error "Unsupported architecture for assembly method."
-
-#endif /* ARCH */
-
-#elif defined(MCO_USE_UCONTEXT)
-
-#include
-
-typedef ucontext_t _mco_ctxbuf;
-
-#if defined(_LP64) || defined(__LP64__)
-static void _mco_wrap_main(unsigned int lo, unsigned int hi) {
- mco_coro* co = (mco_coro*)(((size_t)lo) | (((size_t)hi) << 32)); /* Extract coroutine pointer. */
- _mco_main(co);
-}
-#else
-static void _mco_wrap_main(unsigned int lo) {
- mco_coro* co = (mco_coro*)((size_t)lo); /* Extract coroutine pointer. */
- _mco_main(co);
-}
-#endif
-
-static MCO_FORCE_INLINE void _mco_switch(_mco_ctxbuf* from, _mco_ctxbuf* to) {
- int res = swapcontext(from, to);
- _MCO_UNUSED(res);
- MCO_ASSERT(res == 0);
-}
-
-static mco_result _mco_makectx(mco_coro* co, _mco_ctxbuf* ctx, void* stack_base, size_t stack_size) {
- /* Initialize ucontext. */
- if(getcontext(ctx) != 0) {
- MCO_LOG("failed to get ucontext");
- return MCO_MAKE_CONTEXT_ERROR;
- }
- ctx->uc_link = NULL; /* We never exit from _mco_wrap_main. */
- ctx->uc_stack.ss_sp = stack_base;
- ctx->uc_stack.ss_size = stack_size;
- unsigned int lo = (unsigned int)((size_t)co);
-#if defined(_LP64) || defined(__LP64__)
- unsigned int hi = (unsigned int)(((size_t)co)>>32);
- makecontext(ctx, (void (*)(void))_mco_wrap_main, 2, lo, hi);
-#else
- makecontext(ctx, (void (*)(void))_mco_wrap_main, 1, lo);
-#endif
- return MCO_SUCCESS;
-}
-
-#endif /* defined(MCO_USE_UCONTEXT) */
-
-#ifdef MCO_USE_VALGRIND
-#include
-#endif
-
-typedef struct _mco_context {
-#ifdef MCO_USE_VALGRIND
- unsigned int valgrind_stack_id;
-#endif
- _mco_ctxbuf ctx;
- _mco_ctxbuf back_ctx;
-} _mco_context;
-
-static void _mco_jumpin(mco_coro* co) {
- _mco_context* context = (_mco_context*)co->context;
- _mco_prepare_jumpin(co);
- _mco_switch(&context->back_ctx, &context->ctx); /* Do the context switch. */
-}
-
-static void _mco_jumpout(mco_coro* co) {
- _mco_context* context = (_mco_context*)co->context;
- _mco_prepare_jumpout(co);
- _mco_switch(&context->ctx, &context->back_ctx); /* Do the context switch. */
-}
-
-static mco_result _mco_create_context(mco_coro* co, mco_desc* desc) {
- /* Determine the context and stack address. */
- size_t co_addr = (size_t)co;
- size_t context_addr = _mco_align_forward(co_addr + sizeof(mco_coro), 16);
- size_t storage_addr = _mco_align_forward(context_addr + sizeof(_mco_context), 16);
- size_t stack_addr = _mco_align_forward(storage_addr + desc->storage_size, 16);
- /* Initialize context. */
- _mco_context* context = (_mco_context*)context_addr;
- memset(context, 0, sizeof(_mco_context));
- /* Initialize storage. */
- unsigned char* storage = (unsigned char*)storage_addr;
- memset(storage, 0, desc->storage_size);
- /* Initialize stack. */
- void *stack_base = (void*)stack_addr;
- size_t stack_size = desc->stack_size;
-#ifdef MCO_ZERO_MEMORY
- memset(stack_base, 0, stack_size);
-#endif
- /* Make the context. */
- mco_result res = _mco_makectx(co, &context->ctx, stack_base, stack_size);
- if(res != MCO_SUCCESS) {
- return res;
- }
-#ifdef MCO_USE_VALGRIND
- context->valgrind_stack_id = VALGRIND_STACK_REGISTER(stack_addr, stack_addr + stack_size);
-#endif
- co->context = context;
- co->stack_base = stack_base;
- co->stack_size = stack_size;
- co->storage = storage;
- co->storage_size = desc->storage_size;
- return MCO_SUCCESS;
-}
-
-static void _mco_destroy_context(mco_coro* co) {
-#ifdef MCO_USE_VALGRIND
- _mco_context* context = (_mco_context*)co->context;
- if(context && context->valgrind_stack_id != 0) {
- VALGRIND_STACK_DEREGISTER(context->valgrind_stack_id);
- context->valgrind_stack_id = 0;
- }
-#else
- _MCO_UNUSED(co);
-#endif
-}
-
-static MCO_FORCE_INLINE void _mco_init_desc_sizes(mco_desc* desc, size_t stack_size) {
- desc->coro_size = _mco_align_forward(sizeof(mco_coro), 16) +
- _mco_align_forward(sizeof(_mco_context), 16) +
- _mco_align_forward(desc->storage_size, 16) +
- stack_size + 16;
- desc->stack_size = stack_size; /* This is just a hint, it won't be the real one. */
-}
-
-#endif /* defined(MCO_USE_UCONTEXT) || defined(MCO_USE_ASM) */
-
-/* ---------------------------------------------------------------------------------------------- */
-
-#ifdef MCO_USE_FIBERS
-
-#ifdef _WIN32
-
-#ifndef _WIN32_WINNT
-#define _WIN32_WINNT 0x0400
-#endif
-#include
-
-typedef struct _mco_context {
- void* fib;
- void* back_fib;
-} _mco_context;
-
-static void _mco_jumpin(mco_coro* co) {
- void *cur_fib = GetCurrentFiber();
- if(!cur_fib || cur_fib == (void*)0x1e00) { /* See http://blogs.msdn.com/oldnewthing/archive/2004/12/31/344799.aspx */
- cur_fib = ConvertThreadToFiber(NULL);
- }
- MCO_ASSERT(cur_fib != NULL);
- _mco_context* context = (_mco_context*)co->context;
- context->back_fib = cur_fib;
- _mco_prepare_jumpin(co);
- SwitchToFiber(context->fib);
-}
-
-static void CALLBACK _mco_wrap_main(void* co) {
- _mco_main((mco_coro*)co);
-}
-
-static void _mco_jumpout(mco_coro* co) {
- _mco_context* context = (_mco_context*)co->context;
- void* back_fib = context->back_fib;
- MCO_ASSERT(back_fib != NULL);
- context->back_fib = NULL;
- _mco_prepare_jumpout(co);
- SwitchToFiber(back_fib);
-}
-
-/* Reverse engineered Fiber struct, used to get stack base. */
-typedef struct _mco_fiber {
- LPVOID param; /* fiber param */
- void* except; /* saved exception handlers list */
- void* stack_base; /* top of fiber stack */
- void* stack_limit; /* fiber stack low-water mark */
- void* stack_allocation; /* base of the fiber stack allocation */
- CONTEXT context; /* fiber context */
- DWORD flags; /* fiber flags */
- LPFIBER_START_ROUTINE start; /* start routine */
- void **fls_slots; /* fiber storage slots */
-} _mco_fiber;
-
-static mco_result _mco_create_context(mco_coro* co, mco_desc* desc) {
- /* Determine the context address. */
- size_t co_addr = (size_t)co;
- size_t context_addr = _mco_align_forward(co_addr + sizeof(mco_coro), 16);
- size_t storage_addr = _mco_align_forward(context_addr + sizeof(_mco_context), 16);
- /* Initialize context. */
- _mco_context* context = (_mco_context*)context_addr;
- memset(context, 0, sizeof(_mco_context));
- /* Initialize storage. */
- unsigned char* storage = (unsigned char*)storage_addr;
- memset(storage, 0, desc->storage_size);
- /* Create the fiber. */
- _mco_fiber* fib = (_mco_fiber*)CreateFiberEx(desc->stack_size, desc->stack_size, FIBER_FLAG_FLOAT_SWITCH, _mco_wrap_main, co);
- if(!fib) {
- MCO_LOG("failed to create fiber");
- return MCO_MAKE_CONTEXT_ERROR;
- }
- context->fib = fib;
- co->context = context;
- co->stack_base = fib->stack_base;
- co->stack_size = desc->stack_size;
- co->storage = storage;
- co->storage_size = desc->storage_size;
- return MCO_SUCCESS;
-}
-
-static void _mco_destroy_context(mco_coro* co) {
- _mco_context* context = (_mco_context*)co->context;
- if(context && context->fib) {
- DeleteFiber(context->fib);
- context->fib = NULL;
- }
-}
-
-static MCO_FORCE_INLINE void _mco_init_desc_sizes(mco_desc* desc, size_t stack_size) {
- desc->coro_size = _mco_align_forward(sizeof(mco_coro), 16) +
- _mco_align_forward(sizeof(_mco_context), 16) +
- _mco_align_forward(desc->storage_size, 16) +
- 16;
- desc->stack_size = stack_size;
-}
-
-#elif defined(__EMSCRIPTEN__)
-
-#include
-
-#ifndef MCO_ASYNCFY_STACK_SIZE
-#define MCO_ASYNCFY_STACK_SIZE 16384
-#endif
-
-typedef struct _mco_context {
- emscripten_fiber_t fib;
- emscripten_fiber_t* back_fib;
-} _mco_context;
-
-static emscripten_fiber_t* running_fib = NULL;
-static unsigned char main_asyncify_stack[MCO_ASYNCFY_STACK_SIZE];
-static emscripten_fiber_t main_fib;
-
-static void _mco_wrap_main(void* co) {
- _mco_main((mco_coro*)co);
-}
-
-static void _mco_jumpin(mco_coro* co) {
- _mco_context* context = (_mco_context*)co->context;
- emscripten_fiber_t* back_fib = running_fib;
- if(!back_fib) {
- back_fib = &main_fib;
- emscripten_fiber_init_from_current_context(back_fib, main_asyncify_stack, MCO_ASYNCFY_STACK_SIZE);
- }
- running_fib = &context->fib;
- context->back_fib = back_fib;
- _mco_prepare_jumpin(co);
- emscripten_fiber_swap(back_fib, &context->fib); /* Do the context switch. */
-}
-
-static void _mco_jumpout(mco_coro* co) {
- _mco_context* context = (_mco_context*)co->context;
- running_fib = context->back_fib;
- _mco_prepare_jumpout(co);
- emscripten_fiber_swap(&context->fib, context->back_fib); /* Do the context switch. */
-}
-
-static mco_result _mco_create_context(mco_coro* co, mco_desc* desc) {
- if(emscripten_has_asyncify() != 1) {
- MCO_LOG("failed to create fiber because ASYNCIFY is not enabled");
- return MCO_MAKE_CONTEXT_ERROR;
- }
- /* Determine the context address. */
- size_t co_addr = (size_t)co;
- size_t context_addr = _mco_align_forward(co_addr + sizeof(mco_coro), 16);
- size_t storage_addr = _mco_align_forward(context_addr + sizeof(_mco_context), 16);
- size_t stack_addr = _mco_align_forward(storage_addr + desc->storage_size, 16);
- size_t asyncify_stack_addr = _mco_align_forward(stack_addr + desc->stack_size, 16);
- /* Initialize context. */
- _mco_context* context = (_mco_context*)context_addr;
- memset(context, 0, sizeof(_mco_context));
- /* Initialize storage. */
- unsigned char* storage = (unsigned char*)storage_addr;
- memset(storage, 0, desc->storage_size);
- /* Initialize stack. */
- void *stack_base = (void*)stack_addr;
- size_t stack_size = asyncify_stack_addr - stack_addr;
- void *asyncify_stack_base = (void*)asyncify_stack_addr;
- size_t asyncify_stack_size = co_addr + desc->coro_size - asyncify_stack_addr;
-#ifdef MCO_ZERO_MEMORY
- memset(stack_base, 0, stack_size);
- memset(asyncify_stack_base, 0, asyncify_stack_size);
-#endif
- /* Create the fiber. */
- emscripten_fiber_init(&context->fib, _mco_wrap_main, co, stack_base, stack_size, asyncify_stack_base, asyncify_stack_size);
- co->context = context;
- co->stack_base = stack_base;
- co->stack_size = stack_size;
- co->storage = storage;
- co->storage_size = desc->storage_size;
- return MCO_SUCCESS;
-}
-
-static void _mco_destroy_context(mco_coro* co) {
- /* Nothing to do. */
- _MCO_UNUSED(co);
-}
-
-static MCO_FORCE_INLINE void _mco_init_desc_sizes(mco_desc* desc, size_t stack_size) {
- desc->coro_size = _mco_align_forward(sizeof(mco_coro), 16) +
- _mco_align_forward(sizeof(_mco_context), 16) +
- _mco_align_forward(desc->storage_size, 16) +
- _mco_align_forward(stack_size, 16) +
- _mco_align_forward(MCO_ASYNCFY_STACK_SIZE, 16) +
- 16;
- desc->stack_size = stack_size; /* This is just a hint, it won't be the real one. */
-}
-
-#else
-
-#error "Unsupported architecture for fibers method."
-
-#endif
-
-#endif /* MCO_USE_FIBERS */
-
-/* ---------------------------------------------------------------------------------------------- */
-
-mco_desc mco_desc_init(void (*func)(mco_coro* co), size_t stack_size) {
- if(stack_size != 0) {
- /* Stack size should be at least `MCO_MIN_STACK_SIZE`. */
- if(stack_size < MCO_MIN_STACK_SIZE) {
- stack_size = MCO_MIN_STACK_SIZE;
- }
- } else {
- stack_size = MCO_DEFAULT_STACK_SIZE;
- }
- stack_size = _mco_align_forward(stack_size, 16); /* Stack size should be aligned to 16 bytes. */
- mco_desc desc;
- memset(&desc, 0, sizeof(mco_desc));
-#ifndef MCO_NO_DEFAULT_ALLOCATORS
- /* Set default allocators. */
- desc.malloc_cb = mco_malloc;
- desc.free_cb = mco_free;
-#endif
- desc.func = func;
- desc.storage_size = MCO_DEFAULT_STORAGE_SIZE;
- _mco_init_desc_sizes(&desc, stack_size);
- return desc;
-}
-
-static mco_result _mco_validate_desc(mco_desc* desc) {
- if(!desc) {
- MCO_LOG("coroutine description is NULL");
- return MCO_INVALID_ARGUMENTS;
- }
- if(!desc->func) {
- MCO_LOG("coroutine function in invalid");
- return MCO_INVALID_ARGUMENTS;
- }
- if(desc->stack_size < MCO_MIN_STACK_SIZE) {
- MCO_LOG("coroutine stack size is too small");
- return MCO_INVALID_ARGUMENTS;
- }
- if(desc->coro_size < sizeof(mco_coro)) {
- MCO_LOG("coroutine size is invalid");
- return MCO_INVALID_ARGUMENTS;
- }
- return MCO_SUCCESS;
-}
-
-mco_result mco_init(mco_coro* co, mco_desc* desc) {
- if(!co) {
- MCO_LOG("attempt to initialize an invalid coroutine");
- return MCO_INVALID_COROUTINE;
- }
- memset(co, 0, sizeof(mco_coro));
- /* Validate coroutine description. */
- mco_result res = _mco_validate_desc(desc);
- if(res != MCO_SUCCESS)
- return res;
- /* Create the coroutine. */
- res = _mco_create_context(co, desc);
- if(res != MCO_SUCCESS)
- return res;
- co->state = MCO_SUSPENDED; /* We initialize in suspended state. */
- co->free_cb = desc->free_cb;
- co->allocator_data = desc->allocator_data;
- co->func = desc->func;
- co->user_data = desc->user_data;
-#ifdef _MCO_USE_TSAN
- co->tsan_fiber = __tsan_create_fiber(0);
-#endif
- return MCO_SUCCESS;
-}
-
-mco_result mco_uninit(mco_coro* co) {
- if(!co) {
- MCO_LOG("attempt to uninitialize an invalid coroutine");
- return MCO_INVALID_COROUTINE;
- }
- /* Cannot uninitialize while running. */
- if(!(co->state == MCO_SUSPENDED || co->state == MCO_DEAD)) {
- MCO_LOG("attempt to uninitialize a coroutine that is not dead or suspended");
- return MCO_INVALID_OPERATION;
- }
- /* The coroutine is now dead and cannot be used anymore. */
- co->state = MCO_DEAD;
-#ifdef _MCO_USE_TSAN
- if(co->tsan_fiber != NULL) {
- __tsan_destroy_fiber(co->tsan_fiber);
- co->tsan_fiber = NULL;
- }
-#endif
- _mco_destroy_context(co);
- return MCO_SUCCESS;
-}
-
-mco_result mco_create(mco_coro** out_co, mco_desc* desc) {
- /* Validate input. */
- if(!out_co) {
- MCO_LOG("coroutine output pointer is NULL");
- return MCO_INVALID_POINTER;
- }
- if(!desc || !desc->malloc_cb || !desc->free_cb) {
- *out_co = NULL;
- MCO_LOG("coroutine allocator description is not set");
- return MCO_INVALID_ARGUMENTS;
- }
- /* Allocate the coroutine. */
- mco_coro* co = (mco_coro*)desc->malloc_cb(desc->coro_size, desc->allocator_data);
- if(!co) {
- MCO_LOG("coroutine allocation failed");
- *out_co = NULL;
- return MCO_OUT_OF_MEMORY;
- }
- /* Initialize the coroutine. */
- mco_result res = mco_init(co, desc);
- if(res != MCO_SUCCESS) {
- desc->free_cb(co, desc->allocator_data);
- *out_co = NULL;
- return res;
- }
- *out_co = co;
- return MCO_SUCCESS;
-}
-
-mco_result mco_destroy(mco_coro* co) {
- if(!co) {
- MCO_LOG("attempt to destroy an invalid coroutine");
- return MCO_INVALID_COROUTINE;
- }
- /* Uninitialize the coroutine first. */
- mco_result res = mco_uninit(co);
- if(res != MCO_SUCCESS)
- return res;
- /* Free the coroutine. */
- if(!co->free_cb) {
- MCO_LOG("attempt destroy a coroutine that has no free callback");
- return MCO_INVALID_POINTER;
- }
- co->free_cb(co, co->allocator_data);
- return MCO_SUCCESS;
-}
-
-mco_result mco_resume(mco_coro* co) {
- if(!co) {
- MCO_LOG("attempt to resume an invalid coroutine");
- return MCO_INVALID_COROUTINE;
- }
- if(co->state != MCO_SUSPENDED) { /* Can only resume coroutines that are suspended. */
- MCO_LOG("attempt to resume a coroutine that is not suspended");
- return MCO_NOT_SUSPENDED;
- }
- co->state = MCO_RUNNING; /* The coroutine is now running. */
- _mco_jumpin(co);
- return MCO_SUCCESS;
-}
-
-mco_result mco_yield(mco_coro* co) {
- if(!co) {
- MCO_LOG("attempt to yield an invalid coroutine");
- return MCO_INVALID_COROUTINE;
- }
- if(co->state != MCO_RUNNING) { /* Can only yield coroutines that are running. */
- MCO_LOG("attempt to yield a coroutine that is not running");
- return MCO_NOT_RUNNING;
- }
- co->state = MCO_SUSPENDED; /* The coroutine is now suspended. */
- _mco_jumpout(co);
- return MCO_SUCCESS;
-}
-
-mco_state mco_status(mco_coro* co) {
- if(co != NULL) {
- return co->state;
- }
- return MCO_DEAD;
-}
-
-void* mco_get_user_data(mco_coro* co) {
- if(co != NULL) {
- return co->user_data;
- }
- return NULL;
-}
-
-mco_result mco_push(mco_coro* co, const void* src, size_t len) {
- if(!co) {
- MCO_LOG("attempt to use an invalid coroutine");
- return MCO_INVALID_COROUTINE;
- } else if(len > 0) {
- size_t bytes_stored = co->bytes_stored + len;
- if(bytes_stored > co->storage_size) {
- MCO_LOG("attempt to push bytes too many bytes into coroutine storage");
- return MCO_NOT_ENOUGH_SPACE;
- }
- if(!src) {
- MCO_LOG("attempt push a null pointer into coroutine storage");
- return MCO_INVALID_POINTER;
- }
- memcpy(&co->storage[co->bytes_stored], src, len);
- co->bytes_stored = bytes_stored;
- }
- return MCO_SUCCESS;
-}
-
-mco_result mco_pop(mco_coro* co, void* dest, size_t len) {
- if(!co) {
- MCO_LOG("attempt to use an invalid coroutine");
- return MCO_INVALID_COROUTINE;
- } else if(len > 0) {
- if(len > co->bytes_stored) {
- MCO_LOG("attempt to pop too many bytes from coroutine storage");
- return MCO_NOT_ENOUGH_SPACE;
- }
- size_t bytes_stored = co->bytes_stored - len;
- if(dest) {
- memcpy(dest, &co->storage[bytes_stored], len);
- }
- co->bytes_stored = bytes_stored;
-#ifdef MCO_ZERO_MEMORY
- /* Clear garbage in the discarded storage. */
- memset(&co->storage[bytes_stored], 0, len);
-#endif
- }
- return MCO_SUCCESS;
-}
-
-mco_result mco_peek(mco_coro* co, void* dest, size_t len) {
- if(!co) {
- MCO_LOG("attempt to use an invalid coroutine");
- return MCO_INVALID_COROUTINE;
- } else if(len > 0) {
- if(len > co->bytes_stored) {
- MCO_LOG("attempt to peek too many bytes from coroutine storage");
- return MCO_NOT_ENOUGH_SPACE;
- }
- if(!dest) {
- MCO_LOG("attempt peek into a null pointer");
- return MCO_INVALID_POINTER;
- }
- memcpy(dest, &co->storage[co->bytes_stored - len], len);
- }
- return MCO_SUCCESS;
-}
-
-size_t mco_get_bytes_stored(mco_coro* co) {
- if(co == NULL) {
- return 0;
- }
- return co->bytes_stored;
-}
-
-size_t mco_get_storage_size(mco_coro* co) {
- if(co == NULL) {
- return 0;
- }
- return co->storage_size;
-}
-
-#ifdef MCO_NO_MULTITHREAD
-mco_coro* mco_running(void) {
- return mco_current_co;
-}
-#else
-static mco_coro* _mco_running(void) {
- return mco_current_co;
-}
-mco_coro* mco_running(void) {
- /*
- Compilers aggressively optimize the use of TLS by caching loads.
- Since fiber code can migrate between threads it’s possible for the load to be stale.
- To prevent this from happening we avoid inline functions.
- */
- mco_coro* (*volatile func)(void) = _mco_running;
- return func();
-}
-#endif
-
-const char* mco_result_description(mco_result res) {
- switch(res) {
- case MCO_SUCCESS:
- return "No error";
- case MCO_GENERIC_ERROR:
- return "Generic error";
- case MCO_INVALID_POINTER:
- return "Invalid pointer";
- case MCO_INVALID_COROUTINE:
- return "Invalid coroutine";
- case MCO_NOT_SUSPENDED:
- return "Coroutine not suspended";
- case MCO_NOT_RUNNING:
- return "Coroutine not running";
- case MCO_MAKE_CONTEXT_ERROR:
- return "Make context error";
- case MCO_SWITCH_CONTEXT_ERROR:
- return "Switch context error";
- case MCO_NOT_ENOUGH_SPACE:
- return "Not enough space";
- case MCO_OUT_OF_MEMORY:
- return "Out of memory";
- case MCO_INVALID_ARGUMENTS:
- return "Invalid arguments";
- case MCO_INVALID_OPERATION:
- return "Invalid operation";
- default:
- return "Unknown error";
- }
-}
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* MINICORO_IMPL */
-
-/*
-This software is available as a choice of the following licenses. Choose
-whichever you prefer.
-
-===============================================================================
-ALTERNATIVE 1 - Public Domain (www.unlicense.org)
-===============================================================================
-This is free and unencumbered software released into the public domain.
-
-Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
-software, either in source code form or as a compiled binary, for any purpose,
-commercial or non-commercial, and by any means.
-
-In jurisdictions that recognize copyright laws, the author or authors of this
-software dedicate any and all copyright interest in the software to the public
-domain. We make this dedication for the benefit of the public at large and to
-the detriment of our heirs and successors. We intend this dedication to be an
-overt act of relinquishment in perpetuity of all present and future rights to
-this software under copyright law.
-
-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 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.
-
-For more information, please refer to
-
-===============================================================================
-ALTERNATIVE 2 - MIT No Attribution
-===============================================================================
-Copyright (c) 2021 Eduardo Bart (https://github.com/edubart/minicoro)
-
-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.
-
-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.
-*/
\ No newline at end of file
diff --git a/framework/3rd/termbox b/framework/3rd/termbox
new file mode 160000
index 0000000..d961a81
--- /dev/null
+++ b/framework/3rd/termbox
@@ -0,0 +1 @@
+Subproject commit d961a8122210010e7c2c8f201e61170c13d319b4
diff --git a/framework/3rd/termbox/.gitignore b/framework/3rd/termbox/.gitignore
deleted file mode 100755
index afcccde..0000000
--- a/framework/3rd/termbox/.gitignore
+++ /dev/null
@@ -1,7 +0,0 @@
-bin
-obj
-src/demo/*.o
-src/demo/keyboard
-src/demo/output
-src/demo/paint
-src/demo/truecolor
diff --git a/framework/3rd/termbox/makefile b/framework/3rd/termbox/makefile
deleted file mode 100755
index aa2270b..0000000
--- a/framework/3rd/termbox/makefile
+++ /dev/null
@@ -1,40 +0,0 @@
-NAME=termbox
-CC=gcc
-FLAGS+=-std=c99 -pedantic -Wall -Werror -g
-
-OS:=$(shell uname -s)
-ifeq ($(OS),Linux)
- FLAGS+=-D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE
-endif
-
-BIND=bin
-SRCD=src
-OBJD=obj
-INCL=-I$(SRCD)
-
-SRCS=$(SRCD)/termbox.c
-SRCS+=$(SRCD)/input.c
-SRCS+=$(SRCD)/memstream.c
-SRCS+=$(SRCD)/ringbuffer.c
-SRCS+=$(SRCD)/term.c
-SRCS+=$(SRCD)/utf8.c
-
-OBJS:=$(patsubst $(SRCD)/%.c,$(OBJD)/$(SRCD)/%.o,$(SRCS))
-
-.PHONY:all
-all:$(BIND)/$(NAME).a
-
-$(OBJD)/%.o:%.c
- @echo "building source object $@"
- @mkdir -p $(@D)
- @$(CC) $(INCL) $(FLAGS) -c -o $@ $<
-
-$(BIND)/$(NAME).a:$(OBJS)
- @echo "compiling $@"
- @mkdir -p $(BIND)
- @ar rvs $(BIND)/$(NAME).a $(OBJS)
-
-clean:
- @echo "cleaning workspace"
- @rm -rf $(BIND)
- @rm -rf $(OBJD)
diff --git a/framework/3rd/termbox/readme.md b/framework/3rd/termbox/readme.md
deleted file mode 100755
index d20e68d..0000000
--- a/framework/3rd/termbox/readme.md
+++ /dev/null
@@ -1,57 +0,0 @@
-# Termbox
-[Termbox](https://github.com/nsf/termbox)
-was a promising Text User Interface (TUI) library.
-Unfortunately, its original author
-[changed his mind](https://github.com/nsf/termbox/issues/37#issuecomment-261075481)
-about consoles and despite the
-[community's efforts](https://github.com/nsf/termbox/pull/104#issuecomment-300308156)
-to keep the library's development going, preferred to let it die. Before it happened,
-[some people](https://wiki.musl-libc.org/alternatives.html)
-already noticed the robustness of the initial architecture
-[became compromised](https://github.com/nsf/termbox/commit/66c3f91b14e24510319bce6b5cc2fecf8cf5abff#commitcomment-3790714)
-in a nonsensical refactoring frenzy. Now, the author refuses to merge features
-like true-color support, invoking some
-[spurious correlations](https://github.com/nsf/termbox/pull/104#issuecomment-300292223)
-we will discuss no further.
-
-## The new Termbox-next
-This fork was made to restore the codebase to its original quality (before
-[66c3f91](https://github.com/nsf/termbox/commit/66c3f91b14e24510319bce6b5cc2fecf8cf5abff))
-while providing all the functionnalities of the current implementation.
-This was achieved by branching at
-[a2e217f](https://github.com/nsf/termbox/commit/a2e217f0fb97e6bbd589136ea3945f9d5a123d81)
-and cherry-picking all the commits up to
-[d63b83a](https://github.com/nsf/termbox/commit/d63b83af04e0fd6da836bb8f37e5cec72a1dc95a)
-if they weren't harmful.
-
-## Changes
-A lot of things changed during the process:
- - *waf*, the original build system, was completely removed from the
- project and replaced by make.
- - anything related to python was removed as well
-
-## Getting started
-Termbox's interface only consists of 12 functions:
-```
-tb_init() // initialization
-tb_shutdown() // shutdown
-
-tb_width() // width of the terminal screen
-tb_height() // height of the terminal screen
-
-tb_clear() // clear buffer
-tb_present() // sync internal buffer with terminal
-
-tb_put_cell()
-tb_change_cell()
-tb_blit() // drawing functions
-
-tb_select_input_mode() // change input mode
-tb_peek_event() // peek a keyboard event
-tb_poll_event() // wait for a keyboard event
-```
-See src/termbox.h header file for full detail.
-
-## TL;DR
-`make` to build a static version of the lib under bin/termbox.a
-`cd src/demo && make` to build the example programs in src/demo/
diff --git a/framework/3rd/termbox/src/demo/keyboard.c b/framework/3rd/termbox/src/demo/keyboard.c
deleted file mode 100755
index 3c25c5a..0000000
--- a/framework/3rd/termbox/src/demo/keyboard.c
+++ /dev/null
@@ -1,827 +0,0 @@
-#include
-#include
-#include
-#include
-#include "termbox.h"
-
-struct key
-{
- unsigned char x;
- unsigned char y;
- uint32_t ch;
-};
-
-#define STOP {0,0,0}
-struct key K_ESC[] = {{1, 1, 'E'}, {2, 1, 'S'}, {3, 1, 'C'}, STOP};
-struct key K_F1[] = {{6, 1, 'F'}, {7, 1, '1'}, STOP};
-struct key K_F2[] = {{9, 1, 'F'}, {10, 1, '2'}, STOP};
-struct key K_F3[] = {{12, 1, 'F'}, {13, 1, '3'}, STOP};
-struct key K_F4[] = {{15, 1, 'F'}, {16, 1, '4'}, STOP};
-struct key K_F5[] = {{19, 1, 'F'}, {20, 1, '5'}, STOP};
-struct key K_F6[] = {{22, 1, 'F'}, {23, 1, '6'}, STOP};
-struct key K_F7[] = {{25, 1, 'F'}, {26, 1, '7'}, STOP};
-struct key K_F8[] = {{28, 1, 'F'}, {29, 1, '8'}, STOP};
-struct key K_F9[] = {{33, 1, 'F'}, {34, 1, '9'}, STOP};
-struct key K_F10[] = {{36, 1, 'F'}, {37, 1, '1'}, {38, 1, '0'}, STOP};
-struct key K_F11[] = {{40, 1, 'F'}, {41, 1, '1'}, {42, 1, '1'}, STOP};
-struct key K_F12[] = {{44, 1, 'F'}, {45, 1, '1'}, {46, 1, '2'}, STOP};
-struct key K_PRN[] = {{50, 1, 'P'}, {51, 1, 'R'}, {52, 1, 'N'}, STOP};
-struct key K_SCR[] = {{54, 1, 'S'}, {55, 1, 'C'}, {56, 1, 'R'}, STOP};
-struct key K_BRK[] = {{58, 1, 'B'}, {59, 1, 'R'}, {60, 1, 'K'}, STOP};
-struct key K_LED1[] = {{66, 1, '-'}, STOP};
-struct key K_LED2[] = {{70, 1, '-'}, STOP};
-struct key K_LED3[] = {{74, 1, '-'}, STOP};
-
-struct key K_TILDE[] = {{1, 4, '`'}, STOP};
-struct key K_TILDE_SHIFT[] = {{1, 4, '~'}, STOP};
-struct key K_1[] = {{4, 4, '1'}, STOP};
-struct key K_1_SHIFT[] = {{4, 4, '!'}, STOP};
-struct key K_2[] = {{7, 4, '2'}, STOP};
-struct key K_2_SHIFT[] = {{7, 4, '@'}, STOP};
-struct key K_3[] = {{10, 4, '3'}, STOP};
-struct key K_3_SHIFT[] = {{10, 4, '#'}, STOP};
-struct key K_4[] = {{13, 4, '4'}, STOP};
-struct key K_4_SHIFT[] = {{13, 4, '$'}, STOP};
-struct key K_5[] = {{16, 4, '5'}, STOP};
-struct key K_5_SHIFT[] = {{16, 4, '%'}, STOP};
-struct key K_6[] = {{19, 4, '6'}, STOP};
-struct key K_6_SHIFT[] = {{19, 4, '^'}, STOP};
-struct key K_7[] = {{22, 4, '7'}, STOP};
-struct key K_7_SHIFT[] = {{22, 4, '&'}, STOP};
-struct key K_8[] = {{25, 4, '8'}, STOP};
-struct key K_8_SHIFT[] = {{25, 4, '*'}, STOP};
-struct key K_9[] = {{28, 4, '9'}, STOP};
-struct key K_9_SHIFT[] = {{28, 4, '('}, STOP};
-struct key K_0[] = {{31, 4, '0'}, STOP};
-struct key K_0_SHIFT[] = {{31, 4, ')'}, STOP};
-struct key K_MINUS[] = {{34, 4, '-'}, STOP};
-struct key K_MINUS_SHIFT[] = {{34, 4, '_'}, STOP};
-struct key K_EQUALS[] = {{37, 4, '='}, STOP};
-struct key K_EQUALS_SHIFT[] = {{37, 4, '+'}, STOP};
-struct key K_BACKSLASH[] = {{40, 4, '\\'}, STOP};
-struct key K_BACKSLASH_SHIFT[] = {{40, 4, '|'}, STOP};
-struct key K_BACKSPACE[] = {{44, 4, 0x2190}, {45, 4, 0x2500}, {46, 4, 0x2500}, STOP};
-struct key K_INS[] = {{50, 4, 'I'}, {51, 4, 'N'}, {52, 4, 'S'}, STOP};
-struct key K_HOM[] = {{54, 4, 'H'}, {55, 4, 'O'}, {56, 4, 'M'}, STOP};
-struct key K_PGU[] = {{58, 4, 'P'}, {59, 4, 'G'}, {60, 4, 'U'}, STOP};
-struct key K_K_NUMLOCK[] = {{65, 4, 'N'}, STOP};
-struct key K_K_SLASH[] = {{68, 4, '/'}, STOP};
-struct key K_K_STAR[] = {{71, 4, '*'}, STOP};
-struct key K_K_MINUS[] = {{74, 4, '-'}, STOP};
-
-struct key K_TAB[] = {{1, 6, 'T'}, {2, 6, 'A'}, {3, 6, 'B'}, STOP};
-struct key K_q[] = {{6, 6, 'q'}, STOP};
-struct key K_Q[] = {{6, 6, 'Q'}, STOP};
-struct key K_w[] = {{9, 6, 'w'}, STOP};
-struct key K_W[] = {{9, 6, 'W'}, STOP};
-struct key K_e[] = {{12, 6, 'e'}, STOP};
-struct key K_E[] = {{12, 6, 'E'}, STOP};
-struct key K_r[] = {{15, 6, 'r'}, STOP};
-struct key K_R[] = {{15, 6, 'R'}, STOP};
-struct key K_t[] = {{18, 6, 't'}, STOP};
-struct key K_T[] = {{18, 6, 'T'}, STOP};
-struct key K_y[] = {{21, 6, 'y'}, STOP};
-struct key K_Y[] = {{21, 6, 'Y'}, STOP};
-struct key K_u[] = {{24, 6, 'u'}, STOP};
-struct key K_U[] = {{24, 6, 'U'}, STOP};
-struct key K_i[] = {{27, 6, 'i'}, STOP};
-struct key K_I[] = {{27, 6, 'I'}, STOP};
-struct key K_o[] = {{30, 6, 'o'}, STOP};
-struct key K_O[] = {{30, 6, 'O'}, STOP};
-struct key K_p[] = {{33, 6, 'p'}, STOP};
-struct key K_P[] = {{33, 6, 'P'}, STOP};
-struct key K_LSQB[] = {{36, 6, '['}, STOP};
-struct key K_LCUB[] = {{36, 6, '{'}, STOP};
-struct key K_RSQB[] = {{39, 6, ']'}, STOP};
-struct key K_RCUB[] = {{39, 6, '}'}, STOP};
-struct key K_ENTER[] =
-{
- {43, 6, 0x2591}, {44, 6, 0x2591}, {45, 6, 0x2591}, {46, 6, 0x2591},
- {43, 7, 0x2591}, {44, 7, 0x2591}, {45, 7, 0x21B5}, {46, 7, 0x2591},
- {41, 8, 0x2591}, {42, 8, 0x2591}, {43, 8, 0x2591}, {44, 8, 0x2591},
- {45, 8, 0x2591}, {46, 8, 0x2591}, STOP
-};
-struct key K_DEL[] = {{50, 6, 'D'}, {51, 6, 'E'}, {52, 6, 'L'}, STOP};
-struct key K_END[] = {{54, 6, 'E'}, {55, 6, 'N'}, {56, 6, 'D'}, STOP};
-struct key K_PGD[] = {{58, 6, 'P'}, {59, 6, 'G'}, {60, 6, 'D'}, STOP};
-struct key K_K_7[] = {{65, 6, '7'}, STOP};
-struct key K_K_8[] = {{68, 6, '8'}, STOP};
-struct key K_K_9[] = {{71, 6, '9'}, STOP};
-struct key K_K_PLUS[] = {{74, 6, ' '}, {74, 7, '+'}, {74, 8, ' '}, STOP};
-
-struct key K_CAPS[] = {{1, 8, 'C'}, {2, 8, 'A'}, {3, 8, 'P'}, {4, 8, 'S'}, STOP};
-struct key K_a[] = {{7, 8, 'a'}, STOP};
-struct key K_A[] = {{7, 8, 'A'}, STOP};
-struct key K_s[] = {{10, 8, 's'}, STOP};
-struct key K_S[] = {{10, 8, 'S'}, STOP};
-struct key K_d[] = {{13, 8, 'd'}, STOP};
-struct key K_D[] = {{13, 8, 'D'}, STOP};
-struct key K_f[] = {{16, 8, 'f'}, STOP};
-struct key K_F[] = {{16, 8, 'F'}, STOP};
-struct key K_g[] = {{19, 8, 'g'}, STOP};
-struct key K_G[] = {{19, 8, 'G'}, STOP};
-struct key K_h[] = {{22, 8, 'h'}, STOP};
-struct key K_H[] = {{22, 8, 'H'}, STOP};
-struct key K_j[] = {{25, 8, 'j'}, STOP};
-struct key K_J[] = {{25, 8, 'J'}, STOP};
-struct key K_k[] = {{28, 8, 'k'}, STOP};
-struct key K_K[] = {{28, 8, 'K'}, STOP};
-struct key K_l[] = {{31, 8, 'l'}, STOP};
-struct key K_L[] = {{31, 8, 'L'}, STOP};
-struct key K_SEMICOLON[] = {{34, 8, ';'}, STOP};
-struct key K_PARENTHESIS[] = {{34, 8, ':'}, STOP};
-struct key K_QUOTE[] = {{37, 8, '\''}, STOP};
-struct key K_DOUBLEQUOTE[] = {{37, 8, '"'}, STOP};
-struct key K_K_4[] = {{65, 8, '4'}, STOP};
-struct key K_K_5[] = {{68, 8, '5'}, STOP};
-struct key K_K_6[] = {{71, 8, '6'}, STOP};
-
-struct key K_LSHIFT[] = {{1, 10, 'S'}, {2, 10, 'H'}, {3, 10, 'I'}, {4, 10, 'F'}, {5, 10, 'T'}, STOP};
-struct key K_z[] = {{9, 10, 'z'}, STOP};
-struct key K_Z[] = {{9, 10, 'Z'}, STOP};
-struct key K_x[] = {{12, 10, 'x'}, STOP};
-struct key K_X[] = {{12, 10, 'X'}, STOP};
-struct key K_c[] = {{15, 10, 'c'}, STOP};
-struct key K_C[] = {{15, 10, 'C'}, STOP};
-struct key K_v[] = {{18, 10, 'v'}, STOP};
-struct key K_V[] = {{18, 10, 'V'}, STOP};
-struct key K_b[] = {{21, 10, 'b'}, STOP};
-struct key K_B[] = {{21, 10, 'B'}, STOP};
-struct key K_n[] = {{24, 10, 'n'}, STOP};
-struct key K_N[] = {{24, 10, 'N'}, STOP};
-struct key K_m[] = {{27, 10, 'm'}, STOP};
-struct key K_M[] = {{27, 10, 'M'}, STOP};
-struct key K_COMMA[] = {{30, 10, ','}, STOP};
-struct key K_LANB[] = {{30, 10, '<'}, STOP};
-struct key K_PERIOD[] = {{33, 10, '.'}, STOP};
-struct key K_RANB[] = {{33, 10, '>'}, STOP};
-struct key K_SLASH[] = {{36, 10, '/'}, STOP};
-struct key K_QUESTION[] = {{36, 10, '?'}, STOP};
-struct key K_RSHIFT[] = {{42, 10, 'S'}, {43, 10, 'H'}, {44, 10, 'I'}, {45, 10, 'F'}, {46, 10, 'T'}, STOP};
-struct key K_ARROW_UP[] = {{54, 10, '('}, {55, 10, 0x2191}, {56, 10, ')'}, STOP};
-struct key K_K_1[] = {{65, 10, '1'}, STOP};
-struct key K_K_2[] = {{68, 10, '2'}, STOP};
-struct key K_K_3[] = {{71, 10, '3'}, STOP};
-struct key K_K_ENTER[] = {{74, 10, 0x2591}, {74, 11, 0x2591}, {74, 12, 0x2591}, STOP};
-
-struct key K_LCTRL[] = {{1, 12, 'C'}, {2, 12, 'T'}, {3, 12, 'R'}, {4, 12, 'L'}, STOP};
-struct key K_LWIN[] = {{6, 12, 'W'}, {7, 12, 'I'}, {8, 12, 'N'}, STOP};
-struct key K_LALT[] = {{10, 12, 'A'}, {11, 12, 'L'}, {12, 12, 'T'}, STOP};
-struct key K_SPACE[] =
-{
- {14, 12, ' '}, {15, 12, ' '}, {16, 12, ' '}, {17, 12, ' '}, {18, 12, ' '},
- {19, 12, 'S'}, {20, 12, 'P'}, {21, 12, 'A'}, {22, 12, 'C'}, {23, 12, 'E'},
- {24, 12, ' '}, {25, 12, ' '}, {26, 12, ' '}, {27, 12, ' '}, {28, 12, ' '},
- STOP
-};
-struct key K_RALT[] = {{30, 12, 'A'}, {31, 12, 'L'}, {32, 12, 'T'}, STOP};
-struct key K_RWIN[] = {{34, 12, 'W'}, {35, 12, 'I'}, {36, 12, 'N'}, STOP};
-struct key K_RPROP[] = {{38, 12, 'P'}, {39, 12, 'R'}, {40, 12, 'O'}, {41, 12, 'P'}, STOP};
-struct key K_RCTRL[] = {{43, 12, 'C'}, {44, 12, 'T'}, {45, 12, 'R'}, {46, 12, 'L'}, STOP};
-struct key K_ARROW_LEFT[] = {{50, 12, '('}, {51, 12, 0x2190}, {52, 12, ')'}, STOP};
-struct key K_ARROW_DOWN[] = {{54, 12, '('}, {55, 12, 0x2193}, {56, 12, ')'}, STOP};
-struct key K_ARROW_RIGHT[] = {{58, 12, '('}, {59, 12, 0x2192}, {60, 12, ')'}, STOP};
-struct key K_K_0[] = {{65, 12, ' '}, {66, 12, '0'}, {67, 12, ' '}, {68, 12, ' '}, STOP};
-struct key K_K_PERIOD[] = {{71, 12, '.'}, STOP};
-
-struct combo
-{
- struct key* keys[6];
-};
-
-struct combo combos[] =
-{
- {{K_TILDE, K_2, K_LCTRL, K_RCTRL, 0}},
- {{K_A, K_LCTRL, K_RCTRL, 0}},
- {{K_B, K_LCTRL, K_RCTRL, 0}},
- {{K_C, K_LCTRL, K_RCTRL, 0}},
- {{K_D, K_LCTRL, K_RCTRL, 0}},
- {{K_E, K_LCTRL, K_RCTRL, 0}},
- {{K_F, K_LCTRL, K_RCTRL, 0}},
- {{K_G, K_LCTRL, K_RCTRL, 0}},
- {{K_H, K_BACKSPACE, K_LCTRL, K_RCTRL, 0}},
- {{K_I, K_TAB, K_LCTRL, K_RCTRL, 0}},
- {{K_J, K_LCTRL, K_RCTRL, 0}},
- {{K_K, K_LCTRL, K_RCTRL, 0}},
- {{K_L, K_LCTRL, K_RCTRL, 0}},
- {{K_M, K_ENTER, K_K_ENTER, K_LCTRL, K_RCTRL, 0}},
- {{K_N, K_LCTRL, K_RCTRL, 0}},
- {{K_O, K_LCTRL, K_RCTRL, 0}},
- {{K_P, K_LCTRL, K_RCTRL, 0}},
- {{K_Q, K_LCTRL, K_RCTRL, 0}},
- {{K_R, K_LCTRL, K_RCTRL, 0}},
- {{K_S, K_LCTRL, K_RCTRL, 0}},
- {{K_T, K_LCTRL, K_RCTRL, 0}},
- {{K_U, K_LCTRL, K_RCTRL, 0}},
- {{K_V, K_LCTRL, K_RCTRL, 0}},
- {{K_W, K_LCTRL, K_RCTRL, 0}},
- {{K_X, K_LCTRL, K_RCTRL, 0}},
- {{K_Y, K_LCTRL, K_RCTRL, 0}},
- {{K_Z, K_LCTRL, K_RCTRL, 0}},
- {{K_LSQB, K_ESC, K_3, K_LCTRL, K_RCTRL, 0}},
- {{K_4, K_BACKSLASH, K_LCTRL, K_RCTRL, 0}},
- {{K_RSQB, K_5, K_LCTRL, K_RCTRL, 0}},
- {{K_6, K_LCTRL, K_RCTRL, 0}},
- {{K_7, K_SLASH, K_MINUS_SHIFT, K_LCTRL, K_RCTRL, 0}},
- {{K_SPACE, 0}},
- {{K_1_SHIFT, K_LSHIFT, K_RSHIFT, 0}},
- {{K_DOUBLEQUOTE, K_LSHIFT, K_RSHIFT, 0}},
- {{K_3_SHIFT, K_LSHIFT, K_RSHIFT, 0}},
- {{K_4_SHIFT, K_LSHIFT, K_RSHIFT, 0}},
- {{K_5_SHIFT, K_LSHIFT, K_RSHIFT, 0}},
- {{K_7_SHIFT, K_LSHIFT, K_RSHIFT, 0}},
- {{K_QUOTE, 0}},
- {{K_9_SHIFT, K_LSHIFT, K_RSHIFT, 0}},
- {{K_0_SHIFT, K_LSHIFT, K_RSHIFT, 0}},
- {{K_8_SHIFT, K_K_STAR, K_LSHIFT, K_RSHIFT, 0}},
- {{K_EQUALS_SHIFT, K_K_PLUS, K_LSHIFT, K_RSHIFT, 0}},
- {{K_COMMA, 0}},
- {{K_MINUS, K_K_MINUS, 0}},
- {{K_PERIOD, K_K_PERIOD, 0}},
- {{K_SLASH, K_K_SLASH, 0}},
- {{K_0, K_K_0, 0}},
- {{K_1, K_K_1, 0}},
- {{K_2, K_K_2, 0}},
- {{K_3, K_K_3, 0}},
- {{K_4, K_K_4, 0}},
- {{K_5, K_K_5, 0}},
- {{K_6, K_K_6, 0}},
- {{K_7, K_K_7, 0}},
- {{K_8, K_K_8, 0}},
- {{K_9, K_K_9, 0}},
- {{K_PARENTHESIS, K_LSHIFT, K_RSHIFT, 0}},
- {{K_SEMICOLON, 0}},
- {{K_LANB, K_LSHIFT, K_RSHIFT, 0}},
- {{K_EQUALS, 0}},
- {{K_RANB, K_LSHIFT, K_RSHIFT, 0}},
- {{K_QUESTION, K_LSHIFT, K_RSHIFT, 0}},
- {{K_2_SHIFT, K_LSHIFT, K_RSHIFT, 0}},
- {{K_A, K_LSHIFT, K_RSHIFT, 0}},
- {{K_B, K_LSHIFT, K_RSHIFT, 0}},
- {{K_C, K_LSHIFT, K_RSHIFT, 0}},
- {{K_D, K_LSHIFT, K_RSHIFT, 0}},
- {{K_E, K_LSHIFT, K_RSHIFT, 0}},
- {{K_F, K_LSHIFT, K_RSHIFT, 0}},
- {{K_G, K_LSHIFT, K_RSHIFT, 0}},
- {{K_H, K_LSHIFT, K_RSHIFT, 0}},
- {{K_I, K_LSHIFT, K_RSHIFT, 0}},
- {{K_J, K_LSHIFT, K_RSHIFT, 0}},
- {{K_K, K_LSHIFT, K_RSHIFT, 0}},
- {{K_L, K_LSHIFT, K_RSHIFT, 0}},
- {{K_M, K_LSHIFT, K_RSHIFT, 0}},
- {{K_N, K_LSHIFT, K_RSHIFT, 0}},
- {{K_O, K_LSHIFT, K_RSHIFT, 0}},
- {{K_P, K_LSHIFT, K_RSHIFT, 0}},
- {{K_Q, K_LSHIFT, K_RSHIFT, 0}},
- {{K_R, K_LSHIFT, K_RSHIFT, 0}},
- {{K_S, K_LSHIFT, K_RSHIFT, 0}},
- {{K_T, K_LSHIFT, K_RSHIFT, 0}},
- {{K_U, K_LSHIFT, K_RSHIFT, 0}},
- {{K_V, K_LSHIFT, K_RSHIFT, 0}},
- {{K_W, K_LSHIFT, K_RSHIFT, 0}},
- {{K_X, K_LSHIFT, K_RSHIFT, 0}},
- {{K_Y, K_LSHIFT, K_RSHIFT, 0}},
- {{K_Z, K_LSHIFT, K_RSHIFT, 0}},
- {{K_LSQB, 0}},
- {{K_BACKSLASH, 0}},
- {{K_RSQB, 0}},
- {{K_6_SHIFT, K_LSHIFT, K_RSHIFT, 0}},
- {{K_MINUS_SHIFT, K_LSHIFT, K_RSHIFT, 0}},
- {{K_TILDE, 0}},
- {{K_a, 0}},
- {{K_b, 0}},
- {{K_c, 0}},
- {{K_d, 0}},
- {{K_e, 0}},
- {{K_f, 0}},
- {{K_g, 0}},
- {{K_h, 0}},
- {{K_i, 0}},
- {{K_j, 0}},
- {{K_k, 0}},
- {{K_l, 0}},
- {{K_m, 0}},
- {{K_n, 0}},
- {{K_o, 0}},
- {{K_p, 0}},
- {{K_q, 0}},
- {{K_r, 0}},
- {{K_s, 0}},
- {{K_t, 0}},
- {{K_u, 0}},
- {{K_v, 0}},
- {{K_w, 0}},
- {{K_x, 0}},
- {{K_y, 0}},
- {{K_z, 0}},
- {{K_LCUB, K_LSHIFT, K_RSHIFT, 0}},
- {{K_BACKSLASH_SHIFT, K_LSHIFT, K_RSHIFT, 0}},
- {{K_RCUB, K_LSHIFT, K_RSHIFT, 0}},
- {{K_TILDE_SHIFT, K_LSHIFT, K_RSHIFT, 0}},
- {{K_8, K_BACKSPACE, K_LCTRL, K_RCTRL, 0}}
-};
-
-struct combo func_combos[] =
-{
- {{K_F1, 0}},
- {{K_F2, 0}},
- {{K_F3, 0}},
- {{K_F4, 0}},
- {{K_F5, 0}},
- {{K_F6, 0}},
- {{K_F7, 0}},
- {{K_F8, 0}},
- {{K_F9, 0}},
- {{K_F10, 0}},
- {{K_F11, 0}},
- {{K_F12, 0}},
- {{K_INS, 0}},
- {{K_DEL, 0}},
- {{K_HOM, 0}},
- {{K_END, 0}},
- {{K_PGU, 0}},
- {{K_PGD, 0}},
- {{K_ARROW_UP, 0}},
- {{K_ARROW_DOWN, 0}},
- {{K_ARROW_LEFT, 0}},
- {{K_ARROW_RIGHT, 0}}
-};
-
-void print_tb(const char* str, int x, int y, uint32_t fg, uint32_t bg)
-{
- while (*str)
- {
- uint32_t uni;
- str += utf8_char_to_unicode(&uni, str);
- tb_change_cell(x, y, uni, fg, bg);
- x++;
- }
-}
-
-void printf_tb(int x, int y, uint32_t fg, uint32_t bg, const char* fmt, ...)
-{
- char buf[4096];
- va_list vl;
- va_start(vl, fmt);
- vsnprintf(buf, sizeof(buf), fmt, vl);
- va_end(vl);
- print_tb(buf, x, y, fg, bg);
-}
-
-void draw_key(struct key* k, uint32_t fg, uint32_t bg)
-{
- while (k->x)
- {
- tb_change_cell(k->x + 2, k->y + 4, k->ch, fg, bg);
- k++;
- }
-}
-
-void draw_keyboard()
-{
- int i;
- tb_change_cell(0, 0, 0x250C, TB_WHITE, TB_DEFAULT);
- tb_change_cell(79, 0, 0x2510, TB_WHITE, TB_DEFAULT);
- tb_change_cell(0, 23, 0x2514, TB_WHITE, TB_DEFAULT);
- tb_change_cell(79, 23, 0x2518, TB_WHITE, TB_DEFAULT);
-
- for (i = 1; i < 79; ++i)
- {
- tb_change_cell(i, 0, 0x2500, TB_WHITE, TB_DEFAULT);
- tb_change_cell(i, 23, 0x2500, TB_WHITE, TB_DEFAULT);
- tb_change_cell(i, 17, 0x2500, TB_WHITE, TB_DEFAULT);
- tb_change_cell(i, 4, 0x2500, TB_WHITE, TB_DEFAULT);
- }
-
- for (i = 1; i < 23; ++i)
- {
- tb_change_cell(0, i, 0x2502, TB_WHITE, TB_DEFAULT);
- tb_change_cell(79, i, 0x2502, TB_WHITE, TB_DEFAULT);
- }
-
- tb_change_cell(0, 17, 0x251C, TB_WHITE, TB_DEFAULT);
- tb_change_cell(79, 17, 0x2524, TB_WHITE, TB_DEFAULT);
- tb_change_cell(0, 4, 0x251C, TB_WHITE, TB_DEFAULT);
- tb_change_cell(79, 4, 0x2524, TB_WHITE, TB_DEFAULT);
-
- for (i = 5; i < 17; ++i)
- {
- tb_change_cell(1, i, 0x2588, TB_YELLOW, TB_YELLOW);
- tb_change_cell(78, i, 0x2588, TB_YELLOW, TB_YELLOW);
- }
-
- draw_key(K_ESC, TB_WHITE, TB_BLUE);
- draw_key(K_F1, TB_WHITE, TB_BLUE);
- draw_key(K_F2, TB_WHITE, TB_BLUE);
- draw_key(K_F3, TB_WHITE, TB_BLUE);
- draw_key(K_F4, TB_WHITE, TB_BLUE);
- draw_key(K_F5, TB_WHITE, TB_BLUE);
- draw_key(K_F6, TB_WHITE, TB_BLUE);
- draw_key(K_F7, TB_WHITE, TB_BLUE);
- draw_key(K_F8, TB_WHITE, TB_BLUE);
- draw_key(K_F9, TB_WHITE, TB_BLUE);
- draw_key(K_F10, TB_WHITE, TB_BLUE);
- draw_key(K_F11, TB_WHITE, TB_BLUE);
- draw_key(K_F12, TB_WHITE, TB_BLUE);
- draw_key(K_PRN, TB_WHITE, TB_BLUE);
- draw_key(K_SCR, TB_WHITE, TB_BLUE);
- draw_key(K_BRK, TB_WHITE, TB_BLUE);
- draw_key(K_LED1, TB_WHITE, TB_BLUE);
- draw_key(K_LED2, TB_WHITE, TB_BLUE);
- draw_key(K_LED3, TB_WHITE, TB_BLUE);
-
- draw_key(K_TILDE, TB_WHITE, TB_BLUE);
- draw_key(K_1, TB_WHITE, TB_BLUE);
- draw_key(K_2, TB_WHITE, TB_BLUE);
- draw_key(K_3, TB_WHITE, TB_BLUE);
- draw_key(K_4, TB_WHITE, TB_BLUE);
- draw_key(K_5, TB_WHITE, TB_BLUE);
- draw_key(K_6, TB_WHITE, TB_BLUE);
- draw_key(K_7, TB_WHITE, TB_BLUE);
- draw_key(K_8, TB_WHITE, TB_BLUE);
- draw_key(K_9, TB_WHITE, TB_BLUE);
- draw_key(K_0, TB_WHITE, TB_BLUE);
- draw_key(K_MINUS, TB_WHITE, TB_BLUE);
- draw_key(K_EQUALS, TB_WHITE, TB_BLUE);
- draw_key(K_BACKSLASH, TB_WHITE, TB_BLUE);
- draw_key(K_BACKSPACE, TB_WHITE, TB_BLUE);
- draw_key(K_INS, TB_WHITE, TB_BLUE);
- draw_key(K_HOM, TB_WHITE, TB_BLUE);
- draw_key(K_PGU, TB_WHITE, TB_BLUE);
- draw_key(K_K_NUMLOCK, TB_WHITE, TB_BLUE);
- draw_key(K_K_SLASH, TB_WHITE, TB_BLUE);
- draw_key(K_K_STAR, TB_WHITE, TB_BLUE);
- draw_key(K_K_MINUS, TB_WHITE, TB_BLUE);
-
- draw_key(K_TAB, TB_WHITE, TB_BLUE);
- draw_key(K_q, TB_WHITE, TB_BLUE);
- draw_key(K_w, TB_WHITE, TB_BLUE);
- draw_key(K_e, TB_WHITE, TB_BLUE);
- draw_key(K_r, TB_WHITE, TB_BLUE);
- draw_key(K_t, TB_WHITE, TB_BLUE);
- draw_key(K_y, TB_WHITE, TB_BLUE);
- draw_key(K_u, TB_WHITE, TB_BLUE);
- draw_key(K_i, TB_WHITE, TB_BLUE);
- draw_key(K_o, TB_WHITE, TB_BLUE);
- draw_key(K_p, TB_WHITE, TB_BLUE);
- draw_key(K_LSQB, TB_WHITE, TB_BLUE);
- draw_key(K_RSQB, TB_WHITE, TB_BLUE);
- draw_key(K_ENTER, TB_WHITE, TB_BLUE);
- draw_key(K_DEL, TB_WHITE, TB_BLUE);
- draw_key(K_END, TB_WHITE, TB_BLUE);
- draw_key(K_PGD, TB_WHITE, TB_BLUE);
- draw_key(K_K_7, TB_WHITE, TB_BLUE);
- draw_key(K_K_8, TB_WHITE, TB_BLUE);
- draw_key(K_K_9, TB_WHITE, TB_BLUE);
- draw_key(K_K_PLUS, TB_WHITE, TB_BLUE);
-
- draw_key(K_CAPS, TB_WHITE, TB_BLUE);
- draw_key(K_a, TB_WHITE, TB_BLUE);
- draw_key(K_s, TB_WHITE, TB_BLUE);
- draw_key(K_d, TB_WHITE, TB_BLUE);
- draw_key(K_f, TB_WHITE, TB_BLUE);
- draw_key(K_g, TB_WHITE, TB_BLUE);
- draw_key(K_h, TB_WHITE, TB_BLUE);
- draw_key(K_j, TB_WHITE, TB_BLUE);
- draw_key(K_k, TB_WHITE, TB_BLUE);
- draw_key(K_l, TB_WHITE, TB_BLUE);
- draw_key(K_SEMICOLON, TB_WHITE, TB_BLUE);
- draw_key(K_QUOTE, TB_WHITE, TB_BLUE);
- draw_key(K_K_4, TB_WHITE, TB_BLUE);
- draw_key(K_K_5, TB_WHITE, TB_BLUE);
- draw_key(K_K_6, TB_WHITE, TB_BLUE);
-
- draw_key(K_LSHIFT, TB_WHITE, TB_BLUE);
- draw_key(K_z, TB_WHITE, TB_BLUE);
- draw_key(K_x, TB_WHITE, TB_BLUE);
- draw_key(K_c, TB_WHITE, TB_BLUE);
- draw_key(K_v, TB_WHITE, TB_BLUE);
- draw_key(K_b, TB_WHITE, TB_BLUE);
- draw_key(K_n, TB_WHITE, TB_BLUE);
- draw_key(K_m, TB_WHITE, TB_BLUE);
- draw_key(K_COMMA, TB_WHITE, TB_BLUE);
- draw_key(K_PERIOD, TB_WHITE, TB_BLUE);
- draw_key(K_SLASH, TB_WHITE, TB_BLUE);
- draw_key(K_RSHIFT, TB_WHITE, TB_BLUE);
- draw_key(K_ARROW_UP, TB_WHITE, TB_BLUE);
- draw_key(K_K_1, TB_WHITE, TB_BLUE);
- draw_key(K_K_2, TB_WHITE, TB_BLUE);
- draw_key(K_K_3, TB_WHITE, TB_BLUE);
- draw_key(K_K_ENTER, TB_WHITE, TB_BLUE);
-
- draw_key(K_LCTRL, TB_WHITE, TB_BLUE);
- draw_key(K_LWIN, TB_WHITE, TB_BLUE);
- draw_key(K_LALT, TB_WHITE, TB_BLUE);
- draw_key(K_SPACE, TB_WHITE, TB_BLUE);
- draw_key(K_RCTRL, TB_WHITE, TB_BLUE);
- draw_key(K_RPROP, TB_WHITE, TB_BLUE);
- draw_key(K_RWIN, TB_WHITE, TB_BLUE);
- draw_key(K_RALT, TB_WHITE, TB_BLUE);
- draw_key(K_ARROW_LEFT, TB_WHITE, TB_BLUE);
- draw_key(K_ARROW_DOWN, TB_WHITE, TB_BLUE);
- draw_key(K_ARROW_RIGHT, TB_WHITE, TB_BLUE);
- draw_key(K_K_0, TB_WHITE, TB_BLUE);
- draw_key(K_K_PERIOD, TB_WHITE, TB_BLUE);
-
- printf_tb(33, 1, TB_MAGENTA | TB_BOLD, TB_DEFAULT, "Keyboard demo!");
- printf_tb(21, 2, TB_MAGENTA, TB_DEFAULT,
- "(press CTRL+X and then CTRL+Q to exit)");
- printf_tb(15, 3, TB_MAGENTA, TB_DEFAULT,
- "(press CTRL+X and then CTRL+C to change input mode)");
-
- int inputmode = tb_select_input_mode(0);
- char inputmode_str[64];
-
- if (inputmode & TB_INPUT_ESC)
- {
- sprintf(inputmode_str, "TB_INPUT_ESC");
- }
-
- if (inputmode & TB_INPUT_ALT)
- {
- sprintf(inputmode_str, "TB_INPUT_ALT");
- }
-
- if (inputmode & TB_INPUT_MOUSE)
- {
- sprintf(inputmode_str + 12, " | TB_INPUT_MOUSE");
- }
-
- printf_tb(3, 18, TB_WHITE, TB_DEFAULT, "Input mode: %s", inputmode_str);
-}
-
-const char* funckeymap(int k)
-{
- static const char* fcmap[] =
- {
- "CTRL+2, CTRL+~",
- "CTRL+A",
- "CTRL+B",
- "CTRL+C",
- "CTRL+D",
- "CTRL+E",
- "CTRL+F",
- "CTRL+G",
- "CTRL+H, BACKSPACE",
- "CTRL+I, TAB",
- "CTRL+J",
- "CTRL+K",
- "CTRL+L",
- "CTRL+M, ENTER",
- "CTRL+N",
- "CTRL+O",
- "CTRL+P",
- "CTRL+Q",
- "CTRL+R",
- "CTRL+S",
- "CTRL+T",
- "CTRL+U",
- "CTRL+V",
- "CTRL+W",
- "CTRL+X",
- "CTRL+Y",
- "CTRL+Z",
- "CTRL+3, ESC, CTRL+[",
- "CTRL+4, CTRL+\\",
- "CTRL+5, CTRL+]",
- "CTRL+6",
- "CTRL+7, CTRL+/, CTRL+_",
- "SPACE"
- };
- static const char* fkmap[] =
- {
- "F1",
- "F2",
- "F3",
- "F4",
- "F5",
- "F6",
- "F7",
- "F8",
- "F9",
- "F10",
- "F11",
- "F12",
- "INSERT",
- "DELETE",
- "HOME",
- "END",
- "PGUP",
- "PGDN",
- "ARROW UP",
- "ARROW DOWN",
- "ARROW LEFT",
- "ARROW RIGHT"
- };
-
- if (k == TB_KEY_CTRL_8)
- {
- return "CTRL+8, BACKSPACE 2"; // 0x7F
- }
- else if (k >= TB_KEY_ARROW_RIGHT && k <= 0xFFFF)
- {
- return fkmap[0xFFFF - k];
- }
- else if (k <= TB_KEY_SPACE)
- {
- return fcmap[k];
- }
-
- return "UNKNOWN";
-}
-
-void pretty_print_press(struct tb_event* ev)
-{
- char buf[7];
- buf[utf8_unicode_to_char(buf, ev->ch)] = '\0';
- printf_tb(3, 19, TB_WHITE, TB_DEFAULT, "Key: ");
- printf_tb(8, 19, TB_YELLOW, TB_DEFAULT, "decimal: %d", ev->key);
- printf_tb(8, 20, TB_GREEN, TB_DEFAULT, "hex: 0x%X", ev->key);
- printf_tb(8, 21, TB_CYAN, TB_DEFAULT, "octal: 0%o", ev->key);
- printf_tb(8, 22, TB_RED, TB_DEFAULT, "string: %s", funckeymap(ev->key));
-
- printf_tb(54, 19, TB_WHITE, TB_DEFAULT, "Char: ");
- printf_tb(60, 19, TB_YELLOW, TB_DEFAULT, "decimal: %d", ev->ch);
- printf_tb(60, 20, TB_GREEN, TB_DEFAULT, "hex: 0x%X", ev->ch);
- printf_tb(60, 21, TB_CYAN, TB_DEFAULT, "octal: 0%o", ev->ch);
- printf_tb(60, 22, TB_RED, TB_DEFAULT, "string: %s", buf);
-
- printf_tb(54, 18, TB_WHITE, TB_DEFAULT, "Modifier: %s",
- (ev->mod) ? "TB_MOD_ALT" : "none");
-
-}
-
-void pretty_print_resize(struct tb_event* ev)
-{
- printf_tb(3, 19, TB_WHITE, TB_DEFAULT, "Resize event: %d x %d", ev->w, ev->h);
-}
-
-int counter = 0;
-
-void pretty_print_mouse(struct tb_event* ev)
-{
- printf_tb(3, 19, TB_WHITE, TB_DEFAULT, "Mouse event: %d x %d", ev->x, ev->y);
- char* btn = "";
-
- switch (ev->key)
- {
- case TB_KEY_MOUSE_LEFT:
- btn = "MouseLeft: %d";
- break;
-
- case TB_KEY_MOUSE_MIDDLE:
- btn = "MouseMiddle: %d";
- break;
-
- case TB_KEY_MOUSE_RIGHT:
- btn = "MouseRight: %d";
- break;
-
- case TB_KEY_MOUSE_WHEEL_UP:
- btn = "MouseWheelUp: %d";
- break;
-
- case TB_KEY_MOUSE_WHEEL_DOWN:
- btn = "MouseWheelDown: %d";
- break;
-
- case TB_KEY_MOUSE_RELEASE:
- btn = "MouseRelease: %d";
- }
-
- counter++;
- printf_tb(43, 19, TB_WHITE, TB_DEFAULT, "Key: ");
- printf_tb(48, 19, TB_YELLOW, TB_DEFAULT, btn, counter);
-}
-
-void dispatch_press(struct tb_event* ev)
-{
- if (ev->mod & TB_MOD_ALT)
- {
- draw_key(K_LALT, TB_WHITE, TB_RED);
- draw_key(K_RALT, TB_WHITE, TB_RED);
- }
-
- struct combo* k = 0;
-
- if (ev->key >= TB_KEY_ARROW_RIGHT)
- {
- k = &func_combos[0xFFFF - ev->key];
- }
- else if (ev->ch < 128)
- {
- if (ev->ch == 0 && ev->key < 128)
- {
- k = &combos[ev->key];
- }
- else
- {
- k = &combos[ev->ch];
- }
- }
-
- if (!k)
- {
- return;
- }
-
- struct key** keys = k->keys;
-
- while (*keys)
- {
- draw_key(*keys, TB_WHITE, TB_RED);
- keys++;
- }
-}
-
-int main(int argc, char** argv)
-{
- (void) argc;
- (void) argv;
- int ret;
-
- ret = tb_init();
-
- if (ret)
- {
- fprintf(stderr, "tb_init() failed with error code %d\n", ret);
- return 1;
- }
-
- tb_select_input_mode(TB_INPUT_ESC | TB_INPUT_MOUSE);
- struct tb_event ev;
-
- tb_clear();
- draw_keyboard();
- tb_present();
- int inputmode = 0;
- int ctrlxpressed = 0;
-
- while (tb_poll_event(&ev))
- {
- switch (ev.type)
- {
- case TB_EVENT_KEY:
- if (ev.key == TB_KEY_CTRL_Q && ctrlxpressed)
- {
- tb_shutdown();
- return 0;
- }
-
- if (ev.key == TB_KEY_CTRL_C && ctrlxpressed)
- {
- static int chmap[] =
- {
- TB_INPUT_ESC | TB_INPUT_MOUSE, // 101
- TB_INPUT_ALT | TB_INPUT_MOUSE, // 110
- TB_INPUT_ESC, // 001
- TB_INPUT_ALT, // 010
- };
- inputmode++;
-
- if (inputmode >= 4)
- {
- inputmode = 0;
- }
-
- tb_select_input_mode(chmap[inputmode]);
- }
-
- if (ev.key == TB_KEY_CTRL_X)
- {
- ctrlxpressed = 1;
- }
- else
- {
- ctrlxpressed = 0;
- }
-
- tb_clear();
- draw_keyboard();
- dispatch_press(&ev);
- pretty_print_press(&ev);
- tb_present();
- break;
-
- case TB_EVENT_RESIZE:
- tb_clear();
- draw_keyboard();
- pretty_print_resize(&ev);
- tb_present();
- break;
-
- case TB_EVENT_MOUSE:
- tb_clear();
- draw_keyboard();
- pretty_print_mouse(&ev);
- tb_present();
- break;
-
- default:
- break;
- }
- }
-
- tb_shutdown();
- return 0;
-}
diff --git a/framework/3rd/termbox/src/demo/makefile b/framework/3rd/termbox/src/demo/makefile
deleted file mode 100755
index 74ec8dd..0000000
--- a/framework/3rd/termbox/src/demo/makefile
+++ /dev/null
@@ -1,30 +0,0 @@
-CC=gcc
-FLAGS=-std=c99 -pedantic -Wall -Werror -g -static
-INCL=-I../
-BIND=../../bin
-
-%.o:%.c
- @echo "building source object $@"
- @$(CC) $(INCL) $(FLAGS) -c -o $@ $<
-
-all:keyboard output paint truecolor
-
-keyboard:keyboard.o
- @echo "compiling $@"
- @$(CC) $(INCL) $(FLAGS) -o $@ $@.o $(BIND)/termbox.a
-
-output:output.o
- @echo "compiling $@"
- @$(CC) $(INCL) $(FLAGS) -o $@ $@.o $(BIND)/termbox.a
-
-paint:paint.o
- @echo "compiling $@"
- @$(CC) $(INCL) $(FLAGS) -o $@ $@.o $(BIND)/termbox.a
-
-truecolor:truecolor.o
- @echo "compiling $@"
- @$(CC) $(INCL) $(FLAGS) -o $@ $@.o $(BIND)/termbox.a
-
-clean:
- @echo "cleaning workspace"
- @rm -rf *.o keyboard output paint truecolor
diff --git a/framework/3rd/termbox/src/demo/output.c b/framework/3rd/termbox/src/demo/output.c
deleted file mode 100755
index 447975e..0000000
--- a/framework/3rd/termbox/src/demo/output.c
+++ /dev/null
@@ -1,156 +0,0 @@
-#include
-#include
-#include "../termbox.h"
-
-static const char chars[] = "nnnnnnnnnbbbbbbbbbuuuuuuuuuBBBBBBBBB";
-
-static const uint32_t all_attrs[] =
-{
- 0,
- TB_BOLD,
- TB_UNDERLINE,
- TB_BOLD | TB_UNDERLINE,
-};
-
-static int next_char(int current)
-{
- current++;
-
- if (!chars[current])
- {
- current = 0;
- }
-
- return current;
-}
-
-static void draw_line(int x, int y, uint32_t bg)
-{
- int a, c;
- int current_char = 0;
-
- for (a = 0; a < 4; a++)
- {
- for (c = TB_DEFAULT; c <= TB_WHITE; c++)
- {
- uint32_t fg = all_attrs[a] | c;
- tb_change_cell(x, y, chars[current_char], fg, bg);
- current_char = next_char(current_char);
- x++;
- }
- }
-}
-
-static void print_combinations_table(int sx, int sy, const uint32_t* attrs,
- int attrs_n)
-{
- int i, c;
-
- for (i = 0; i < attrs_n; i++)
- {
- for (c = TB_DEFAULT; c <= TB_WHITE; c++)
- {
- uint32_t bg = attrs[i] | c;
- draw_line(sx, sy, bg);
- sy++;
- }
- }
-}
-
-static void draw_all()
-{
- tb_clear();
-
- tb_select_output_mode(TB_OUTPUT_NORMAL);
- static const uint32_t col1[] = {0, TB_BOLD};
- static const uint32_t col2[] = {TB_REVERSE};
- print_combinations_table(1, 1, col1, 2);
- print_combinations_table(2 + strlen(chars), 1, col2, 1);
- tb_present();
-
- tb_select_output_mode(TB_OUTPUT_GRAYSCALE);
- int c, x, y;
-
- for (x = 0, y = 23; x < 24; ++x)
- {
- tb_change_cell(x, y, '@', x, 0);
- tb_change_cell(x + 25, y, ' ', 0, x);
- }
-
- tb_present();
-
- tb_select_output_mode(TB_OUTPUT_216);
- y++;
-
- for (c = 0, x = 0; c < 216; ++c, ++x)
- {
- if (!(x % 24))
- {
- x = 0;
- ++y;
- }
-
- tb_change_cell(x, y, '@', c, 0);
- tb_change_cell(x + 25, y, ' ', 0, c);
- }
-
- tb_present();
-
- tb_select_output_mode(TB_OUTPUT_256);
- y++;
-
- for (c = 0, x = 0; c < 256; ++c, ++x)
- {
- if (!(x % 24))
- {
- x = 0;
- ++y;
- }
-
- tb_change_cell(x, y, '+', c | ((y & 1) ? TB_UNDERLINE : 0), 0);
- tb_change_cell(x + 25, y, ' ', 0, c);
- }
-
- tb_present();
-}
-
-int main(int argc, char** argv)
-{
- (void)argc;
- (void)argv;
- int ret = tb_init();
-
- if (ret)
- {
- fprintf(stderr, "tb_init() failed with error code %d\n", ret);
- return 1;
- }
-
- draw_all();
-
- struct tb_event ev;
-
- while (tb_poll_event(&ev))
- {
- switch (ev.type)
- {
- case TB_EVENT_KEY:
- switch (ev.key)
- {
- case TB_KEY_ESC:
- goto done;
- break;
- }
-
- break;
-
- case TB_EVENT_RESIZE:
- draw_all();
- break;
- }
- }
-
-done:
- tb_shutdown();
- return 0;
-}
diff --git a/framework/3rd/termbox/src/demo/paint.c b/framework/3rd/termbox/src/demo/paint.c
deleted file mode 100755
index 1f68c2d..0000000
--- a/framework/3rd/termbox/src/demo/paint.c
+++ /dev/null
@@ -1,183 +0,0 @@
-#include "../termbox.h"
-#include
-#include
-#include
-
-static int curCol = 0;
-static int curRune = 0;
-static struct tb_cell* backbuf;
-static int bbw = 0, bbh = 0;
-
-static const uint32_t runes[] =
-{
- 0x20, // ' '
- 0x2591, // '░'
- 0x2592, // '▒'
- 0x2593, // '▓'
- 0x2588, // '█'
-};
-
-#define len(a) (sizeof(a)/sizeof(a[0]))
-
-static const uint32_t colors[] =
-{
- TB_BLACK,
- TB_RED,
- TB_GREEN,
- TB_YELLOW,
- TB_BLUE,
- TB_MAGENTA,
- TB_CYAN,
- TB_WHITE,
-};
-
-void updateAndDrawButtons(int* current, int x, int y, int mx, int my, int n,
- void (*attrFunc)(int, uint32_t*, uint32_t*, uint32_t*))
-{
- int lx = x;
- int ly = y;
-
- for (int i = 0; i < n; i++)
- {
- if (lx <= mx && mx <= lx + 3 && ly <= my && my <= ly + 1)
- {
- *current = i;
- }
-
- uint32_t r;
- uint32_t fg, bg;
- (*attrFunc)(i, &r, &fg, &bg);
- tb_change_cell(lx + 0, ly + 0, r, fg, bg);
- tb_change_cell(lx + 1, ly + 0, r, fg, bg);
- tb_change_cell(lx + 2, ly + 0, r, fg, bg);
- tb_change_cell(lx + 3, ly + 0, r, fg, bg);
- tb_change_cell(lx + 0, ly + 1, r, fg, bg);
- tb_change_cell(lx + 1, ly + 1, r, fg, bg);
- tb_change_cell(lx + 2, ly + 1, r, fg, bg);
- tb_change_cell(lx + 3, ly + 1, r, fg, bg);
- lx += 4;
- }
-
- lx = x;
- ly = y;
-
- for (int i = 0; i < n; i++)
- {
- if (*current == i)
- {
- uint32_t fg = TB_RED | TB_BOLD;
- uint32_t bg = TB_DEFAULT;
- tb_change_cell(lx + 0, ly + 2, '^', fg, bg);
- tb_change_cell(lx + 1, ly + 2, '^', fg, bg);
- tb_change_cell(lx + 2, ly + 2, '^', fg, bg);
- tb_change_cell(lx + 3, ly + 2, '^', fg, bg);
- }
-
- lx += 4;
- }
-}
-
-void runeAttrFunc(int i, uint32_t* r, uint32_t* fg, uint32_t* bg)
-{
- *r = runes[i];
- *fg = TB_DEFAULT;
- *bg = TB_DEFAULT;
-}
-
-void colorAttrFunc(int i, uint32_t* r, uint32_t* fg, uint32_t* bg)
-{
- *r = ' ';
- *fg = TB_DEFAULT;
- *bg = colors[i];
-}
-
-void updateAndRedrawAll(int mx, int my)
-{
- tb_clear();
-
- if (mx != -1 && my != -1)
- {
- backbuf[bbw * my + mx].ch = runes[curRune];
- backbuf[bbw * my + mx].fg = colors[curCol];
- }
-
- memcpy(tb_cell_buffer(), backbuf, sizeof(struct tb_cell)*bbw * bbh);
- int h = tb_height();
- updateAndDrawButtons(&curRune, 0, 0, mx, my, len(runes), runeAttrFunc);
- updateAndDrawButtons(&curCol, 0, h - 3, mx, my, len(colors), colorAttrFunc);
- tb_present();
-}
-
-void reallocBackBuffer(int w, int h)
-{
- bbw = w;
- bbh = h;
-
- if (backbuf)
- {
- free(backbuf);
- }
-
- backbuf = calloc(sizeof(struct tb_cell), w * h);
-}
-
-int main(int argv, char** argc)
-{
- (void)argc;
- (void)argv;
- int code = tb_init();
-
- if (code < 0)
- {
- fprintf(stderr, "termbox init failed, code: %d\n", code);
- return -1;
- }
-
- tb_select_input_mode(TB_INPUT_ESC | TB_INPUT_MOUSE);
- int w = tb_width();
- int h = tb_height();
- reallocBackBuffer(w, h);
- updateAndRedrawAll(-1, -1);
-
- for (;;)
- {
- struct tb_event ev;
- int mx = -1;
- int my = -1;
- int t = tb_poll_event(&ev);
-
- if (t == -1)
- {
- tb_shutdown();
- fprintf(stderr, "termbox poll event error\n");
- return -1;
- }
-
- switch (t)
- {
- case TB_EVENT_KEY:
- if (ev.key == TB_KEY_ESC)
- {
- tb_shutdown();
- return 0;
- }
-
- break;
-
- case TB_EVENT_MOUSE:
- if (ev.key == TB_KEY_MOUSE_LEFT)
- {
- mx = ev.x;
- my = ev.y;
- }
-
- break;
-
- case TB_EVENT_RESIZE:
- reallocBackBuffer(ev.w, ev.h);
- break;
- }
-
- updateAndRedrawAll(mx, my);
- }
-}
diff --git a/framework/3rd/termbox/src/demo/truecolor.c b/framework/3rd/termbox/src/demo/truecolor.c
deleted file mode 100755
index 33609fd..0000000
--- a/framework/3rd/termbox/src/demo/truecolor.c
+++ /dev/null
@@ -1,69 +0,0 @@
-#include "termbox.h"
-
-int main()
-{
- tb_init();
- tb_select_output_mode(TB_OUTPUT_TRUECOLOR);
- int w = tb_width();
- int h = tb_height();
- uint32_t bg = 0x000000, fg = 0x000000;
- tb_clear();
- int z = 0;
-
- for (int y = 1; y < h; y++)
- {
- for (int x = 1; x < w; x++)
- {
- uint32_t ch;
- utf8_char_to_unicode(&ch, "x");
- fg = 0;
-
- if (z % 2 == 0)
- {
- fg |= TB_BOLD;
- }
-
- if (z % 3 == 0)
- {
- fg |= TB_UNDERLINE;
- }
-
- if (z % 5 == 0)
- {
- fg |= TB_REVERSE;
- }
-
- tb_change_cell(x, y, ch, fg, bg);
- bg += 0x000101;
- z++;
- }
-
- bg += 0x080000;
-
- if (bg > 0xFFFFFF)
- {
- bg = 0;
- }
- }
-
- tb_present();
-
- while (1)
- {
- struct tb_event ev;
- int t = tb_poll_event(&ev);
-
- if (t == -1)
- {
- break;
- }
-
- if (t == TB_EVENT_KEY)
- {
- break;
- }
- }
-
- tb_shutdown();
- return 0;
-}
diff --git a/framework/3rd/termbox/src/input.c b/framework/3rd/termbox/src/input.c
deleted file mode 100755
index 2d3f84d..0000000
--- a/framework/3rd/termbox/src/input.c
+++ /dev/null
@@ -1,319 +0,0 @@
-#include
-#include
-#include
-#include
-#include
-
-#include "term.h"
-
-#define BUFFER_SIZE_MAX 16
-
-// if s1 starts with s2 returns 1, else 0
-static int starts_with(const char* s1, const char* s2)
-{
- // nice huh?
- while (*s2)
- {
- if (*s1++ != *s2++)
- {
- return 0;
- }
- }
-
- return 1;
-}
-
-static int parse_mouse_event(struct tb_event* event, const char* buf, int len)
-{
- if ((len >= 6) && starts_with(buf, "\033[M"))
- {
- // X10 mouse encoding, the simplest one
- // \033 [ M Cb Cx Cy
- int b = buf[3] - 32;
-
- switch (b & 3)
- {
- case 0:
- if ((b & 64) != 0)
- {
- event->key = TB_KEY_MOUSE_WHEEL_UP;
- }
- else
- {
- event->key = TB_KEY_MOUSE_LEFT;
- }
-
- break;
-
- case 1:
- if ((b & 64) != 0)
- {
- event->key = TB_KEY_MOUSE_WHEEL_DOWN;
- }
- else
- {
- event->key = TB_KEY_MOUSE_MIDDLE;
- }
-
- break;
-
- case 2:
- event->key = TB_KEY_MOUSE_RIGHT;
- break;
-
- case 3:
- event->key = TB_KEY_MOUSE_RELEASE;
- break;
-
- default:
- return -6;
- }
-
- event->type = TB_EVENT_MOUSE; // TB_EVENT_KEY by default
-
- if ((b & 32) != 0)
- {
- event->mod |= TB_MOD_MOTION;
- }
-
- // the coord is 1,1 for upper left
- event->x = (uint8_t)buf[4] - 1 - 32;
- event->y = (uint8_t)buf[5] - 1 - 32;
- return 6;
- }
- else if (starts_with(buf, "\033[<") || starts_with(buf, "\033["))
- {
- // xterm 1006 extended mode or urxvt 1015 extended mode
- // xterm: \033 [ < Cb ; Cx ; Cy (M or m)
- // urxvt: \033 [ Cb ; Cx ; Cy M
- int i, mi = -1, starti = -1;
- int isM, isU, s1 = -1, s2 = -1;
- int n1 = 0, n2 = 0, n3 = 0;
-
- for (i = 0; i < len; i++)
- {
- // We search the first (s1) and the last (s2) ';'
- if (buf[i] == ';')
- {
- if (s1 == -1)
- {
- s1 = i;
- }
-
- s2 = i;
- }
-
- // We search for the first 'm' or 'M'
- if ((buf[i] == 'm' || buf[i] == 'M') && mi == -1)
- {
- mi = i;
- break;
- }
- }
-
- if (mi == -1)
- {
- return 0;
- }
-
- // whether it's a capital M or not
- isM = (buf[mi] == 'M');
-
- if (buf[2] == '<')
- {
- isU = 0;
- starti = 3;
- }
- else
- {
- isU = 1;
- starti = 2;
- }
-
- if (s1 == -1 || s2 == -1 || s1 == s2)
- {
- return 0;
- }
-
- n1 = strtoul(&buf[starti], NULL, 10);
- n2 = strtoul(&buf[s1 + 1], NULL, 10);
- n3 = strtoul(&buf[s2 + 1], NULL, 10);
-
- if (isU)
- {
- n1 -= 32;
- }
-
- switch (n1 & 3)
- {
- case 0:
- if ((n1 & 64) != 0)
- {
- event->key = TB_KEY_MOUSE_WHEEL_UP;
- }
- else
- {
- event->key = TB_KEY_MOUSE_LEFT;
- }
-
- break;
-
- case 1:
- if ((n1 & 64) != 0)
- {
- event->key = TB_KEY_MOUSE_WHEEL_DOWN;
- }
- else
- {
- event->key = TB_KEY_MOUSE_MIDDLE;
- }
-
- break;
-
- case 2:
- event->key = TB_KEY_MOUSE_RIGHT;
- break;
-
- case 3:
- event->key = TB_KEY_MOUSE_RELEASE;
- break;
-
- default:
- return mi + 1;
- }
-
- if (!isM)
- {
- // on xterm mouse release is signaled by lowercase m
- event->key = TB_KEY_MOUSE_RELEASE;
- }
-
- event->type = TB_EVENT_MOUSE; // TB_EVENT_KEY by default
-
- if ((n1 & 32) != 0)
- {
- event->mod |= TB_MOD_MOTION;
- }
-
- event->x = (uint8_t)n2 - 1;
- event->y = (uint8_t)n3 - 1;
- return mi + 1;
- }
-
- return 0;
-}
-
-// convert escape sequence to event, and return consumed bytes on success (failure == 0)
-static int parse_escape_seq(struct tb_event* event, const char* buf, int len)
-{
- int mouse_parsed = parse_mouse_event(event, buf, len);
-
- if (mouse_parsed != 0)
- {
- return mouse_parsed;
- }
-
- // it's pretty simple here, find 'starts_with' match and return success, else return failure
- int i;
-
- for (i = 0; keys[i]; i++)
- {
- if (starts_with(buf, keys[i]))
- {
- event->ch = 0;
- event->key = 0xFFFF - i;
- return strlen(keys[i]);
- }
- }
-
- return 0;
-}
-
-bool extract_event(struct tb_event* event, struct ringbuffer* inbuf,
- int inputmode)
-{
- char buf[BUFFER_SIZE_MAX + 1];
- int nbytes = ringbuffer_data_size(inbuf);
-
- if (nbytes > BUFFER_SIZE_MAX)
- {
- nbytes = BUFFER_SIZE_MAX;
- }
-
- if (nbytes == 0)
- {
- return false;
- }
-
- ringbuffer_read(inbuf, buf, nbytes);
- buf[nbytes] = '\0';
-
- if (buf[0] == '\033')
- {
- int n = parse_escape_seq(event, buf, nbytes);
-
- if (n != 0)
- {
- bool success = true;
-
- if (n < 0)
- {
- success = false;
- n = -n;
- }
-
- ringbuffer_pop(inbuf, 0, n);
- return success;
- }
- else
- {
- // it's not escape sequence, then it's ALT or ESC, check inputmode
- if (inputmode & TB_INPUT_ESC)
- {
- // if we're in escape mode, fill ESC event, pop buffer, return success
- event->ch = 0;
- event->key = TB_KEY_ESC;
- event->mod = 0;
- ringbuffer_pop(inbuf, 0, 1);
- return true;
- }
- else if (inputmode & TB_INPUT_ALT)
- {
- // if we're in alt mode, set ALT modifier to event and redo parsing
- event->mod = TB_MOD_ALT;
- ringbuffer_pop(inbuf, 0, 1);
- return extract_event(event, inbuf, inputmode);
- }
-
- assert(!"never got here");
- }
- }
-
- // if we're here, this is not an escape sequence and not an alt sequence
- // so, it's a FUNCTIONAL KEY or a UNICODE character
-
- // first of all check if it's a functional key*/
- if ((unsigned char)buf[0] <= TB_KEY_SPACE ||
- (unsigned char)buf[0] == TB_KEY_BACKSPACE2)
- {
- // fill event, pop buffer, return success
- event->ch = 0;
- event->key = (uint16_t)buf[0];
- ringbuffer_pop(inbuf, 0, 1);
- return true;
- }
-
- // feh... we got utf8 here
-
- // check if there is all bytes
- if (nbytes >= utf8_char_length(buf[0]))
- {
- // everything ok, fill event, pop buffer, return success
- utf8_char_to_unicode(&event->ch, buf);
- event->key = 0;
- ringbuffer_pop(inbuf, 0, utf8_char_length(buf[0]));
- return true;
- }
-
- return false;
-}
diff --git a/framework/3rd/termbox/src/memstream.c b/framework/3rd/termbox/src/memstream.c
deleted file mode 100755
index d218b54..0000000
--- a/framework/3rd/termbox/src/memstream.c
+++ /dev/null
@@ -1,36 +0,0 @@
-#include
-#include
-#include
-#include "memstream.h"
-
-void memstream_init(struct memstream* s, int fd, void* buffer, size_t len)
-{
- s->file = fd;
- s->data = buffer;
- s->pos = 0;
- s->capa = len;
-}
-
-void memstream_flush(struct memstream* s)
-{
- write(s->file, s->data, s->pos);
- s->pos = 0;
-}
-
-void memstream_write(struct memstream* s, void* source, size_t len)
-{
- unsigned char* data = source;
-
- if (s->pos + len > s->capa)
- {
- memstream_flush(s);
- }
-
- memcpy(s->data + s->pos, data, len);
- s->pos += len;
-}
-
-void memstream_puts(struct memstream* s, const char* str)
-{
- memstream_write(s, (void*) str, strlen(str));
-}
diff --git a/framework/3rd/termbox/src/memstream.h b/framework/3rd/termbox/src/memstream.h
deleted file mode 100755
index c5d864a..0000000
--- a/framework/3rd/termbox/src/memstream.h
+++ /dev/null
@@ -1,20 +0,0 @@
-#ifndef H_MEMSTREAM
-#define H_MEMSTREAM
-
-#include
-#include
-
-struct memstream
-{
- size_t pos;
- size_t capa;
- int file;
- unsigned char* data;
-};
-
-void memstream_init(struct memstream* s, int fd, void* buffer, size_t len);
-void memstream_flush(struct memstream* s);
-void memstream_write(struct memstream* s, void* source, size_t len);
-void memstream_puts(struct memstream* s, const char* str);
-
-#endif
diff --git a/framework/3rd/termbox/src/ringbuffer.c b/framework/3rd/termbox/src/ringbuffer.c
deleted file mode 100755
index a8de0f6..0000000
--- a/framework/3rd/termbox/src/ringbuffer.c
+++ /dev/null
@@ -1,195 +0,0 @@
-#include "ringbuffer.h"
-#include
-#include
-#include
-#include // for ptrdiff_t
-
-int init_ringbuffer(struct ringbuffer* r, size_t size)
-{
- r->buf = (char*)malloc(size);
-
- if (!r->buf)
- {
- return ERINGBUFFER_ALLOC_FAIL;
- }
-
- r->size = size;
- clear_ringbuffer(r);
-
- return 0;
-}
-
-void free_ringbuffer(struct ringbuffer* r)
-{
- free(r->buf);
-}
-
-void clear_ringbuffer(struct ringbuffer* r)
-{
- r->begin = 0;
- r->end = 0;
-}
-
-size_t ringbuffer_free_space(struct ringbuffer* r)
-{
- if (r->begin == 0 && r->end == 0)
- {
- return r->size;
- }
-
- if (r->begin < r->end)
- {
- return r->size - (r->end - r->begin) - 1;
- }
- else
- {
- return r->begin - r->end - 1;
- }
-}
-
-size_t ringbuffer_data_size(struct ringbuffer* r)
-{
- if (r->begin == 0 && r->end == 0)
- {
- return 0;
- }
-
- if (r->begin <= r->end)
- {
- return r->end - r->begin + 1;
- }
- else
- {
- return r->size - (r->begin - r->end) + 1;
- }
-}
-
-
-void ringbuffer_push(struct ringbuffer* r, const void* data, size_t size)
-{
- if (ringbuffer_free_space(r) < size)
- {
- return;
- }
-
- if (r->begin == 0 && r->end == 0)
- {
- memcpy(r->buf, data, size);
- r->begin = r->buf;
- r->end = r->buf + size - 1;
- return;
- }
-
- r->end++;
-
- if (r->begin < r->end)
- {
- if ((size_t)(r->buf + (ptrdiff_t)r->size - r->begin) >= size)
- {
- // we can fit without cut
- memcpy(r->end, data, size);
- r->end += size - 1;
- }
- else
- {
- // make a cut
- size_t s = r->buf + r->size - r->end;
- memcpy(r->end, data, s);
- size -= s;
- memcpy(r->buf, (char*)data + s, size);
- r->end = r->buf + size - 1;
- }
- }
- else
- {
- memcpy(r->end, data, size);
- r->end += size - 1;
- }
-}
-
-void ringbuffer_pop(struct ringbuffer* r, void* data, size_t size)
-{
- if (ringbuffer_data_size(r) < size)
- {
- return;
- }
-
- int need_clear = 0;
-
- if (ringbuffer_data_size(r) == size)
- {
- need_clear = 1;
- }
-
- if (r->begin < r->end)
- {
- if (data)
- {
- memcpy(data, r->begin, size);
- }
-
- r->begin += size;
- }
- else
- {
- if ((size_t)(r->buf + (ptrdiff_t)r->size - r->begin) >= size)
- {
- if (data)
- {
- memcpy(data, r->begin, size);
- }
-
- r->begin += size;
- }
- else
- {
- size_t s = r->buf + r->size - r->begin;
-
- if (data)
- {
- memcpy(data, r->begin, s);
- }
-
- size -= s;
-
- if (data)
- {
- memcpy((char*)data + s, r->buf, size);
- }
-
- r->begin = r->buf + size;
- }
- }
-
- if (need_clear)
- {
- clear_ringbuffer(r);
- }
-}
-
-void ringbuffer_read(struct ringbuffer* r, void* data, size_t size)
-{
- if (ringbuffer_data_size(r) < size)
- {
- return;
- }
-
- if (r->begin < r->end)
- {
- memcpy(data, r->begin, size);
- }
- else
- {
- if ((size_t)(r->buf + (ptrdiff_t)r->size - r->begin) >= size)
- {
- memcpy(data, r->begin, size);
- }
- else
- {
- size_t s = r->buf + r->size - r->begin;
- memcpy(data, r->begin, s);
- size -= s;
- memcpy((char*)data + s, r->buf, size);
- }
- }
-}
diff --git a/framework/3rd/termbox/src/ringbuffer.h b/framework/3rd/termbox/src/ringbuffer.h
deleted file mode 100755
index 9a8b0d6..0000000
--- a/framework/3rd/termbox/src/ringbuffer.h
+++ /dev/null
@@ -1,26 +0,0 @@
-#ifndef H_RINGBUFFER
-#define H_RINGBUFFER
-
-#include
-
-#define ERINGBUFFER_ALLOC_FAIL -1
-
-struct ringbuffer
-{
- char* buf;
- size_t size;
-
- char* begin;
- char* end;
-};
-
-int init_ringbuffer(struct ringbuffer* r, size_t size);
-void free_ringbuffer(struct ringbuffer* r);
-void clear_ringbuffer(struct ringbuffer* r);
-size_t ringbuffer_free_space(struct ringbuffer* r);
-size_t ringbuffer_data_size(struct ringbuffer* r);
-void ringbuffer_push(struct ringbuffer* r, const void* data, size_t size);
-void ringbuffer_pop(struct ringbuffer* r, void* data, size_t size);
-void ringbuffer_read(struct ringbuffer* r, void* data, size_t size);
-
-#endif
diff --git a/framework/3rd/termbox/src/term.c b/framework/3rd/termbox/src/term.c
deleted file mode 100755
index c94f394..0000000
--- a/framework/3rd/termbox/src/term.c
+++ /dev/null
@@ -1,412 +0,0 @@
-#include
-#include
-#include
-#include
-#include
-
-#include "term.h"
-#define ENTER_MOUSE_SEQ "\x1b[?1000h\x1b[?1002h\x1b[?1015h\x1b[?1006h"
-#define EXIT_MOUSE_SEQ "\x1b[?1006l\x1b[?1015l\x1b[?1002l\x1b[?1000l"
-
-#define EUNSUPPORTED_TERM -1
-
-// rxvt-256color
-static const char* rxvt_256color_keys[] =
-{
- "\033[11~", "\033[12~", "\033[13~", "\033[14~", "\033[15~", "\033[17~",
- "\033[18~", "\033[19~", "\033[20~", "\033[21~", "\033[23~", "\033[24~",
- "\033[2~", "\033[3~", "\033[7~", "\033[8~", "\033[5~", "\033[6~",
- "\033[A", "\033[B", "\033[D", "\033[C", NULL
-};
-static const char* rxvt_256color_funcs[] =
-{
- "\0337\033[?47h", "\033[2J\033[?47l\0338", "\033[?25h", "\033[?25l",
- "\033[H\033[2J", "\033[m", "\033[4m", "\033[1m", "\033[5m", "\033[7m",
- "\033=", "\033>", ENTER_MOUSE_SEQ, EXIT_MOUSE_SEQ,
-};
-
-// Eterm
-static const char* eterm_keys[] =
-{
- "\033[11~", "\033[12~", "\033[13~", "\033[14~", "\033[15~", "\033[17~",
- "\033[18~", "\033[19~", "\033[20~", "\033[21~", "\033[23~", "\033[24~",
- "\033[2~", "\033[3~", "\033[7~", "\033[8~", "\033[5~", "\033[6~",
- "\033[A", "\033[B", "\033[D", "\033[C", NULL
-};
-static const char* eterm_funcs[] =
-{
- "\0337\033[?47h", "\033[2J\033[?47l\0338", "\033[?25h", "\033[?25l",
- "\033[H\033[2J", "\033[m", "\033[4m", "\033[1m", "\033[5m", "\033[7m",
- "", "", "", "",
-};
-
-// screen
-static const char* screen_keys[] =
-{
- "\033OP", "\033OQ", "\033OR", "\033OS", "\033[15~", "\033[17~",
- "\033[18~", "\033[19~", "\033[20~", "\033[21~", "\033[23~", "\033[24~",
- "\033[2~", "\033[3~", "\033[1~", "\033[4~", "\033[5~", "\033[6~",
- "\033OA", "\033OB", "\033OD", "\033OC", NULL
-};
-static const char* screen_funcs[] =
-{
- "\033[?1049h", "\033[?1049l", "\033[34h\033[?25h", "\033[?25l",
- "\033[H\033[J", "\033[m", "\033[4m", "\033[1m", "\033[5m", "\033[7m",
- "\033[?1h\033=", "\033[?1l\033>", ENTER_MOUSE_SEQ, EXIT_MOUSE_SEQ,
-};
-
-// rxvt-unicode
-static const char* rxvt_unicode_keys[] =
-{
- "\033[11~", "\033[12~", "\033[13~", "\033[14~", "\033[15~", "\033[17~",
- "\033[18~", "\033[19~", "\033[20~", "\033[21~", "\033[23~", "\033[24~",
- "\033[2~", "\033[3~", "\033[7~", "\033[8~", "\033[5~", "\033[6~",
- "\033[A", "\033[B", "\033[D", "\033[C", NULL
-};
-static const char* rxvt_unicode_funcs[] =
-{
- "\033[?1049h", "\033[r\033[?1049l", "\033[?25h", "\033[?25l",
- "\033[H\033[2J", "\033[m\033(B", "\033[4m", "\033[1m", "\033[5m",
- "\033[7m", "\033=", "\033>", ENTER_MOUSE_SEQ, EXIT_MOUSE_SEQ,
-};
-
-// linux
-static const char* linux_keys[] =
-{
- "\033[[A", "\033[[B", "\033[[C", "\033[[D", "\033[[E", "\033[17~",
- "\033[18~", "\033[19~", "\033[20~", "\033[21~", "\033[23~", "\033[24~",
- "\033[2~", "\033[3~", "\033[1~", "\033[4~", "\033[5~", "\033[6~",
- "\033[A", "\033[B", "\033[D", "\033[C", NULL
-};
-static const char* linux_funcs[] =
-{
- "", "", "\033[?25h\033[?0c", "\033[?25l\033[?1c", "\033[H\033[J",
- "\033[0;10m", "\033[4m", "\033[1m", "\033[5m", "\033[7m", "", "", "", "",
-};
-
-// xterm
-static const char* xterm_keys[] =
-{
- "\033OP", "\033OQ", "\033OR", "\033OS", "\033[15~", "\033[17~", "\033[18~",
- "\033[19~", "\033[20~", "\033[21~", "\033[23~", "\033[24~", "\033[2~",
- "\033[3~", "\033OH", "\033OF", "\033[5~", "\033[6~", "\033OA", "\033OB",
- "\033OD", "\033OC", NULL
-};
-static const char* xterm_funcs[] =
-{
- "\033[?1049h", "\033[?1049l", "\033[?12l\033[?25h", "\033[?25l",
- "\033[H\033[2J", "\033(B\033[m", "\033[4m", "\033[1m", "\033[5m", "\033[7m",
- "\033[?1h\033=", "\033[?1l\033>", ENTER_MOUSE_SEQ, EXIT_MOUSE_SEQ,
-};
-
-struct term
-{
- const char* name;
- const char** keys;
- const char** funcs;
-};
-
-static struct term terms[] =
-{
- {"rxvt-256color", rxvt_256color_keys, rxvt_256color_funcs},
- {"Eterm", eterm_keys, eterm_funcs},
- {"screen", screen_keys, screen_funcs},
- {"rxvt-unicode", rxvt_unicode_keys, rxvt_unicode_funcs},
- {"linux", linux_keys, linux_funcs},
- {"xterm", xterm_keys, xterm_funcs},
- {0, 0, 0},
-};
-
-static int init_from_terminfo = 0;
-const char** keys;
-const char** funcs;
-
-static int try_compatible(const char* term, const char* name,
- const char** tkeys, const char** tfuncs)
-{
- if (strstr(term, name))
- {
- keys = tkeys;
- funcs = tfuncs;
- return 0;
- }
-
- return EUNSUPPORTED_TERM;
-}
-
-static int init_term_builtin(void)
-{
- int i;
- const char* term = getenv("TERM");
-
- if (term)
- {
- for (i = 0; terms[i].name; i++)
- {
- if (!strcmp(terms[i].name, term))
- {
- keys = terms[i].keys;
- funcs = terms[i].funcs;
- return 0;
- }
- }
-
- // let's do some heuristic, maybe it's a compatible terminal
- if (try_compatible(term, "xterm", xterm_keys, xterm_funcs) == 0)
- {
- return 0;
- }
-
- if (try_compatible(term, "rxvt", rxvt_unicode_keys, rxvt_unicode_funcs) == 0)
- {
- return 0;
- }
-
- if (try_compatible(term, "linux", linux_keys, linux_funcs) == 0)
- {
- return 0;
- }
-
- if (try_compatible(term, "Eterm", eterm_keys, eterm_funcs) == 0)
- {
- return 0;
- }
-
- if (try_compatible(term, "screen", screen_keys, screen_funcs) == 0)
- {
- return 0;
- }
-
- // let's assume that 'cygwin' is xterm compatible
- if (try_compatible(term, "cygwin", xterm_keys, xterm_funcs) == 0)
- {
- return 0;
- }
- }
-
- return EUNSUPPORTED_TERM;
-}
-
-// terminfo
-static char* read_file(const char* file)
-{
- FILE* f = fopen(file, "rb");
-
- if (!f)
- {
- return 0;
- }
-
- struct stat st;
-
- if (fstat(fileno(f), &st) != 0)
- {
- fclose(f);
- return 0;
- }
-
- char* data = malloc(st.st_size);
-
- if (!data)
- {
- fclose(f);
- return 0;
- }
-
- if (fread(data, 1, st.st_size, f) != (size_t)st.st_size)
- {
- fclose(f);
- free(data);
- return 0;
- }
-
- fclose(f);
- return data;
-}
-
-static char* terminfo_try_path(const char* path, const char* term)
-{
- char tmp[4096];
- snprintf(tmp, sizeof(tmp), "%s/%c/%s", path, term[0], term);
- tmp[sizeof(tmp) - 1] = '\0';
- char* data = read_file(tmp);
-
- if (data)
- {
- return data;
- }
-
- // fallback to darwin specific dirs structure
- snprintf(tmp, sizeof(tmp), "%s/%x/%s", path, term[0], term);
- tmp[sizeof(tmp) - 1] = '\0';
- return read_file(tmp);
-}
-
-static char* load_terminfo(void)
-{
- char tmp[4096];
- const char* term = getenv("TERM");
-
- if (!term)
- {
- return 0;
- }
-
- // if TERMINFO is set, no other directory should be searched
- const char* terminfo = getenv("TERMINFO");
-
- if (terminfo)
- {
- return terminfo_try_path(terminfo, term);
- }
-
- // next, consider ~/.terminfo
- const char* home = getenv("HOME");
-
- if (home)
- {
- snprintf(tmp, sizeof(tmp), "%s/.terminfo", home);
- tmp[sizeof(tmp) - 1] = '\0';
- char* data = terminfo_try_path(tmp, term);
-
- if (data)
- {
- return data;
- }
- }
-
- // next, TERMINFO_DIRS
- const char* dirs = getenv("TERMINFO_DIRS");
-
- if (dirs)
- {
- snprintf(tmp, sizeof(tmp), "%s", dirs);
- tmp[sizeof(tmp) - 1] = '\0';
- char* dir = strtok(tmp, ":");
-
- while (dir)
- {
- const char* cdir = dir;
-
- if (strcmp(cdir, "") == 0)
- {
- cdir = "/usr/share/terminfo";
- }
-
- char* data = terminfo_try_path(cdir, term);
-
- if (data)
- {
- return data;
- }
-
- dir = strtok(0, ":");
- }
- }
-
- // fallback to /usr/share/terminfo
- return terminfo_try_path("/usr/share/terminfo", term);
-}
-
-#define TI_MAGIC 0432
-#define TI_ALT_MAGIC 542
-#define TI_HEADER_LENGTH 12
-#define TB_KEYS_NUM 22
-
-static const char* terminfo_copy_string(char* data, int str, int table)
-{
- const int16_t off = *(int16_t*)(data + str);
- const char* src = data + table + off;
- int len = strlen(src);
- char* dst = malloc(len + 1);
- strcpy(dst, src);
- return dst;
-}
-
-const int16_t ti_funcs[] =
-{
- 28, 40, 16, 13, 5, 39, 36, 27, 26, 34, 89, 88,
-};
-
-const int16_t ti_keys[] =
-{
- // apparently not a typo; 67 is F10 for whatever reason
- 66, 68, 69, 70, 71, 72, 73, 74, 75, 67, 216, 217, 77, 59, 76, 164, 82,
- 81, 87, 61, 79, 83,
-};
-
-int init_term(void)
-{
- int i;
- char* data = load_terminfo();
-
- if (!data)
- {
- init_from_terminfo = 0;
- return init_term_builtin();
- }
-
- int16_t* header = (int16_t*)data;
-
- const int number_sec_len = header[0] == TI_ALT_MAGIC ? 4 : 2;
-
- if ((header[1] + header[2]) % 2)
- {
- // old quirk to align everything on word boundaries
- header[2] += 1;
- }
-
- const int str_offset = TI_HEADER_LENGTH +
- header[1] + header[2] + number_sec_len * header[3];
- const int table_offset = str_offset + 2 * header[4];
-
- keys = malloc(sizeof(const char*) * (TB_KEYS_NUM + 1));
-
- for (i = 0; i < TB_KEYS_NUM; i++)
- {
- keys[i] = terminfo_copy_string(data,
- str_offset + 2 * ti_keys[i], table_offset);
- }
-
- keys[i] = NULL;
-
- funcs = malloc(sizeof(const char*) * T_FUNCS_NUM);
-
- // the last two entries are reserved for mouse. because the table offset is
- // not there, the two entries have to fill in manually
- for (i = 0; i < T_FUNCS_NUM - 2; i++)
- {
- funcs[i] = terminfo_copy_string(data,
- str_offset + 2 * ti_funcs[i], table_offset);
- }
-
- funcs[T_FUNCS_NUM - 2] = ENTER_MOUSE_SEQ;
- funcs[T_FUNCS_NUM - 1] = EXIT_MOUSE_SEQ;
- init_from_terminfo = 1;
- free(data);
- return 0;
-}
-
-void shutdown_term(void)
-{
- if (init_from_terminfo)
- {
- int i;
-
- for (i = 0; i < TB_KEYS_NUM; i++)
- {
- free((void*)keys[i]);
- }
-
- // the last two entries are reserved for mouse. because the table offset
- // is not there, the two entries have to fill in manually and do not
- // need to be freed.
- for (i = 0; i < T_FUNCS_NUM - 2; i++)
- {
- free((void*)funcs[i]);
- }
-
- free(keys);
- free(funcs);
- }
-}
diff --git a/framework/3rd/termbox/src/term.h b/framework/3rd/termbox/src/term.h
deleted file mode 100755
index 8f4d93d..0000000
--- a/framework/3rd/termbox/src/term.h
+++ /dev/null
@@ -1,38 +0,0 @@
-#ifndef H_TERM
-#define H_TERM
-
-#include "termbox.h"
-#include "ringbuffer.h"
-#include
-
-#define EUNSUPPORTED_TERM -1
-
-enum
-{
- T_ENTER_CA,
- T_EXIT_CA,
- T_SHOW_CURSOR,
- T_HIDE_CURSOR,
- T_CLEAR_SCREEN,
- T_SGR0,
- T_UNDERLINE,
- T_BOLD,
- T_BLINK,
- T_REVERSE,
- T_ENTER_KEYPAD,
- T_EXIT_KEYPAD,
- T_ENTER_MOUSE,
- T_EXIT_MOUSE,
- T_FUNCS_NUM,
-};
-
-extern const char** keys;
-extern const char** funcs;
-
-// true on success, false on failure
-bool extract_event(struct tb_event* event, struct ringbuffer* inbuf,
- int inputmode);
-int init_term(void);
-void shutdown_term(void);
-
-#endif
diff --git a/framework/3rd/termbox/src/termbox.c b/framework/3rd/termbox/src/termbox.c
deleted file mode 100755
index 72a4335..0000000
--- a/framework/3rd/termbox/src/termbox.c
+++ /dev/null
@@ -1,885 +0,0 @@
-#include "term.h"
-#include "termbox.h"
-#include "memstream.h"
-
-#include
-#include
-#include
-#include
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-struct cellbuf
-{
- int width;
- int height;
- struct tb_cell* cells;
-};
-
-#define CELL(buf, x, y) (buf)->cells[(y) * (buf)->width + (x)]
-#define IS_CURSOR_HIDDEN(cx, cy) (cx == -1 || cy == -1)
-#define LAST_COORD_INIT -1
-
-static struct termios orig_tios;
-
-static struct cellbuf back_buffer;
-static struct cellbuf front_buffer;
-static unsigned char write_buffer_data[32 * 1024];
-static struct memstream write_buffer;
-
-static int termw = -1;
-static int termh = -1;
-
-static int inputmode = TB_INPUT_ESC;
-static int outputmode = TB_OUTPUT_NORMAL;
-
-static struct ringbuffer inbuf;
-
-static int out;
-static FILE* in;
-
-static int out_fileno;
-static int in_fileno;
-
-static int winch_fds[2];
-
-static int lastx = LAST_COORD_INIT;
-static int lasty = LAST_COORD_INIT;
-static int cursor_x = -1;
-static int cursor_y = -1;
-
-static uint32_t background = TB_DEFAULT;
-static uint32_t foreground = TB_DEFAULT;
-
-static void write_cursor(int x, int y);
-static void write_sgr(uint32_t fg, uint32_t bg);
-
-static void cellbuf_init(struct cellbuf* buf, int width, int height);
-static void cellbuf_resize(struct cellbuf* buf, int width, int height);
-static void cellbuf_clear(struct cellbuf* buf);
-static void cellbuf_free(struct cellbuf* buf);
-
-static void update_size(void);
-static void update_term_size(void);
-static void send_attr(uint32_t fg, uint32_t bg);
-static void send_char(int x, int y, uint32_t c);
-static void send_clear(void);
-static void sigwinch_handler(int xxx);
-static int wait_fill_event(struct tb_event* event, struct timeval* timeout);
-
-// may happen in a different thread
-static volatile int buffer_size_change_request;
-
-int tb_init_file(const char* name)
-{
- out = open(name, O_WRONLY);
- in = fopen(name, "r");
-
- if (out == -1 || !in)
- {
- if (out != -1)
- {
- close(out);
- }
-
- if (in)
- {
- fclose(in);
- }
-
- return TB_EFAILED_TO_OPEN_TTY;
- }
-
- out_fileno = out;
- in_fileno = fileno(in);
-
- if (init_term() < 0)
- {
- close(out);
- fclose(in);
-
- return TB_EUNSUPPORTED_TERMINAL;
- }
-
- if (pipe(winch_fds) < 0)
- {
- close(out);
- fclose(in);
-
- return TB_EPIPE_TRAP_ERROR;
- }
-
- struct sigaction sa;
-
- memset(&sa, 0, sizeof(sa));
- sa.sa_handler = sigwinch_handler;
- sa.sa_flags = 0;
- sigaction(SIGWINCH, &sa, 0);
- tcgetattr(out_fileno, &orig_tios);
-
- struct termios tios;
-
- memcpy(&tios, &orig_tios, sizeof(tios));
- tios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
- | INLCR | IGNCR | ICRNL | IXON);
- tios.c_oflag &= ~OPOST;
- tios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
- tios.c_cflag &= ~(CSIZE | PARENB);
- tios.c_cflag |= CS8;
- tios.c_cc[VMIN] = 0;
- tios.c_cc[VTIME] = 0;
- tcsetattr(out_fileno, TCSAFLUSH, &tios);
-
- memstream_init(&write_buffer, out_fileno, write_buffer_data,
- sizeof(write_buffer_data));
- memstream_puts(&write_buffer, funcs[T_ENTER_CA]);
- memstream_puts(&write_buffer, funcs[T_ENTER_KEYPAD]);
- memstream_puts(&write_buffer, funcs[T_HIDE_CURSOR]);
- send_clear();
-
- update_term_size();
- cellbuf_init(&back_buffer, termw, termh);
- cellbuf_init(&front_buffer, termw, termh);
- cellbuf_clear(&back_buffer);
- cellbuf_clear(&front_buffer);
- init_ringbuffer(&inbuf, 4096);
- return 0;
-}
-
-int tb_init(void)
-{
- return tb_init_file("/dev/tty");
-}
-
-void tb_shutdown(void)
-{
- if (termw == -1)
- {
- fputs("tb_shutdown() should not be called twice.", stderr);
- abort();
- }
-
- memstream_puts(&write_buffer, funcs[T_SHOW_CURSOR]);
- memstream_puts(&write_buffer, funcs[T_SGR0]);
- memstream_puts(&write_buffer, funcs[T_CLEAR_SCREEN]);
- memstream_puts(&write_buffer, funcs[T_EXIT_CA]);
- memstream_puts(&write_buffer, funcs[T_EXIT_KEYPAD]);
- memstream_puts(&write_buffer, funcs[T_EXIT_MOUSE]);
- memstream_flush(&write_buffer);
- tcsetattr(out_fileno, TCSAFLUSH, &orig_tios);
-
- shutdown_term();
- close(out);
- fclose(in);
- close(winch_fds[0]);
- close(winch_fds[1]);
-
- cellbuf_free(&back_buffer);
- cellbuf_free(&front_buffer);
- free_ringbuffer(&inbuf);
- termw = termh = -1;
-}
-
-void tb_present(void)
-{
- int x, y, w, i;
- struct tb_cell* back, *front;
-
- // invalidate cursor position
- lastx = LAST_COORD_INIT;
- lasty = LAST_COORD_INIT;
-
- if (buffer_size_change_request)
- {
- update_size();
- buffer_size_change_request = 0;
- }
-
- for (y = 0; y < front_buffer.height; ++y)
- {
- for (x = 0; x < front_buffer.width;)
- {
- back = &CELL(&back_buffer, x, y);
- front = &CELL(&front_buffer, x, y);
- w = wcwidth(back->ch);
-
- if (w < 1)
- {
- w = 1;
- }
-
- if (memcmp(back, front, sizeof(struct tb_cell)) == 0)
- {
- x += w;
- continue;
- }
-
- memcpy(front, back, sizeof(struct tb_cell));
- send_attr(back->fg, back->bg);
-
- if (w > 1 && x >= front_buffer.width - (w - 1))
- {
- // Not enough room for wide ch, so send spaces
- for (i = x; i < front_buffer.width; ++i)
- {
- send_char(i, y, ' ');
- }
- }
- else
- {
- send_char(x, y, back->ch);
-
- for (i = 1; i < w; ++i)
- {
- front = &CELL(&front_buffer, x + i, y);
- front->ch = 0;
- front->fg = back->fg;
- front->bg = back->bg;
- }
- }
-
- x += w;
- }
- }
-
- if (!IS_CURSOR_HIDDEN(cursor_x, cursor_y))
- {
- write_cursor(cursor_x, cursor_y);
- }
-
- memstream_flush(&write_buffer);
-}
-
-void tb_set_cursor(int cx, int cy)
-{
- if (IS_CURSOR_HIDDEN(cursor_x, cursor_y) && !IS_CURSOR_HIDDEN(cx, cy))
- {
- memstream_puts(&write_buffer, funcs[T_SHOW_CURSOR]);
- }
-
- if (!IS_CURSOR_HIDDEN(cursor_x, cursor_y) && IS_CURSOR_HIDDEN(cx, cy))
- {
- memstream_puts(&write_buffer, funcs[T_HIDE_CURSOR]);
- }
-
- cursor_x = cx;
- cursor_y = cy;
-
- if (!IS_CURSOR_HIDDEN(cursor_x, cursor_y))
- {
- write_cursor(cursor_x, cursor_y);
- }
-}
-
-void tb_put_cell(int x, int y, const struct tb_cell* cell)
-{
- if ((unsigned)x >= (unsigned)back_buffer.width)
- {
- return;
- }
-
- if ((unsigned)y >= (unsigned)back_buffer.height)
- {
- return;
- }
-
- CELL(&back_buffer, x, y) = *cell;
-}
-
-void tb_change_cell(int x, int y, uint32_t ch, uint32_t fg, uint32_t bg)
-{
- struct tb_cell c = {ch, fg, bg};
- tb_put_cell(x, y, &c);
-}
-
-void tb_blit(int x, int y, int w, int h, const struct tb_cell* cells)
-{
- if (x + w < 0 || x >= back_buffer.width)
- {
- return;
- }
-
- if (y + h < 0 || y >= back_buffer.height)
- {
- return;
- }
-
- int xo = 0, yo = 0, ww = w, hh = h;
-
- if (x < 0)
- {
- xo = -x;
- ww -= xo;
- x = 0;
- }
-
- if (y < 0)
- {
- yo = -y;
- hh -= yo;
- y = 0;
- }
-
- if (ww > back_buffer.width - x)
- {
- ww = back_buffer.width - x;
- }
-
- if (hh > back_buffer.height - y)
- {
- hh = back_buffer.height - y;
- }
-
- int sy;
- struct tb_cell* dst = &CELL(&back_buffer, x, y);
- const struct tb_cell* src = cells + yo * w + xo;
- size_t size = sizeof(struct tb_cell) * ww;
-
- for (sy = 0; sy < hh; ++sy)
- {
- memcpy(dst, src, size);
- dst += back_buffer.width;
- src += w;
- }
-}
-
-struct tb_cell* tb_cell_buffer(void)
-{
- return back_buffer.cells;
-}
-
-int tb_poll_event(struct tb_event* event)
-{
- return wait_fill_event(event, 0);
-}
-
-int tb_peek_event(struct tb_event* event, int timeout)
-{
- struct timeval tv;
- tv.tv_sec = timeout / 1000;
- tv.tv_usec = (timeout - (tv.tv_sec * 1000)) * 1000;
- return wait_fill_event(event, &tv);
-}
-
-int tb_width(void)
-{
- return termw;
-}
-
-int tb_height(void)
-{
- return termh;
-}
-
-void tb_clear(void)
-{
- if (buffer_size_change_request)
- {
- update_size();
- buffer_size_change_request = 0;
- }
-
- cellbuf_clear(&back_buffer);
-}
-
-int tb_select_input_mode(int mode)
-{
- if (mode)
- {
- if ((mode & (TB_INPUT_ESC | TB_INPUT_ALT)) == 0)
- {
- mode |= TB_INPUT_ESC;
- }
-
- // technically termbox can handle that, but let's be nice
- // and show here what mode is actually used
- if ((mode & (TB_INPUT_ESC | TB_INPUT_ALT)) == (TB_INPUT_ESC | TB_INPUT_ALT))
- {
- mode &= ~TB_INPUT_ALT;
- }
-
- inputmode = mode;
-
- if (mode & TB_INPUT_MOUSE)
- {
- memstream_puts(&write_buffer, funcs[T_ENTER_MOUSE]);
- memstream_flush(&write_buffer);
- }
- else
- {
- memstream_puts(&write_buffer, funcs[T_EXIT_MOUSE]);
- memstream_flush(&write_buffer);
- }
- }
-
- return inputmode;
-}
-
-int tb_select_output_mode(int mode)
-{
- if (mode)
- {
- outputmode = mode;
- }
-
- return outputmode;
-}
-
-void tb_set_clear_attributes(uint32_t fg, uint32_t bg)
-{
- foreground = fg;
- background = bg;
-}
-
-static unsigned convertnum(uint32_t num, char* buf)
-{
- unsigned i, l = 0;
- int ch;
-
- do
- {
- buf[l++] = '0' + (num % 10);
- num /= 10;
- }
- while (num);
-
- for (i = 0; i < l / 2; i++)
- {
- ch = buf[i];
- buf[i] = buf[l - 1 - i];
- buf[l - 1 - i] = ch;
- }
-
- return l;
-}
-
-#define WRITE_LITERAL(X) memstream_write(&write_buffer, (X), sizeof(X) -1)
-#define WRITE_INT(X) memstream_write(&write_buffer, buf, convertnum((X), buf))
-
-static void write_cursor(int x, int y)
-{
- char buf[32];
- WRITE_LITERAL("\033[");
- WRITE_INT(y + 1);
- WRITE_LITERAL(";");
- WRITE_INT(x + 1);
- WRITE_LITERAL("H");
-}
-
-static void write_sgr(uint32_t fg, uint32_t bg)
-{
- char buf[32];
-
- if (outputmode != TB_OUTPUT_TRUECOLOR && fg == TB_DEFAULT && bg == TB_DEFAULT)
- {
- return;
- }
-
- switch (outputmode)
- {
- case TB_OUTPUT_TRUECOLOR:
- WRITE_LITERAL("\033[38;2;");
- WRITE_INT(fg >> 16 & 0xFF);
- WRITE_LITERAL(";");
- WRITE_INT(fg >> 8 & 0xFF);
- WRITE_LITERAL(";");
- WRITE_INT(fg & 0xFF);
- WRITE_LITERAL(";48;2;");
- WRITE_INT(bg >> 16 & 0xFF);
- WRITE_LITERAL(";");
- WRITE_INT(bg >> 8 & 0xFF);
- WRITE_LITERAL(";");
- WRITE_INT(bg & 0xFF);
- WRITE_LITERAL("m");
- break;
-
- case TB_OUTPUT_256:
- case TB_OUTPUT_216:
- case TB_OUTPUT_GRAYSCALE:
- WRITE_LITERAL("\033[");
-
- if (fg != TB_DEFAULT)
- {
- WRITE_LITERAL("38;5;");
- WRITE_INT(fg);
-
- if (bg != TB_DEFAULT)
- {
- WRITE_LITERAL(";");
- }
- }
-
- if (bg != TB_DEFAULT)
- {
- WRITE_LITERAL("48;5;");
- WRITE_INT(bg);
- }
-
- WRITE_LITERAL("m");
- break;
-
- case TB_OUTPUT_NORMAL:
- default:
- WRITE_LITERAL("\033[");
-
- if (fg != TB_DEFAULT)
- {
- WRITE_LITERAL("3");
- WRITE_INT(fg - 1);
-
- if (bg != TB_DEFAULT)
- {
- WRITE_LITERAL(";");
- }
- }
-
- if (bg != TB_DEFAULT)
- {
- WRITE_LITERAL("4");
- WRITE_INT(bg - 1);
- }
-
- WRITE_LITERAL("m");
- break;
- }
-}
-
-static void cellbuf_init(struct cellbuf* buf, int width, int height)
-{
- buf->cells = (struct tb_cell*)malloc(sizeof(struct tb_cell) * width * height);
- assert(buf->cells);
- buf->width = width;
- buf->height = height;
-}
-
-static void cellbuf_resize(struct cellbuf* buf, int width, int height)
-{
- if (buf->width == width && buf->height == height)
- {
- return;
- }
-
- int oldw = buf->width;
- int oldh = buf->height;
- struct tb_cell* oldcells = buf->cells;
-
- cellbuf_init(buf, width, height);
- cellbuf_clear(buf);
-
- int minw = (width < oldw) ? width : oldw;
- int minh = (height < oldh) ? height : oldh;
- int i;
-
- for (i = 0; i < minh; ++i)
- {
- struct tb_cell* csrc = oldcells + (i * oldw);
- struct tb_cell* cdst = buf->cells + (i * width);
- memcpy(cdst, csrc, sizeof(struct tb_cell) * minw);
- }
-
- free(oldcells);
-}
-
-static void cellbuf_clear(struct cellbuf* buf)
-{
- int i;
- int ncells = buf->width * buf->height;
-
- for (i = 0; i < ncells; ++i)
- {
- buf->cells[i].ch = ' ';
- buf->cells[i].fg = foreground;
- buf->cells[i].bg = background;
- }
-}
-
-static void cellbuf_free(struct cellbuf* buf)
-{
- free(buf->cells);
-}
-
-static void get_term_size(int* w, int* h)
-{
- struct winsize sz;
- memset(&sz, 0, sizeof(sz));
-
- ioctl(out_fileno, TIOCGWINSZ, &sz);
-
- if (w)
- {
- *w = sz.ws_col;
- }
-
- if (h)
- {
- *h = sz.ws_row;
- }
-}
-
-static void update_term_size(void)
-{
- struct winsize sz;
- memset(&sz, 0, sizeof(sz));
-
- ioctl(out_fileno, TIOCGWINSZ, &sz);
-
- termw = sz.ws_col;
- termh = sz.ws_row;
-}
-
-static void send_attr(uint32_t fg, uint32_t bg)
-{
-#define LAST_ATTR_INIT 0xFFFFFFFF
- static uint32_t lastfg = LAST_ATTR_INIT, lastbg = LAST_ATTR_INIT;
-
- if (fg != lastfg || bg != lastbg)
- {
- memstream_puts(&write_buffer, funcs[T_SGR0]);
- uint32_t fgcol;
- uint32_t bgcol;
-
- switch (outputmode)
- {
- case TB_OUTPUT_TRUECOLOR:
- fgcol = fg;
- bgcol = bg;
- break;
-
- case TB_OUTPUT_256:
- fgcol = fg & 0xFF;
- bgcol = bg & 0xFF;
- break;
-
- case TB_OUTPUT_216:
- fgcol = fg & 0xFF;
-
- if (fgcol > 215)
- {
- fgcol = 7;
- }
-
- bgcol = bg & 0xFF;
-
- if (bgcol > 215)
- {
- bgcol = 0;
- }
-
- fgcol += 0x10;
- bgcol += 0x10;
- break;
-
- case TB_OUTPUT_GRAYSCALE:
- fgcol = fg & 0xFF;
-
- if (fgcol > 23)
- {
- fgcol = 23;
- }
-
- bgcol = bg & 0xFF;
-
- if (bgcol > 23)
- {
- bgcol = 0;
- }
-
- fgcol += 0xe8;
- bgcol += 0xe8;
- break;
-
- case TB_OUTPUT_NORMAL:
- default:
- fgcol = fg & 0x0F;
- bgcol = bg & 0x0F;
- }
-
- if (fg & TB_BOLD)
- {
- memstream_puts(&write_buffer, funcs[T_BOLD]);
- }
-
- if (bg & TB_BOLD)
- {
- memstream_puts(&write_buffer, funcs[T_BLINK]);
- }
-
- if (fg & TB_UNDERLINE)
- {
- memstream_puts(&write_buffer, funcs[T_UNDERLINE]);
- }
-
- if ((fg & TB_REVERSE) || (bg & TB_REVERSE))
- {
- memstream_puts(&write_buffer, funcs[T_REVERSE]);
- }
-
- write_sgr(fgcol, bgcol);
-
- lastfg = fg;
- lastbg = bg;
- }
-}
-
-static void send_char(int x, int y, uint32_t c)
-{
- char buf[7];
- int bw = utf8_unicode_to_char(buf, c);
- buf[bw] = '\0';
-
- if (x - 1 != lastx || y != lasty)
- {
- write_cursor(x, y);
- }
-
- lastx = x;
- lasty = y;
-
- if (!c)
- {
- buf[0] = ' '; // replace 0 with whitespace
- }
-
- memstream_puts(&write_buffer, buf);
-}
-
-static void send_clear(void)
-{
- send_attr(foreground, background);
- memstream_puts(&write_buffer, funcs[T_CLEAR_SCREEN]);
-
- if (!IS_CURSOR_HIDDEN(cursor_x, cursor_y))
- {
- write_cursor(cursor_x, cursor_y);
- }
-
- memstream_flush(&write_buffer);
-
- // we need to invalidate cursor position too and these two vars are
- // used only for simple cursor positioning optimization, cursor
- // actually may be in the correct place, but we simply discard
- // optimization once and it gives us simple solution for the case when
- // cursor moved
- lastx = LAST_COORD_INIT;
- lasty = LAST_COORD_INIT;
-}
-
-static void sigwinch_handler(int xxx)
-{
- (void) xxx;
- const int zzz = 1;
- write(winch_fds[1], &zzz, sizeof(int));
-}
-
-static void update_size(void)
-{
- update_term_size();
- cellbuf_resize(&back_buffer, termw, termh);
- cellbuf_resize(&front_buffer, termw, termh);
- cellbuf_clear(&front_buffer);
- send_clear();
-}
-
-static int wait_fill_event(struct tb_event* event, struct timeval* timeout)
-{
-#define ENOUGH_DATA_FOR_INPUT_PARSING 128
- int result;
- char buf[ENOUGH_DATA_FOR_INPUT_PARSING];
- fd_set events;
- memset(event, 0, sizeof(struct tb_event));
-
- // try to extract event from input buffer, return on success
- event->type = TB_EVENT_KEY;
-
- if (extract_event(event, &inbuf, inputmode))
- {
- return event->type;
- }
-
- // it looks like input buffer is incomplete, let's try the short path
- size_t r = fread(buf, 1, ENOUGH_DATA_FOR_INPUT_PARSING, in);
-
- if (r < ENOUGH_DATA_FOR_INPUT_PARSING && feof(in))
- {
- clearerr(in);
- }
-
- if (r > 0)
- {
- if (ringbuffer_free_space(&inbuf) < r)
- {
- return -1;
- }
-
- ringbuffer_push(&inbuf, buf, r);
-
- if (extract_event(event, &inbuf, inputmode))
- {
- return event->type;
- }
- }
-
- // no stuff in FILE's internal buffer, block in select
- while (1)
- {
- FD_ZERO(&events);
- FD_SET(in_fileno, &events);
- FD_SET(winch_fds[0], &events);
- int maxfd = (winch_fds[0] > in_fileno) ? winch_fds[0] : in_fileno;
- result = select(maxfd + 1, &events, 0, 0, timeout);
-
- if (!result)
- {
- return 0;
- }
-
- if (FD_ISSET(in_fileno, &events))
- {
- event->type = TB_EVENT_KEY;
- size_t r = fread(buf, 1, ENOUGH_DATA_FOR_INPUT_PARSING, in);
-
- if (r < ENOUGH_DATA_FOR_INPUT_PARSING && feof(in))
- {
- clearerr(in);
- }
-
- if (r == 0)
- {
- continue;
- }
-
- // if there is no free space in input buffer, return error
- if (ringbuffer_free_space(&inbuf) < r)
- {
- return -1;
- }
-
- // fill buffer
- ringbuffer_push(&inbuf, buf, r);
-
- if (extract_event(event, &inbuf, inputmode))
- {
- return event->type;
- }
- }
-
- if (FD_ISSET(winch_fds[0], &events))
- {
- event->type = TB_EVENT_RESIZE;
- int zzz = 0;
- read(winch_fds[0], &zzz, sizeof(int));
- buffer_size_change_request = 1;
- get_term_size(&event->w, &event->h);
- return TB_EVENT_RESIZE;
- }
- }
-}
diff --git a/framework/3rd/termbox/src/utf8.c b/framework/3rd/termbox/src/utf8.c
deleted file mode 100755
index 43efd7f..0000000
--- a/framework/3rd/termbox/src/utf8.c
+++ /dev/null
@@ -1,106 +0,0 @@
-#include "termbox.h"
-
-static const unsigned char utf8_length[256] =
-{
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
- 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 1, 1
-};
-
-static const unsigned char utf8_mask[6] =
-{
- 0x7F,
- 0x1F,
- 0x0F,
- 0x07,
- 0x03,
- 0x01
-};
-
-int utf8_char_length(char c)
-{
- return utf8_length[(unsigned char)c];
-}
-
-int utf8_char_to_unicode(uint32_t* out, const char* c)
-{
- if (*c == 0)
- {
- return TB_EOF;
- }
-
- int i;
- unsigned char len = utf8_char_length(*c);
- unsigned char mask = utf8_mask[len - 1];
- uint32_t result = c[0] & mask;
-
- for (i = 1; i < len; ++i)
- {
- result <<= 6;
- result |= c[i] & 0x3f;
- }
-
- *out = result;
- return (int)len;
-}
-
-int utf8_unicode_to_char(char* out, uint32_t c)
-{
- int len = 0;
- int first;
- int i;
-
- if (c < 0x80)
- {
- first = 0;
- len = 1;
- }
- else if (c < 0x800)
- {
- first = 0xc0;
- len = 2;
- }
- else if (c < 0x10000)
- {
- first = 0xe0;
- len = 3;
- }
- else if (c < 0x200000)
- {
- first = 0xf0;
- len = 4;
- }
- else if (c < 0x4000000)
- {
- first = 0xf8;
- len = 5;
- }
- else
- {
- first = 0xfc;
- len = 6;
- }
-
- for (i = len - 1; i > 0; --i)
- {
- out[i] = (c & 0x3f) | 0x80;
- c >>= 6;
- }
-
- out[0] = c | first;
-
- return len;
-}
diff --git a/framework/3rd/termbox/tools/astylerc b/framework/3rd/termbox/tools/astylerc
deleted file mode 100755
index a296bc3..0000000
--- a/framework/3rd/termbox/tools/astylerc
+++ /dev/null
@@ -1,27 +0,0 @@
---style=break
---indent=force-tab=4
---indent-classes
---indent-switches
---indent-namespaces
---indent-after-parens
---indent-continuation=1
---indent-preproc-block
---indent-preproc-define
---indent-preproc-cond
---indent-col1-comments
---min-conditional-indent=0
---max-continuation-indent=40
---break-blocks
---pad-oper
---pad-comma
---pad-header
---unpad-paren
---align-pointer=type
---align-reference=type
---break-one-line-headers
---add-braces
---attach-return-type
---attach-return-type-decl
---remove-comment-prefix
---max-code-length=80
---mode=c
diff --git a/framework/3rd/termbox/tools/collect_terminfo.py b/framework/3rd/termbox/tools/collect_terminfo.py
deleted file mode 100755
index 596c3c4..0000000
--- a/framework/3rd/termbox/tools/collect_terminfo.py
+++ /dev/null
@@ -1,108 +0,0 @@
-#!/usr/bin/env python
-
-import sys, os, subprocess
-
-def escaped(s):
- return s.replace("\033", "\\033")
-
-def tput(term, name):
- try:
- return subprocess.check_output(['tput', '-T%s' % term, name]).decode()
- except subprocess.CalledProcessError as e:
- return e.output.decode()
-
-
-def w(s):
- if s == None:
- return
- sys.stdout.write(s)
-
-terminals = {
- 'xterm' : 'xterm',
- 'rxvt-256color' : 'rxvt_256color',
- 'rxvt-unicode' : 'rxvt_unicode',
- 'linux' : 'linux',
- 'Eterm' : 'eterm',
- 'screen' : 'screen'
-}
-
-keys = [
- "F1", "kf1",
- "F2", "kf2",
- "F3", "kf3",
- "F4", "kf4",
- "F5", "kf5",
- "F6", "kf6",
- "F7", "kf7",
- "F8", "kf8",
- "F9", "kf9",
- "F10", "kf10",
- "F11", "kf11",
- "F12", "kf12",
- "INSERT", "kich1",
- "DELETE", "kdch1",
- "HOME", "khome",
- "END", "kend",
- "PGUP", "kpp",
- "PGDN", "knp",
- "KEY_UP", "kcuu1",
- "KEY_DOWN", "kcud1",
- "KEY_LEFT", "kcub1",
- "KEY_RIGHT", "kcuf1"
-]
-
-funcs = [
- "T_ENTER_CA", "smcup",
- "T_EXIT_CA", "rmcup",
- "T_SHOW_CURSOR", "cnorm",
- "T_HIDE_CURSOR", "civis",
- "T_CLEAR_SCREEN", "clear",
- "T_SGR0", "sgr0",
- "T_UNDERLINE", "smul",
- "T_BOLD", "bold",
- "T_BLINK", "blink",
- "T_REVERSE", "rev",
- "T_ENTER_KEYPAD", "smkx",
- "T_EXIT_KEYPAD", "rmkx"
-]
-
-def iter_pairs(iterable):
- iterable = iter(iterable)
- while True:
- yield (next(iterable), next(iterable))
-
-def do_term(term, nick):
- w("// %s\n" % term)
- w("static const char *%s_keys[] = {\n\t" % nick)
- for k, v in iter_pairs(keys):
- w('"')
- w(escaped(tput(term, v)))
- w('",')
- w(" 0\n};\n")
- w("static const char *%s_funcs[] = {\n\t" % nick)
- for k,v in iter_pairs(funcs):
- w('"')
- if v == "sgr":
- w("\\033[3%d;4%dm")
- elif v == "cup":
- w("\\033[%d;%dH")
- else:
- w(escaped(tput(term, v)))
- w('", ')
- w("\n};\n\n")
-
-def do_terms(d):
- w("static struct term {\n")
- w("\tconst char *name;\n")
- w("\tconst char **keys;\n")
- w("\tconst char **funcs;\n")
- w("} terms[] = {\n")
- for k, v in d.items():
- w('\t{"%s", %s_keys, %s_funcs},\n' % (k, v, v))
- w("\t{0, 0, 0},\n")
- w("};\n")
-
-for k,v in terminals.items():
- do_term(k, v)
-
-do_terms(terminals)
diff --git a/framework/lualib-src/Makefile b/framework/lualib-src/Makefile
index f47b369..0948b9c 100644
--- a/framework/lualib-src/Makefile
+++ b/framework/lualib-src/Makefile
@@ -16,6 +16,7 @@ LFS_SO = $(LUA_CLIB_PATH)/lfs.so
CJSON_SO = $(LUA_CLIB_PATH)/cjson.so
PROFILE_SO = $(LUA_CLIB_PATH)/profile.so
SKIPLIST_SO = $(LUA_CLIB_PATH)/skiplist.so
+SKIPSET_SO = $(LUA_CLIB_PATH)/skipset.so
SNAPSHOT_SO = $(LUA_CLIB_PATH)/snapshot.so
SHIFTTIMER_SO = $(LUA_CLIB_PATH)/shiftimer.so
CLUA_SO = $(LUA_CLIB_PATH)/clua.so
@@ -24,12 +25,14 @@ ECS_SO = $(LUA_CLIB_PATH)/ecs.so
LUASOCKET_SO = $(LUA_CLIB_PATH)/socket.so
TERMFX_SO = $(LUA_CLIB_PATH)/termfx.so
RC4_SO = $(LUA_CLIB_PATH)/rc4.so
+MATH_SO = $(LUA_CLIB_PATH)/math3d.so
#####################################################
all: $(LFS_SO) \
$(CJSON_SO) \
$(PROFILE_SO) \
$(SKIPLIST_SO) \
+ $(SKIPSET_SO) \
$(SNAPSHOT_SO) \
$(SHIFTTIMER_SO) \
$(AOI_SO) \
@@ -37,6 +40,7 @@ all: $(LFS_SO) \
$(TERMFX_SO) \
$(RC4_SO) \
$(CLUA_SO) \
+ $(MATH_SO) \
$(LUASOCKET_SO)
#####################################################
@@ -51,7 +55,10 @@ $(PROFILE_SO):
cd lua-profile && $(MAKE) PLAT=$(PLAT)
$(SKIPLIST_SO):
- cd lua-zset && $(MAKE) PLAT=$(PLAT)
+ cd lua-skiplist && $(MAKE) PLAT=$(PLAT)
+
+$(SKIPSET_SO):
+ cd lua-skipset && $(MAKE) PLAT=$(PLAT)
$(SNAPSHOT_SO):
cd lua-snapshot && $(MAKE) PLAT=$(PLAT)
@@ -68,6 +75,9 @@ $(CLUA_SO):
$(ECS_SO):
cd lua-ecs && $(MAKE) PLAT=$(PLAT)
+$(MATH_SO):
+ cd math3d && $(MAKE) PLAT=$(PLAT)
+
$(RC4_SO):
cd lua-rc4 && $(MAKE) PLAT=$(PLAT)
diff --git a/framework/lualib-src/lua-zset/Makefile b/framework/lualib-src/lua-skiplist/Makefile
similarity index 100%
rename from framework/lualib-src/lua-zset/Makefile
rename to framework/lualib-src/lua-skiplist/Makefile
diff --git a/framework/lualib-src/lua-zset/lua-skiplist.c b/framework/lualib-src/lua-skiplist/lua-skiplist.c
similarity index 77%
rename from framework/lualib-src/lua-zset/lua-skiplist.c
rename to framework/lualib-src/lua-skiplist/lua-skiplist.c
index 80e0f76..74ba301 100644
--- a/framework/lualib-src/lua-zset/lua-skiplist.c
+++ b/framework/lualib-src/lua-skiplist/lua-skiplist.c
@@ -11,16 +11,19 @@
#include "skiplist.h"
static inline skiplist *
-_to_skiplist(lua_State *L) {
+_to_skiplist(lua_State *L)
+{
skiplist **sl = lua_touserdata(L, 1);
- if (sl == NULL) {
+ if (sl == NULL)
+ {
luaL_error(L, "must be skiplist object");
}
return *sl;
}
static int
-_insert(lua_State *L) {
+_insert(lua_State *L)
+{
skiplist *sl = _to_skiplist(L);
double score = luaL_checknumber(L, 2);
lua_Integer obj = luaL_checkinteger(L, 3);
@@ -29,7 +32,8 @@ _insert(lua_State *L) {
}
static int
-_delete(lua_State *L) {
+_delete(lua_State *L)
+{
skiplist *sl = _to_skiplist(L);
double score = luaL_checknumber(L, 2);
lua_Integer obj = luaL_checkinteger(L, 3);
@@ -38,7 +42,8 @@ _delete(lua_State *L) {
}
static void
-_delete_rank_cb(void *ud, int64_t obj) {
+_delete_rank_cb(void *ud, int64_t obj)
+{
lua_State *L = (lua_State *)ud;
lua_pushvalue(L, 4);
lua_pushinteger(L, obj);
@@ -46,12 +51,14 @@ _delete_rank_cb(void *ud, int64_t obj) {
}
static int
-_delete_by_rank(lua_State *L) {
+_delete_by_rank(lua_State *L)
+{
skiplist *sl = _to_skiplist(L);
unsigned int start = luaL_checkinteger(L, 2);
unsigned int end = luaL_checkinteger(L, 3);
luaL_checktype(L, 4, LUA_TFUNCTION);
- if (start > end) {
+ if (start > end)
+ {
unsigned int tmp = start;
start = end;
end = tmp;
@@ -62,20 +69,23 @@ _delete_by_rank(lua_State *L) {
}
static int
-_get_count(lua_State *L) {
+_get_count(lua_State *L)
+{
skiplist *sl = _to_skiplist(L);
lua_pushinteger(L, sl->length);
return 1;
}
static int
-_get_rank(lua_State *L) {
+_get_rank(lua_State *L)
+{
skiplist *sl = _to_skiplist(L);
double score = luaL_checknumber(L, 2);
lua_Integer obj = luaL_checkinteger(L, 3);
unsigned long rank = slGetRank(sl, score, obj);
- if (rank == 0) {
+ if (rank == 0)
+ {
return 0;
}
@@ -84,15 +94,19 @@ _get_rank(lua_State *L) {
}
static int
-_get_rank_range(lua_State *L) {
+_get_rank_range(lua_State *L)
+{
skiplist *sl = _to_skiplist(L);
unsigned long r1 = luaL_checkinteger(L, 2);
unsigned long r2 = luaL_checkinteger(L, 3);
int reverse, rangelen;
- if (r1 <= r2) {
+ if (r1 <= r2)
+ {
reverse = 0;
rangelen = r2 - r1 + 1;
- } else {
+ }
+ else
+ {
reverse = 1;
rangelen = r1 - r2 + 1;
}
@@ -100,7 +114,8 @@ _get_rank_range(lua_State *L) {
skiplistNode *node = slGetNodeByRank(sl, r1);
lua_createtable(L, rangelen, 0);
int n = 0;
- while (node && n < rangelen) {
+ while (node && n < rangelen)
+ {
n++;
lua_pushinteger(L, node->obj);
lua_rawseti(L, -2, n);
@@ -110,15 +125,19 @@ _get_rank_range(lua_State *L) {
}
static int
-_get_rank_score_range(lua_State *L) {
+_get_rank_score_range(lua_State *L)
+{
skiplist *sl = _to_skiplist(L);
unsigned long r1 = luaL_checkinteger(L, 2);
unsigned long r2 = luaL_checkinteger(L, 3);
int reverse, rangelen;
- if (r1 <= r2) {
+ if (r1 <= r2)
+ {
reverse = 0;
rangelen = r2 - r1 + 1;
- } else {
+ }
+ else
+ {
reverse = 1;
rangelen = r1 - r2 + 1;
}
@@ -126,7 +145,8 @@ _get_rank_score_range(lua_State *L) {
skiplistNode *node = slGetNodeByRank(sl, r1);
lua_createtable(L, rangelen, 0);
int n = 0;
- while (node && n < rangelen) {
+ while (node && n < rangelen)
+ {
n++;
lua_createtable(L, 2, 0);
lua_pushinteger(L, node->obj);
@@ -141,28 +161,36 @@ _get_rank_score_range(lua_State *L) {
}
static int
-_get_score_range(lua_State *L) {
+_get_score_range(lua_State *L)
+{
skiplist *sl = _to_skiplist(L);
double s1 = luaL_checknumber(L, 2);
double s2 = luaL_checknumber(L, 3);
int reverse;
skiplistNode *node;
- if (s1 <= s2) {
+ if (s1 <= s2)
+ {
reverse = 0;
node = slFirstInRange(sl, s1, s2);
- } else {
+ }
+ else
+ {
reverse = 1;
node = slLastInRange(sl, s2, s1);
}
lua_newtable(L);
int n = 0;
- while (node) {
- if (reverse) {
+ while (node)
+ {
+ if (reverse)
+ {
if (node->score < s2)
break;
- } else {
+ }
+ else
+ {
if (node->score > s2)
break;
}
@@ -177,11 +205,13 @@ _get_score_range(lua_State *L) {
}
static int
-_get_member_by_rank(lua_State *L){
+_get_member_by_rank(lua_State *L)
+{
skiplist *sl = _to_skiplist(L);
unsigned long r = luaL_checkinteger(L, 2);
skiplistNode *node = slGetNodeByRank(sl, r);
- if (node) {
+ if (node)
+ {
lua_pushinteger(L, node->obj);
return 1;
}
@@ -189,7 +219,8 @@ _get_member_by_rank(lua_State *L){
}
static int
-_new(lua_State *L) {
+_new(lua_State *L)
+{
skiplist *psl = slCreate();
skiplist **sl = (skiplist **)lua_newuserdata(L, sizeof(skiplist *));
@@ -200,7 +231,8 @@ _new(lua_State *L) {
}
static int
-_release(lua_State *L) {
+_release(lua_State *L)
+{
skiplist *sl = _to_skiplist(L);
//printf("collect sl:%p\n", sl);
slFree(sl);
@@ -208,24 +240,24 @@ _release(lua_State *L) {
}
LUAMOD_API int
-luaopen_skiplist_c(lua_State *L) {
+luaopen_skiplist_c(lua_State *L)
+{
luaL_checkversion(L);
luaL_Reg l[] = {
- { "insert", _insert },
- { "delete", _delete },
- { "delete_by_rank", _delete_by_rank },
+ {"insert", _insert},
+ {"delete", _delete},
+ {"delete_by_rank", _delete_by_rank},
- { "get_count", _get_count },
- { "get_rank", _get_rank },
- { "get_rank_range", _get_rank_range },
- { "get_score_range", _get_score_range },
- { "get_member_by_rank", _get_member_by_rank},
+ {"get_count", _get_count},
+ {"get_rank", _get_rank},
+ {"get_rank_range", _get_rank_range},
+ {"get_score_range", _get_score_range},
+ {"get_member_by_rank", _get_member_by_rank},
- { "get_rank_score_range", _get_rank_score_range },
+ {"get_rank_score_range", _get_rank_score_range},
- { NULL, NULL }
- };
+ {NULL, NULL}};
lua_createtable(L, 0, 2);
diff --git a/framework/lualib-src/lua-zset/skiplist.c b/framework/lualib-src/lua-skiplist/skiplist.c
similarity index 81%
rename from framework/lualib-src/lua-zset/skiplist.c
rename to framework/lualib-src/lua-skiplist/skiplist.c
index ba468e7..bcf0c5f 100644
--- a/framework/lualib-src/lua-zset/skiplist.c
+++ b/framework/lualib-src/lua-skiplist/skiplist.c
@@ -14,18 +14,21 @@
#define SKIPLIST_MAXLEVEL 32
#define SKIPLIST_P 0.25
-skiplistNode *slCreateNode(int level, double score, int64_t obj) {
+skiplistNode *slCreateNode(int level, double score, int64_t obj)
+{
skiplistNode *n = malloc(sizeof(*n) + level * sizeof(struct skiplistLevel));
n->score = score;
n->obj = obj;
return n;
}
-void slFreeNode(skiplistNode *node) {
+void slFreeNode(skiplistNode *node)
+{
free(node);
}
-skiplist *slCreate(void) {
+skiplist *slCreate(void)
+{
int j;
skiplist *sl;
@@ -33,7 +36,8 @@ skiplist *slCreate(void) {
sl->level = 1;
sl->length = 0;
sl->header = slCreateNode(SKIPLIST_MAXLEVEL, 0, 0);
- for (j = 0; j < SKIPLIST_MAXLEVEL; j++) {
+ for (j = 0; j < SKIPLIST_MAXLEVEL; j++)
+ {
sl->header->level[j].forward = NULL;
sl->header->level[j].span = 0;
}
@@ -42,10 +46,12 @@ skiplist *slCreate(void) {
return sl;
}
-void slFree(skiplist *sl) {
+void slFree(skiplist *sl)
+{
skiplistNode *node = sl->header->level[0].forward, *next;
free(sl->header);
- while (node) {
+ while (node)
+ {
next = node->level[0].forward;
slFreeNode(node);
node = next;
@@ -53,23 +59,27 @@ void slFree(skiplist *sl) {
free(sl);
}
-int slRandomLevel(void) {
+int slRandomLevel(void)
+{
int level = 1;
while ((random() & 0xffff) < (SKIPLIST_P * 0xffff))
level += 1;
return (level < SKIPLIST_MAXLEVEL) ? level : SKIPLIST_MAXLEVEL;
}
-void slInsert(skiplist *sl, double score, int64_t obj) {
+void slInsert(skiplist *sl, double score, int64_t obj)
+{
skiplistNode *update[SKIPLIST_MAXLEVEL], *x;
unsigned int rank[SKIPLIST_MAXLEVEL];
int i, level;
x = sl->header;
- for (i = sl->level - 1; i >= 0; i--) {
+ for (i = sl->level - 1; i >= 0; i--)
+ {
/* store rank that is crossed to reach the insert position */
rank[i] = i == (sl->level - 1) ? 0 : rank[i + 1];
- while (x->level[i].forward && (x->level[i].forward->score < score || (x->level[i].forward->score == score && (x->level[i].forward->obj - obj) < 0))) {
+ while (x->level[i].forward && (x->level[i].forward->score < score || (x->level[i].forward->score == score && (x->level[i].forward->obj - obj) < 0)))
+ {
rank[i] += x->level[i].span;
x = x->level[i].forward;
}
@@ -80,8 +90,10 @@ void slInsert(skiplist *sl, double score, int64_t obj) {
* happen since the caller of slInsert() should test in the hash table
* if the element is already inside or not. */
level = slRandomLevel();
- if (level > sl->level) {
- for (i = sl->level; i < level; i++) {
+ if (level > sl->level)
+ {
+ for (i = sl->level; i < level; i++)
+ {
rank[i] = 0;
update[i] = sl->header;
update[i]->level[i].span = sl->length;
@@ -89,7 +101,8 @@ void slInsert(skiplist *sl, double score, int64_t obj) {
sl->level = level;
}
x = slCreateNode(level, score, obj);
- for (i = 0; i < level; i++) {
+ for (i = 0; i < level; i++)
+ {
x->level[i].forward = update[i]->level[i].forward;
update[i]->level[i].forward = x;
@@ -99,7 +112,8 @@ void slInsert(skiplist *sl, double score, int64_t obj) {
}
/* increment span for untouched levels */
- for (i = level; i < sl->level; i++) {
+ for (i = level; i < sl->level; i++)
+ {
update[i]->level[i].span++;
}
@@ -112,19 +126,27 @@ void slInsert(skiplist *sl, double score, int64_t obj) {
}
/* Internal function used by slDelete, slDeleteByScore */
-void slDeleteNode(skiplist *sl, skiplistNode *x, skiplistNode **update) {
+void slDeleteNode(skiplist *sl, skiplistNode *x, skiplistNode **update)
+{
int i;
- for (i = 0; i < sl->level; i++) {
- if (update[i]->level[i].forward == x) {
+ for (i = 0; i < sl->level; i++)
+ {
+ if (update[i]->level[i].forward == x)
+ {
update[i]->level[i].span += x->level[i].span - 1;
update[i]->level[i].forward = x->level[i].forward;
- } else {
+ }
+ else
+ {
update[i]->level[i].span -= 1;
}
}
- if (x->level[0].forward) {
+ if (x->level[0].forward)
+ {
x->level[0].forward->backward = x->backward;
- } else {
+ }
+ else
+ {
sl->tail = x->backward;
}
while (sl->level > 1 && sl->header->level[sl->level - 1].forward == NULL)
@@ -133,12 +155,14 @@ void slDeleteNode(skiplist *sl, skiplistNode *x, skiplistNode **update) {
}
/* Delete an element with matching score/object from the skiplist. */
-int slDelete(skiplist *sl, double score, int64_t obj) {
+int slDelete(skiplist *sl, double score, int64_t obj)
+{
skiplistNode *update[SKIPLIST_MAXLEVEL], *x;
int i;
x = sl->header;
- for (i = sl->level - 1; i >= 0; i--) {
+ for (i = sl->level - 1; i >= 0; i--)
+ {
while (x->level[i].forward && (x->level[i].forward->score < score || (x->level[i].forward->score == score && (x->level[i].forward->obj - obj) < 0)))
x = x->level[i].forward;
update[i] = x;
@@ -146,11 +170,14 @@ int slDelete(skiplist *sl, double score, int64_t obj) {
/* We may have multiple elements with the same score, what we need
* is to find the element with both the right score and object. */
x = x->level[0].forward;
- if (x && score == x->score && (x->obj == obj)) {
+ if (x && score == x->score && (x->obj == obj))
+ {
slDeleteNode(sl, x, update);
slFreeNode(x);
return 1;
- } else {
+ }
+ else
+ {
return 0; /* not found */
}
return 0; /* not found */
@@ -158,14 +185,17 @@ int slDelete(skiplist *sl, double score, int64_t obj) {
/* Delete all the elements with rank between start and end from the skiplist.
* Start and end are inclusive. Note that start and end need to be 1-based */
-unsigned long slDeleteByRank(skiplist *sl, unsigned int start, unsigned int end, slDeleteCb cb, void *ud) {
+unsigned long slDeleteByRank(skiplist *sl, unsigned int start, unsigned int end, slDeleteCb cb, void *ud)
+{
skiplistNode *update[SKIPLIST_MAXLEVEL], *x;
unsigned long traversed = 0, removed = 0;
int i;
x = sl->header;
- for (i = sl->level - 1; i >= 0; i--) {
- while (x->level[i].forward && (traversed + x->level[i].span) < start) {
+ for (i = sl->level - 1; i >= 0; i--)
+ {
+ while (x->level[i].forward && (traversed + x->level[i].span) < start)
+ {
traversed += x->level[i].span;
x = x->level[i].forward;
}
@@ -174,7 +204,8 @@ unsigned long slDeleteByRank(skiplist *sl, unsigned int start, unsigned int end,
traversed++;
x = x->level[0].forward;
- while (x && traversed <= end) {
+ while (x && traversed <= end)
+ {
skiplistNode *next = x->level[0].forward;
slDeleteNode(sl, x, update);
cb(ud, x->obj);
@@ -190,20 +221,24 @@ unsigned long slDeleteByRank(skiplist *sl, unsigned int start, unsigned int end,
* Returns 0 when the element cannot be found, rank otherwise.
* Note that the rank is 1-based due to the span of sl->header to the
* first element. */
-unsigned long slGetRank(skiplist *sl, double score, int64_t o) {
+unsigned long slGetRank(skiplist *sl, double score, int64_t o)
+{
skiplistNode *x;
unsigned long rank = 0;
int i;
x = sl->header;
- for (i = sl->level - 1; i >= 0; i--) {
- while (x->level[i].forward && (x->level[i].forward->score < score || (x->level[i].forward->score == score && (x->level[i].forward->obj - o) <= 0))) {
+ for (i = sl->level - 1; i >= 0; i--)
+ {
+ while (x->level[i].forward && (x->level[i].forward->score < score || (x->level[i].forward->score == score && (x->level[i].forward->obj - o) <= 0)))
+ {
rank += x->level[i].span;
x = x->level[i].forward;
}
/* x might be equal to sl->header, so test if obj is non-NULL */
- if (x->obj && (x->obj == o)) {
+ if (x->obj && (x->obj == o))
+ {
return rank;
}
}
@@ -211,8 +246,10 @@ unsigned long slGetRank(skiplist *sl, double score, int64_t o) {
}
/* Finds an element by its rank. The rank argument needs to be 1-based. */
-skiplistNode *slGetNodeByRank(skiplist *sl, unsigned long rank) {
- if (rank == 0 || rank > sl->length) {
+skiplistNode *slGetNodeByRank(skiplist *sl, unsigned long rank)
+{
+ if (rank == 0 || rank > sl->length)
+ {
return NULL;
}
@@ -221,12 +258,15 @@ skiplistNode *slGetNodeByRank(skiplist *sl, unsigned long rank) {
int i;
x = sl->header;
- for (i = sl->level - 1; i >= 0; i--) {
- while (x->level[i].forward && (traversed + x->level[i].span) <= rank) {
+ for (i = sl->level - 1; i >= 0; i--)
+ {
+ while (x->level[i].forward && (traversed + x->level[i].span) <= rank)
+ {
traversed += x->level[i].span;
x = x->level[i].forward;
}
- if (traversed == rank) {
+ if (traversed == rank)
+ {
return x;
}
}
@@ -236,11 +276,13 @@ skiplistNode *slGetNodeByRank(skiplist *sl, unsigned long rank) {
/* range [min, max], left & right both include */
/* Returns if there is a part of the zset is in range. */
-int slIsInRange(skiplist *sl, double min, double max) {
+int slIsInRange(skiplist *sl, double min, double max)
+{
skiplistNode *x;
/* Test for ranges that will always be empty. */
- if (min > max) {
+ if (min > max)
+ {
return 0;
}
x = sl->tail;
@@ -255,7 +297,8 @@ int slIsInRange(skiplist *sl, double min, double max) {
/* Find the first node that is contained in the specified range.
* Returns NULL when no element is contained in the range. */
-skiplistNode *slFirstInRange(skiplist *sl, double min, double max) {
+skiplistNode *slFirstInRange(skiplist *sl, double min, double max)
+{
skiplistNode *x;
int i;
@@ -264,7 +307,8 @@ skiplistNode *slFirstInRange(skiplist *sl, double min, double max) {
return NULL;
x = sl->header;
- for (i = sl->level - 1; i >= 0; i--) {
+ for (i = sl->level - 1; i >= 0; i--)
+ {
/* Go forward while *OUT* of range. */
while (x->level[i].forward && x->level[i].forward->score < min)
x = x->level[i].forward;
@@ -277,7 +321,8 @@ skiplistNode *slFirstInRange(skiplist *sl, double min, double max) {
/* Find the last node that is contained in the specified range.
* Returns NULL when no element is contained in the range. */
-skiplistNode *slLastInRange(skiplist *sl, double min, double max) {
+skiplistNode *slLastInRange(skiplist *sl, double min, double max)
+{
skiplistNode *x;
int i;
@@ -286,7 +331,8 @@ skiplistNode *slLastInRange(skiplist *sl, double min, double max) {
return NULL;
x = sl->header;
- for (i = sl->level - 1; i >= 0; i--) {
+ for (i = sl->level - 1; i >= 0; i--)
+ {
/* Go forward while *IN* range. */
while (x->level[i].forward && x->level[i].forward->score <= max)
x = x->level[i].forward;
diff --git a/framework/lualib-src/lua-zset/skiplist.h b/framework/lualib-src/lua-skiplist/skiplist.h
similarity index 89%
rename from framework/lualib-src/lua-zset/skiplist.h
rename to framework/lualib-src/lua-skiplist/skiplist.h
index d4c4dbd..dc06c89 100644
--- a/framework/lualib-src/lua-zset/skiplist.h
+++ b/framework/lualib-src/lua-skiplist/skiplist.h
@@ -3,17 +3,20 @@
#include
-typedef struct skiplistNode {
+typedef struct skiplistNode
+{
int64_t obj;
double score;
struct skiplistNode *backward;
- struct skiplistLevel {
+ struct skiplistLevel
+ {
struct skiplistNode *forward;
unsigned int span;
} level[];
} skiplistNode;
-typedef struct skiplist {
+typedef struct skiplist
+{
struct skiplistNode *header, *tail;
unsigned long length;
int level;
diff --git a/framework/lualib-src/lua-skipset/Makefile b/framework/lualib-src/lua-skipset/Makefile
new file mode 100644
index 0000000..74530ce
--- /dev/null
+++ b/framework/lualib-src/lua-skipset/Makefile
@@ -0,0 +1,30 @@
+SKYNET_ROOT ?= ../../skynet
+include $(SKYNET_ROOT)/platform.mk
+
+PLAT ?= none
+
+TARGET = ../../luaclib/skipset.so
+
+ifeq ($(PLAT), macosx)
+ CFLAGS = -g -O2 -dynamiclib -Wl,-undefined,dynamic_lookup
+else
+ifeq ($(PLAT), linux)
+ CFLAGS = -g -O2 -shared -fPIC
+endif
+endif
+
+LUA_LIB ?= $(SKYNET_ROOT)/3rd/lua/
+LUA_INC ?= $(SKYNET_ROOT)/3rd/lua/
+SKYNET_SRC ?= $(SKYNET_ROOT)/skynet-src
+
+SRC = .
+
+.PHONY: all clean
+
+all: $(TARGET)
+
+$(TARGET): $(foreach dir, $(SRC), $(wildcard $(dir)/*.c))
+ $(CC) $(CFLAGS) $(SHARED) -I$(LUA_INC) -I$(SKYNET_SRC) $^ -o $@
+
+clean:
+ rm -f *.o $(TARGET)
\ No newline at end of file
diff --git a/framework/lualib-src/lua-zset/lua-skipset.c b/framework/lualib-src/lua-skipset/lua-skipset.c
similarity index 75%
rename from framework/lualib-src/lua-zset/lua-skipset.c
rename to framework/lualib-src/lua-skipset/lua-skipset.c
index 42cf1c4..1114622 100644
--- a/framework/lualib-src/lua-zset/lua-skipset.c
+++ b/framework/lualib-src/lua-skipset/lua-skipset.c
@@ -6,46 +6,56 @@
#include "lauxlib.h"
#include "lua.h"
+// https://github.com/hongling0/lua-zset
+
#define SKIPLIST_MAXLEVEL 32
#define SKIPLIST_P 0.25
-typedef struct skipsetNode {
+typedef struct skipsetNode
+{
int64_t obj;
struct skipsetNode *backward;
- struct skiplistLevel {
+ struct skiplistLevel
+ {
struct skipsetNode *forward;
unsigned int span;
} level[];
} skipsetNode;
-typedef struct skipset {
+typedef struct skipset
+{
struct skipsetNode *header, *tail;
unsigned long length;
int level;
} skipset;
-static int slRandomLevel(void) {
+static int slRandomLevel(void)
+{
int level = 1;
while ((random() & 0xffff) < (SKIPLIST_P * 0xffff))
level += 1;
return (level < SKIPLIST_MAXLEVEL) ? level : SKIPLIST_MAXLEVEL;
}
-static skipsetNode *slCreateNode(int level, int64_t obj) {
+static skipsetNode *slCreateNode(int level, int64_t obj)
+{
skipsetNode *n = malloc(sizeof(*n) + level * sizeof(struct skiplistLevel));
n->obj = obj;
return n;
}
-static int slInsert(skipset *sl, int64_t obj) {
+static int slInsert(skipset *sl, int64_t obj)
+{
skipsetNode *update[SKIPLIST_MAXLEVEL], *x;
unsigned int rank[SKIPLIST_MAXLEVEL];
int i, level;
x = sl->header;
- for (i = sl->level - 1; i >= 0; i--) {
+ for (i = sl->level - 1; i >= 0; i--)
+ {
rank[i] = i == (sl->level - 1) ? 0 : rank[i + 1];
- while (x->level[i].forward && x->level[i].forward->obj < obj) {
+ while (x->level[i].forward && x->level[i].forward->obj < obj)
+ {
rank[i] += x->level[i].span;
x = x->level[i].forward;
}
@@ -56,8 +66,10 @@ static int slInsert(skipset *sl, int64_t obj) {
return 0;
level = slRandomLevel();
- if (level > sl->level) {
- for (i = sl->level; i < level; i++) {
+ if (level > sl->level)
+ {
+ for (i = sl->level; i < level; i++)
+ {
rank[i] = 0;
update[i] = sl->header;
update[i]->level[i].span = sl->length;
@@ -65,7 +77,8 @@ static int slInsert(skipset *sl, int64_t obj) {
sl->level = level;
}
x = slCreateNode(level, obj);
- for (i = 0; i < level; i++) {
+ for (i = 0; i < level; i++)
+ {
x->level[i].forward = update[i]->level[i].forward;
update[i]->level[i].forward = x;
@@ -73,7 +86,8 @@ static int slInsert(skipset *sl, int64_t obj) {
update[i]->level[i].span = (rank[0] - rank[i]) + 1;
}
- for (i = level; i < sl->level; i++) {
+ for (i = level; i < sl->level; i++)
+ {
update[i]->level[i].span++;
}
@@ -86,19 +100,27 @@ static int slInsert(skipset *sl, int64_t obj) {
return 1;
}
-static void slDeleteNode(skipset *sl, skipsetNode *x, skipsetNode **update) {
+static void slDeleteNode(skipset *sl, skipsetNode *x, skipsetNode **update)
+{
int i;
- for (i = 0; i < sl->level; i++) {
- if (update[i]->level[i].forward == x) {
+ for (i = 0; i < sl->level; i++)
+ {
+ if (update[i]->level[i].forward == x)
+ {
update[i]->level[i].span += x->level[i].span - 1;
update[i]->level[i].forward = x->level[i].forward;
- } else {
+ }
+ else
+ {
update[i]->level[i].span -= 1;
}
}
- if (x->level[0].forward) {
+ if (x->level[0].forward)
+ {
x->level[0].forward->backward = x->backward;
- } else {
+ }
+ else
+ {
sl->tail = x->backward;
}
while (sl->level > 1 && sl->header->level[sl->level - 1].forward == NULL)
@@ -106,51 +128,62 @@ static void slDeleteNode(skipset *sl, skipsetNode *x, skipsetNode **update) {
sl->length--;
}
-static void slFreeNode(skipsetNode *node) {
+static void slFreeNode(skipsetNode *node)
+{
free(node);
}
-static int slDelete(skipset *sl, int64_t obj) {
+static int slDelete(skipset *sl, int64_t obj)
+{
skipsetNode *update[SKIPLIST_MAXLEVEL], *x;
int i;
x = sl->header;
- for (i = sl->level - 1; i >= 0; i--) {
+ for (i = sl->level - 1; i >= 0; i--)
+ {
while (x->level[i].forward && x->level[i].forward->obj < obj)
x = x->level[i].forward;
update[i] = x;
}
x = x->level[0].forward;
- if (x && (x->obj == obj)) {
+ if (x && (x->obj == obj))
+ {
slDeleteNode(sl, x, update);
slFreeNode(x);
return 1;
- } else {
+ }
+ else
+ {
return 0; /* not found */
}
return 0; /* not found */
}
-static unsigned long slGetRank(skipset *sl, int64_t obj) {
+static unsigned long slGetRank(skipset *sl, int64_t obj)
+{
skipsetNode *x;
unsigned long rank = 0;
int i;
x = sl->header;
- for (i = sl->level - 1; i >= 0; i--) {
- while (x->level[i].forward && x->level[i].forward->obj <= obj) {
+ for (i = sl->level - 1; i >= 0; i--)
+ {
+ while (x->level[i].forward && x->level[i].forward->obj <= obj)
+ {
rank += x->level[i].span;
x = x->level[i].forward;
}
- if (x->obj && (x->obj == obj)) {
+ if (x->obj && (x->obj == obj))
+ {
return rank;
}
}
return 0;
}
-static skipsetNode *slGetNodeByRank(skipset *sl, unsigned long rank) {
+static skipsetNode *slGetNodeByRank(skipset *sl, unsigned long rank)
+{
if (rank == 0 || rank > sl->length)
return NULL;
@@ -159,12 +192,15 @@ static skipsetNode *slGetNodeByRank(skipset *sl, unsigned long rank) {
int i;
x = sl->header;
- for (i = sl->level - 1; i >= 0; i--) {
- while (x->level[i].forward && (traversed + x->level[i].span) <= rank) {
+ for (i = sl->level - 1; i >= 0; i--)
+ {
+ while (x->level[i].forward && (traversed + x->level[i].span) <= rank)
+ {
traversed += x->level[i].span;
x = x->level[i].forward;
}
- if (traversed == rank) {
+ if (traversed == rank)
+ {
return x;
}
}
@@ -172,14 +208,17 @@ static skipsetNode *slGetNodeByRank(skipset *sl, unsigned long rank) {
return NULL;
}
-unsigned long slDeleteByRank(skipset *sl, unsigned int rank) {
+unsigned long slDeleteByRank(skipset *sl, unsigned int rank)
+{
skipsetNode *update[SKIPLIST_MAXLEVEL], *x;
unsigned long traversed = 0;
int i;
x = sl->header;
- for (i = sl->level - 1; i >= 0; i--) {
- while (x->level[i].forward && (traversed + x->level[i].span) < rank) {
+ for (i = sl->level - 1; i >= 0; i--)
+ {
+ while (x->level[i].forward && (traversed + x->level[i].span) < rank)
+ {
traversed += x->level[i].span;
x = x->level[i].forward;
}
@@ -187,7 +226,8 @@ unsigned long slDeleteByRank(skipset *sl, unsigned int rank) {
}
x = x->level[0].forward;
- if (x) {
+ if (x)
+ {
slDeleteNode(sl, x, update);
slFreeNode(x);
return 1;
@@ -195,7 +235,8 @@ unsigned long slDeleteByRank(skipset *sl, unsigned int rank) {
return 0;
}
-static skipset *slCreate(void) {
+static skipset *slCreate(void)
+{
int j;
skipset *sl;
@@ -203,7 +244,8 @@ static skipset *slCreate(void) {
sl->level = 1;
sl->length = 0;
sl->header = slCreateNode(SKIPLIST_MAXLEVEL, 0);
- for (j = 0; j < SKIPLIST_MAXLEVEL; j++) {
+ for (j = 0; j < SKIPLIST_MAXLEVEL; j++)
+ {
sl->header->level[j].forward = NULL;
sl->header->level[j].span = 0;
}
@@ -212,10 +254,12 @@ static skipset *slCreate(void) {
return sl;
}
-static void slFree(skipset *sl) {
+static void slFree(skipset *sl)
+{
skipsetNode *node = sl->header->level[0].forward, *next;
free(sl->header);
- while (node) {
+ while (node)
+ {
next = node->level[0].forward;
slFreeNode(node);
node = next;
@@ -224,16 +268,19 @@ static void slFree(skipset *sl) {
}
static inline skipset *
-_to_skipset(lua_State *L, int idx) {
+_to_skipset(lua_State *L, int idx)
+{
skipset **sl = lua_touserdata(L, idx);
- if (sl == NULL) {
+ if (sl == NULL)
+ {
luaL_error(L, "must be skipset object");
}
return *sl;
}
static int
-_insert(lua_State *L) {
+_insert(lua_State *L)
+{
skipset *sl = _to_skipset(L, 1);
lua_Integer obj = luaL_checkinteger(L, 2);
lua_pushboolean(L, slInsert(sl, obj));
@@ -241,7 +288,8 @@ _insert(lua_State *L) {
}
static int
-_delete(lua_State *L) {
+_delete(lua_State *L)
+{
skipset *sl = _to_skipset(L, 1);
lua_Integer obj = luaL_checkinteger(L, 2);
lua_pushboolean(L, slDelete(sl, obj));
@@ -249,19 +297,22 @@ _delete(lua_State *L) {
}
static int
-_get_count(lua_State *L) {
+_get_count(lua_State *L)
+{
skipset *sl = _to_skipset(L, 1);
lua_pushinteger(L, sl->length);
return 1;
}
static int
-_get_rank(lua_State *L) {
+_get_rank(lua_State *L)
+{
skipset *sl = _to_skipset(L, 1);
lua_Integer obj = luaL_checkinteger(L, 2);
unsigned long rank = slGetRank(sl, obj);
- if (rank == 0) {
+ if (rank == 0)
+ {
return 0;
}
@@ -270,12 +321,14 @@ _get_rank(lua_State *L) {
}
static int
-_get_by_rank(lua_State *L) {
+_get_by_rank(lua_State *L)
+{
skipset *sl = _to_skipset(L, 1);
unsigned long r1 = luaL_checkinteger(L, 2);
skipsetNode *node = slGetNodeByRank(sl, r1);
- if (node) {
+ if (node)
+ {
lua_pushinteger(L, node->obj);
return 1;
}
@@ -283,7 +336,8 @@ _get_by_rank(lua_State *L) {
}
static int
-_delete_by_rank(lua_State *L) {
+_delete_by_rank(lua_State *L)
+{
skipset *sl = _to_skipset(L, 1);
unsigned int rank = luaL_checkinteger(L, 2);
lua_pushinteger(L, slDeleteByRank(sl, rank));
@@ -291,7 +345,8 @@ _delete_by_rank(lua_State *L) {
}
static int
-_new(lua_State *L) {
+_new(lua_State *L)
+{
skipset *psl = slCreate();
skipset **sl = (skipset **)lua_newuserdata(L, sizeof(skipset *));
@@ -302,27 +357,28 @@ _new(lua_State *L) {
}
static int
-_release(lua_State *L) {
+_release(lua_State *L)
+{
skipset *sl = _to_skipset(L, 1);
slFree(sl);
return 0;
}
LUAMOD_API int
-luaopen_skipset_c(lua_State *L) {
+luaopen_skipset_c(lua_State *L)
+{
luaL_checkversion(L);
luaL_Reg l[] = {
- { "insert", _insert },
- { "delete", _delete },
- { "delete_byrank", _delete_by_rank },
+ {"insert", _insert},
+ {"delete", _delete},
+ {"delete_byrank", _delete_by_rank},
- { "count", _get_count },
- { "rank", _get_rank },
- { "byrank", _get_by_rank },
+ {"count", _get_count},
+ {"rank", _get_rank},
+ {"byrank", _get_by_rank},
- { NULL, NULL }
- };
+ {NULL, NULL}};
lua_createtable(L, 0, 2);
diff --git a/framework/3rd/termbox/src/termbox.h b/framework/lualib-src/lua-termfx/termbox.h
old mode 100755
new mode 100644
similarity index 100%
rename from framework/3rd/termbox/src/termbox.h
rename to framework/lualib-src/lua-termfx/termbox.h
diff --git a/framework/lualib-src/testluaclib.lua b/framework/lualib-src/testluaclib.lua
index 90771cd..b6f3ca9 100644
--- a/framework/lualib-src/testluaclib.lua
+++ b/framework/lualib-src/testluaclib.lua
@@ -34,3 +34,9 @@ print("termfx", termfx)
local socket = require "socket.core"
print("socket", socket)
+local skipset = require "skipset.c"
+print("skipset", skipset)
+
+local math3d = require "math3d"
+print("math3d", math3d)
+
diff --git a/framework/skynet b/framework/skynet
index ff24ce6..bc298e4 160000
--- a/framework/skynet
+++ b/framework/skynet
@@ -1 +1 @@
-Subproject commit ff24ce6b004259f4e372e0f82bd9104a8b9a1b7b
+Subproject commit bc298e4a91d016df653e9a48f118672d209a3a05