summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorTyge Løvset <[email protected]>2022-05-31 15:04:52 +0200
committerTyge Løvset <[email protected]>2022-05-31 15:04:52 +0200
commiteb9821bec4a292458499042392924595b3338085 (patch)
tree90b6286619deefc2eaf72fce4fc67d5959c84557
parent0a92ec2235b5f42e93012be14938bb11e3f3650a (diff)
downloadSTC-modified-eb9821bec4a292458499042392924595b3338085.tar.gz
STC-modified-eb9821bec4a292458499042392924595b3338085.zip
1) REMOVED files/modules not relevant: makes lib more focused:
- threads.h/threads.c (external lib) - coptions.h - will be kept as a gist. - more will follow, (examples, some benchmarks, etc). 2) Replaced UTF8 decoder with Björn Höhrmann's DFA decoder.
-rw-r--r--README.md10
-rw-r--r--docs/coption_api.md71
-rw-r--r--examples/gauss1.c20
-rw-r--r--examples/threads_demo.c63
-rw-r--r--include/stc/coption.h180
-rw-r--r--include/stc/cstr.h6
-rw-r--r--include/stc/utf8.h17
-rw-r--r--include/threads.h481
-rw-r--r--src/threads.c932
-rw-r--r--src/utf8code.c88
10 files changed, 57 insertions, 1811 deletions
diff --git a/README.md b/README.md
index 0d402d79..ba2fcb3e 100644
--- a/README.md
+++ b/README.md
@@ -53,6 +53,7 @@ Contents
- [***cmap*** - **std::unordered_map** alike type](docs/cmap_api.md)
- [***cpque*** - **std::priority_queue** alike type](docs/cpque_api.md)
- [***cqueue*** - **std::queue** alike type](docs/cqueue_api.md)
+- [***crandom*** - A novel very fast *PRNG* named **stc64**](docs/crandom_api.md)
- [***cset*** - **std::unordered_set** alike type](docs/cset_api.md)
- [***csmap*** - **std::map** sorted map alike type](docs/csmap_api.md)
- [***csset*** - **std::set** sorted set alike type](docs/csset_api.md)
@@ -61,11 +62,6 @@ Contents
- [***csview*** - **std::string_view** alike type](docs/csview_api.md)
- [***cvec*** - **std::vector** alike type](docs/cvec_api.md)
-Others:
-- [***crandom*** - A novel very fast *PRNG* named **stc64**](docs/crandom_api.md)
-- [***coption*** - Command line options scanner](docs/coption_api.md)
-- [***threads*** - Mimic **C11-threads** (by Marcus Geelnard)](include/threads.h)
-
Highlights
----------
- **User friendly** - Just include the headers and you are good. The API and functionality is very close to c++ STL, and is fully listed in the docs.
@@ -257,8 +253,8 @@ If containers are used across several translation units with common instantiated
recommended to build as a "library" with external linking to minimize executable size. To enable this,
specify `-DSTC_HEADER` as compiler option in your build environment. Next, place all the instantiations
of the containers used inside a single C-source file as in the example below, and `#define STC_IMPLEMENT` at top.
-You may also cherry-pick external linking mode on individual containers by `#define i_opt c_header` and
-`#define i_opt c_implement`, or force static symbols by `#define i_opt c_static` before container includes.
+You may also cherry-pick external linking mode on individual containers by `#define i_header` and
+`#define i_implement`, or force static symbols by `#define i_static` before container includes.
```c
// stc_libs.c
#define STC_IMPLEMENT
diff --git a/docs/coption_api.md b/docs/coption_api.md
deleted file mode 100644
index be0d0978..00000000
--- a/docs/coption_api.md
+++ /dev/null
@@ -1,71 +0,0 @@
-# STC [coption](../include/stc/coption.h): Command line argument parsing
-
-This describes the API of the *coption_get()* function for command line argument parsing.
-
-See [getopt_long](https://www.freebsd.org/cgi/man.cgi?getopt_long(3)) for a similar posix function.
-
-## Types
-
-```c
-typedef enum {
- coption_no_argument,
- coption_required_argument,
- coption_optional_argument
-} coption_type;
-
-typedef struct {
- const char *name;
- coption_type type;
- int val;
-} coption_long;
-
-typedef struct {
- int ind; /* equivalent to posix optind */
- int opt; /* equivalent to posix optopt */
- const char *optstr; /* points to the option string, if any */
- const char *arg; /* equivalent to posix optarg */
- ...
-} coption;
-```
-
-## Methods
-
-```c
-coption coption_init(void);
-int coption_get(coption *opt, int argc, char *argv[],
- const char *shortopts, const coption_long *longopts);
-```
-
-## Example
-
-```c
-#include <stdio.h>
-#include <stc/coption.h>
-
-int main(int argc, char *argv[]) {
- coption_long longopts[] = {
- {"foo", coption_no_argument, 'f'},
- {"bar", coption_required_argument, 'b'},
- {"opt", coption_optional_argument, 'o'},
- {0}
- };
- const char* shortopts = "xy:z::123";
- if (argc == 1)
- printf("Usage: program -x -y ARG -z [ARG] -1 -2 -3 --foo --bar ARG --opt [ARG] [ARGUMENTS]\n", argv[0]);
- coption opt = coption_init();
- int c;
- while ((c = coption_get(&opt, argc, argv, shortopts, longopts)) != -1) {
- switch (c) {
- case '?': printf("error: unknown option: %s\n", opt.optstr); break;
- case ':': printf("error: missing argument for %s\n", opt.optstr); break;
- default: printf("option: %c [%s]\n", opt.opt, opt.arg ? opt.arg : ""); break;
- }
- }
- printf("\nNon-option arguments:");
- for (int i = opt.ind; i < argc; ++i)
- printf(" %s", argv[i]);
- putchar('\n');
-}
- return 0;
-}
-```
diff --git a/examples/gauss1.c b/examples/gauss1.c
index da58c86e..c3ff5a4d 100644
--- a/examples/gauss1.c
+++ b/examples/gauss1.c
@@ -6,17 +6,15 @@
#include <stc/cstr.h>
// Declare int -> int hashmap. Uses typetag 'ii' for ints.
-#define i_key int32_t
-#define i_val size_t
+#define i_key int
+#define i_val int
#define i_tag ii
#include <stc/cmap.h>
-// Declare int vector with map entries that can be sorted by map keys.
-struct { int first; size_t second; } typedef mapval;
-
-#define i_val mapval
+// Declare int vector with entries from the cmap.
+#define i_val cmap_ii_raw
#define i_less(x, y) x->first < y->first
-#define i_tag pair
+#define i_tag ii
#include <stc/cvec.h>
int main()
@@ -32,7 +30,7 @@ int main()
stc64_normalf_t dist = stc64_normalf_new(Mean, StdDev);
// Create and init histogram vec and map with defered destructors:
- c_auto (cvec_pair, histvec)
+ c_auto (cvec_ii, histvec)
c_auto (cmap_ii, histmap)
{
c_forrange (N) {
@@ -42,13 +40,13 @@ int main()
// Transfer map to vec and sort it by map keys.
c_foreach (i, cmap_ii, histmap)
- cvec_pair_push_back(&histvec, (mapval){i.ref->first, i.ref->second});
+ cvec_ii_push_back(&histvec, (cmap_ii_raw){i.ref->first, i.ref->second});
- cvec_pair_sort(&histvec);
+ cvec_ii_sort(&histvec);
// Print the gaussian bar chart
c_auto (cstr, bar)
- c_foreach (i, cvec_pair, histvec) {
+ c_foreach (i, cvec_ii, histvec) {
size_t n = (size_t) (i.ref->second * StdDev * Scale * 2.5 / (float)N);
if (n > 0) {
cstr_resize(&bar, n, '*');
diff --git a/examples/threads_demo.c b/examples/threads_demo.c
deleted file mode 100644
index 98c86e52..00000000
--- a/examples/threads_demo.c
+++ /dev/null
@@ -1,63 +0,0 @@
-// Example translated to C from https://en.cppreference.com/w/cpp/memory/shared_ptr
-
-#include <stdio.h>
-#include <threads.h>
-#include <time.h>
-
-struct Base
-{
- int value;
-} typedef Base;
-
-#define i_type BaseArc
-#define i_val Base
-#define i_valdrop(x) printf("Drop Base: %d\n", (x)->value)
-#define i_opt c_no_cmp
-#include <stc/carc.h>
-
-mtx_t mtx;
-
-void* thr(BaseArc* p)
-{
- struct timespec one_sec = {.tv_sec=1};
- thrd_sleep(&one_sec, NULL);
- BaseArc lp = BaseArc_clone(*p); // thread-safe, even though the
- // shared use_count is incremented
- c_autoscope (mtx_lock(&mtx), mtx_unlock(&mtx))
- {
- printf("local pointer in a thread:\n"
- " p.get() = %p, p.use_count() = %ld\n", (void*)lp.get, BaseArc_use_count(lp));
- /* safe to modify base here */
- lp.get->value += 1;
- }
- BaseArc_drop(&lp);
- BaseArc_drop(p);
- return NULL;
-}
-
-int main()
-{
- BaseArc p = BaseArc_make((Base){0});
-
- mtx_init(&mtx, mtx_plain);
- printf("Created a shared Base\n"
- " p.get() = %p, p.use_count() = %ld\n", (void*)p.get, BaseArc_use_count(p));
- enum {N = 3};
- struct { thrd_t t; BaseArc p; } task[N];
- c_forrange (i, N) {
- task[i].p = BaseArc_clone(p);
- thrd_create(&task[i].t, (thrd_start_t)thr, &task[i].p);
- }
-
- BaseArc_reset(&p);
- printf("Shared ownership between %d threads and released\n"
- "ownership from main:\n"
- " p.get() = %p, p.use_count() = %ld\n", N, (void*)p.get, BaseArc_use_count(p));
-
- c_forrange (i, N) thrd_join(task[i].t, NULL);
- printf("All threads completed, the last one deleted Base\n");
-}
-
-#ifdef STC_TINYCTHREAD_H_
-#include "../src/threads.c"
-#endif
diff --git a/include/stc/coption.h b/include/stc/coption.h
deleted file mode 100644
index d726fb0c..00000000
--- a/include/stc/coption.h
+++ /dev/null
@@ -1,180 +0,0 @@
-/* MIT License
- *
- * Copyright (c) 2022 Tyge Løvset, NORCE, www.norceresearch.no
- *
- * 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.
- */
-/*
-Inspired by https://attractivechaos.wordpress.com/2018/08/31/a-survey-of-argument-parsing-libraries-in-c-c
-Fixed major bugs with optional arguments (both long and short).
-Added arg->optstr output field, more consistent API.
-
-coption_get() is similar to GNU's getopt_long(). Each call parses one option and
-returns the option name. opt->arg points to the option argument if present.
-The function returns -1 when all command-line arguments are parsed. In this case,
-opt->ind is the index of the first non-option argument.
-
-#include <stdio.h>
-#include <stc/coption.h>
-
-int main(int argc, char *argv[])
-{
- coption_long longopts[] = {
- {"foo", coption_no_argument, 'f'},
- {"bar", coption_required_argument, 'b'},
- {"opt", coption_optional_argument, 'o'},
- {0}
- };
- const char* optstr = "xy:z::123";
- printf("program -x -y ARG -z [ARG] -1 -2 -3 --foo --bar ARG --opt [ARG] [ARGUMENTS]\n");
- int c;
- coption opt = coption_init();
- while ((c = coption_get(&opt, argc, argv, optstr, longopts)) != -1) {
- switch (c) {
- case '?': printf("error: unknown option: %s\n", opt.optstr); break;
- case ':': printf("error: missing argument for %s (%c)\n", opt.optstr, opt.opt); break;
- default: printf("option: %c [%s]\n", opt.opt, opt.arg ? opt.arg : ""); break;
- }
- }
- printf("\nNon-option arguments:");
- for (int i = opt.ind; i < argc; ++i)
- printf(" %s", argv[i]);
- putchar('\n');
- return 0;
-}
-*/
-#ifndef COPTION_H_INCLUDED
-#define COPTION_H_INCLUDED
-
-#include <string.h>
-#include <stdbool.h>
-
-typedef enum {
- coption_no_argument,
- coption_required_argument,
- coption_optional_argument
-} coption_type;
-
-typedef struct {
- const char *name;
- coption_type type;
- int val;
-} coption_long;
-
-typedef struct {
- int ind; /* equivalent to optind */
- int opt; /* equivalent to optopt */
- const char *optstr; /* points to the option string */
- const char *arg; /* equivalent to optarg */
- int _i, _pos, _nargs;
- char _optstr[4];
-} coption;
-
-static inline coption coption_init(void) {
- coption opt = {1, 0, NULL, NULL, 1, 0, 0, {'-', '?', '\0'}};
- return opt;
-}
-
-/* move argv[j] over n elements to the left */
-static void coption_permute_(char *argv[], int j, int n) {
- int k;
- char *p = argv[j];
- for (k = 0; k < n; ++k)
- argv[j - k] = argv[j - k - 1];
- argv[j - k] = p;
-}
-
-/* @param opt output; must be initialized to coption_init() on first call
- * @return ASCII val for a short option; longopt.val for a long option;
- * -1 if argv[] is fully processed; '?' for an unknown option or
- * an ambiguous long option; ':' if an option argument is missing
- */
-static int coption_get(coption *opt, int argc, char *argv[],
- const char *shortopts, const coption_long *longopts) {
- int optc = -1, i0, j, posixly_correct = (shortopts[0] == '+');
- if (!posixly_correct) {
- while (opt->_i < argc && (argv[opt->_i][0] != '-' || argv[opt->_i][1] == '\0'))
- ++opt->_i, ++opt->_nargs;
- }
- opt->opt = 0, opt->optstr = NULL, opt->arg = NULL, i0 = opt->_i;
- if (opt->_i >= argc || argv[opt->_i][0] != '-' || argv[opt->_i][1] == '\0') {
- opt->ind = opt->_i - opt->_nargs;
- return -1;
- }
- if (argv[opt->_i][0] == '-' && argv[opt->_i][1] == '-') { /* "--" or a long option */
- if (argv[opt->_i][2] == '\0') { /* a bare "--" */
- coption_permute_(argv, opt->_i, opt->_nargs);
- ++opt->_i, opt->ind = opt->_i - opt->_nargs;
- return -1;
- }
- optc = '?', opt->_pos = -1;
- if (longopts) { /* parse long options */
- int k, n_exact = 0, n_partial = 0;
- const coption_long *o = 0, *o_exact = 0, *o_partial = 0;
- for (j = 2; argv[opt->_i][j] != '\0' && argv[opt->_i][j] != '='; ++j) {} /* find the end of the option name */
- for (k = 0; longopts[k].name != 0; ++k)
- if (strncmp(&argv[opt->_i][2], longopts[k].name, j - 2) == 0) {
- if (longopts[k].name[j - 2] == 0) ++n_exact, o_exact = &longopts[k];
- else ++n_partial, o_partial = &longopts[k];
- }
- opt->optstr = argv[opt->_i];
- if (n_exact > 1 || (n_exact == 0 && n_partial > 1)) return '?';
- o = n_exact == 1? o_exact : n_partial == 1? o_partial : 0;
- if (o) {
- opt->opt = optc = o->val;
- if (o->type != coption_no_argument) {
- if (argv[opt->_i][j] == '=')
- opt->arg = &argv[opt->_i][j + 1];
- else if (argv[opt->_i][j] == '\0' && opt->_i < argc - 1 && (o->type == coption_required_argument ||
- argv[opt->_i + 1][0] != '-'))
- opt->arg = argv[++opt->_i];
- else if (o->type == coption_required_argument)
- optc = ':'; /* missing option argument */
- }
- }
- }
- } else { /* a short option */
- const char *p;
- if (opt->_pos == 0) opt->_pos = 1;
- optc = opt->opt = argv[opt->_i][opt->_pos++];
- opt->_optstr[1] = optc, opt->optstr = opt->_optstr;
- p = strchr((char *) shortopts, optc);
- if (p == 0) {
- optc = '?'; /* unknown option */
- } else if (p[1] == ':') {
- if (argv[opt->_i][opt->_pos] != '\0')
- opt->arg = &argv[opt->_i][opt->_pos];
- else if (opt->_i < argc - 1 && (p[2] != ':' || argv[opt->_i + 1][0] != '-'))
- opt->arg = argv[++opt->_i];
- else if (p[2] != ':')
- optc = ':';
- opt->_pos = -1;
- }
- }
- if (opt->_pos < 0 || argv[opt->_i][opt->_pos] == 0) {
- ++opt->_i, opt->_pos = 0;
- if (opt->_nargs > 0) /* permute */
- for (j = i0; j < opt->_i; ++j)
- coption_permute_(argv, j, opt->_nargs);
- }
- opt->ind = opt->_i - opt->_nargs;
- return optc;
-}
-
-#endif
diff --git a/include/stc/cstr.h b/include/stc/cstr.h
index 7c788ce4..ce8b9e26 100644
--- a/include/stc/cstr.h
+++ b/include/stc/cstr.h
@@ -180,12 +180,6 @@ extern void cstr_uppercase(cstr* self);
STC_INLINE bool cstr_valid_u8(const cstr* self)
{ return utf8_valid(cstr_str(self)); }
-STC_INLINE utf8_decode_t cstr_peek(const cstr* self, size_t bytepos) {
- utf8_decode_t d = {UTF8_OK};
- utf8_peek(&d, cstr_str(self) + bytepos);
- return d;
-}
-
// other utf8
STC_INLINE size_t cstr_size_u8(cstr s)
diff --git a/include/stc/utf8.h b/include/stc/utf8.h
index e5fa5894..630a7a7c 100644
--- a/include/stc/utf8.h
+++ b/include/stc/utf8.h
@@ -35,19 +35,22 @@ bool utf8_isalpha(uint32_t c);
bool utf8_isalnum(uint32_t c);
uint32_t utf8_tolower(uint32_t c);
uint32_t utf8_toupper(uint32_t c);
-
bool utf8_valid(const char* s);
bool utf8_valid_n(const char* s, size_t n);
-
int utf8_icmp_n(size_t u8max, const char* s1, size_t n1,
const char* s2, size_t n2);
+unsigned utf8_encode(char *out, uint32_t c);
+
/* encode/decode next utf8 codepoint. */
-enum { UTF8_OK = 0, UTF8_ERROR = 4 };
-typedef struct { uint32_t state, codep, size; } utf8_decode_t;
+typedef struct { uint32_t state, codep; } utf8_decode_t;
-void utf8_peek(utf8_decode_t* d, const char *s);
-unsigned utf8_encode(char *out, uint32_t c);
-void utf8_decode(utf8_decode_t *d, const uint8_t b);
+STC_INLINE uint32_t utf8_decode(utf8_decode_t* d, const uint32_t byte) {
+ extern const uint8_t utf8_dtab[];
+ const uint32_t type = utf8_dtab[byte];
+ d->codep = d->state ? (byte & 0x3fu) | (d->codep << 6)
+ : (0xff >> type) & byte;
+ return d->state = utf8_dtab[256 + d->state + type];
+}
/* case-insensitive utf8 string comparison */
STC_INLINE int utf8_icmp(const char* s1, const char* s2) {
diff --git a/include/threads.h b/include/threads.h
deleted file mode 100644
index 73919aed..00000000
--- a/include/threads.h
+++ /dev/null
@@ -1,481 +0,0 @@
-/* -*- mode: c; tab-width: 2; indent-tabs-mode: nil; -*-
-Copyright (c) 2012 Marcus Geelnard
-Copyright (c) 2013-2016 Evan Nemerson
-This software is provided 'as-is', without any express or implied
-warranty. In no event will the authors be held liable for any damages
-arising from the use of this software.
-Permission is granted to anyone to use this software for any purpose,
-including commercial applications, and to alter it and redistribute it
-freely, subject to the following restrictions:
- 1. The origin of this software must not be misrepresented; you must not
- claim that you wrote the original software. If you use this software
- in a product, an acknowledgment in the product documentation would be
- appreciated but is not required.
- 2. Altered source versions must be plainly marked as such, and must not be
- misrepresented as being the original software.
- 3. This notice may not be removed or altered from any source
- distribution.
-*/
-
-#ifndef STC_TINYCTHREAD_H_
-#define STC_TINYCTHREAD_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
-* @file
-* @mainpage TinyCThread API Reference
-*
-* @section intro_sec Introduction
-* TinyCThread is a minimal, portable implementation of basic threading
-* classes for C.
-*
-* They closely mimic the functionality and naming of the C11 standard, and
-* should be easily replaceable with the corresponding standard variants.
-*
-* @section port_sec Portability
-* The Win32 variant uses the native Win32 API for implementing the thread
-* classes, while for other systems, the POSIX threads API (pthread) is used.
-*
-* @section misc_sec Miscellaneous
-* The following special keywords are available: #_Thread_local.
-*
-* For more detailed information, browse the different sections of this
-* documentation. A good place to start is:
-* tinycthread.h.
-*/
-
-/* Which platform are we on? */
-#if !defined(_TTHREAD_PLATFORM_DEFINED_)
- #if defined(_WIN32) || defined(__WIN32__) || defined(__WINDOWS__)
- #define _TTHREAD_WIN32_
- #else
- #define _TTHREAD_POSIX_
- #endif
- #define _TTHREAD_PLATFORM_DEFINED_
-#endif
-
-/* Activate some POSIX functionality (e.g. clock_gettime and recursive mutexes) */
-#if defined(_TTHREAD_POSIX_)
- #undef _FEATURES_H
- #if !defined(_GNU_SOURCE)
- #define _GNU_SOURCE
- #endif
- #if !defined(_POSIX_C_SOURCE) || ((_POSIX_C_SOURCE - 0) < 199309L)
- #undef _POSIX_C_SOURCE
- #define _POSIX_C_SOURCE 199309L
- #endif
- #if !defined(_XOPEN_SOURCE) || ((_XOPEN_SOURCE - 0) < 500)
- #undef _XOPEN_SOURCE
- #define _XOPEN_SOURCE 500
- #endif
- #define _XPG6
-#endif
-
-/* Generic includes */
-#include <time.h>
-
-/* Platform specific includes */
-#if defined(_TTHREAD_POSIX_)
- #include <pthread.h>
-#elif defined(_TTHREAD_WIN32_)
- #ifndef WIN32_LEAN_AND_MEAN
- #define WIN32_LEAN_AND_MEAN
- #define __UNDEF_LEAN_AND_MEAN
- #endif
- #include <windows.h>
- #ifdef __UNDEF_LEAN_AND_MEAN
- #undef WIN32_LEAN_AND_MEAN
- #undef __UNDEF_LEAN_AND_MEAN
- #endif
-#endif
-
-/* Compiler-specific information */
-#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
- #define TTHREAD_NORETURN _Noreturn
-#elif defined(__GNUC__)
- #define TTHREAD_NORETURN __attribute__((__noreturn__))
-#else
- #define TTHREAD_NORETURN
-#endif
-
-/* If TIME_UTC is missing, provide it and provide a wrapper for
- timespec_get. */
-#ifndef TIME_UTC
-#define TIME_UTC 1
-#define _TTHREAD_EMULATE_TIMESPEC_GET_
-
-#if defined(_TTHREAD_WIN32_)
-struct _tthread_timespec {
- time_t tv_sec;
- long tv_nsec;
-};
-#define timespec _tthread_timespec
-#endif
-
-int _tthread_timespec_get(struct timespec *ts, int base);
-#define timespec_get _tthread_timespec_get
-#endif
-
-/** TinyCThread version (major number). */
-#define TINYCTHREAD_VERSION_MAJOR 1
-/** TinyCThread version (minor number). */
-#define TINYCTHREAD_VERSION_MINOR 2
-/** TinyCThread version (full version). */
-#define TINYCTHREAD_VERSION (TINYCTHREAD_VERSION_MAJOR * 100 + TINYCTHREAD_VERSION_MINOR)
-
-/**
-* @def _Thread_local
-* Thread local storage keyword.
-* A variable that is declared with the @c _Thread_local keyword makes the
-* value of the variable local to each thread (known as thread-local storage,
-* or TLS). Example usage:
-* @code
-* // This variable is local to each thread.
-* _Thread_local int variable;
-* @endcode
-* @note The @c _Thread_local keyword is a macro that maps to the corresponding
-* compiler directive (e.g. @c __declspec(thread)).
-* @note This directive is currently not supported on Mac OS X (it will give
-* a compiler error), since compile-time TLS is not supported in the Mac OS X
-* executable format. Also, some older versions of MinGW (before GCC 4.x) do
-* not support this directive, nor does the Tiny C Compiler.
-* @hideinitializer
-*/
-
-#if !(defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201102L) && !defined(__STDC_NO_THREADS__)) && !defined(_Thread_local)
- #if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__SUNPRO_CC) || defined(__IBMCPP__)
- #define _Thread_local __thread
- #else
- #define _Thread_local __declspec(thread)
- #endif
-#elif defined(__GNUC__) && defined(__GNUC_MINOR__) && (((__GNUC__ << 8) | __GNUC_MINOR__) < ((4 << 8) | 9))
- #define _Thread_local __thread
-#endif
-#ifdef _Thread_local
- #define thread_local _Thread_local
-#endif
-
-/* Macros */
-#if defined(_TTHREAD_WIN32_)
-#define TSS_DTOR_ITERATIONS (4)
-#else
-#define TSS_DTOR_ITERATIONS PTHREAD_DESTRUCTOR_ITERATIONS
-#endif
-
-/* Function return values */
-enum {
- thrd_success = 0, /**< The requested operation succeeded */
- thrd_busy = 1, /**< The requested operation failed because a resource requested by a test and return function is already in use */
- thrd_error = 2, /**< The requested operation failed */
- thrd_nomem = 3, /**< The requested operation failed because it was unable to allocate memory */
- thrd_timedout = 4 /**< The time specified in the call was reached without acquiring the requested resource */
-};
-
-/* Mutex types */
-enum {
- mtx_plain = 0,
- mtx_recursive = 1,
- mtx_timed = 2,
-};
-
-/* Mutex */
-#if defined(_TTHREAD_WIN32_)
-typedef struct {
- union {
- CRITICAL_SECTION cs; /* Critical section handle (used for non-timed mutexes) */
- HANDLE mut; /* Mutex handle (used for timed mutex) */
- } mHandle; /* Mutex handle */
- int mAlreadyLocked; /* TRUE if the mutex is already locked */
- int mRecursive; /* TRUE if the mutex is recursive */
- int mTimed; /* TRUE if the mutex is timed */
-} mtx_t;
-#else
-typedef pthread_mutex_t mtx_t;
-#endif
-
-/** Create a mutex object.
-* @param mtx A mutex object.
-* @param type Bit-mask that must have one of the following six values:
-* @li @c mtx_plain for a simple non-recursive mutex
-* @li @c mtx_timed for a non-recursive mutex that supports timeout
-* @li @c mtx_plain | @c mtx_recursive (same as @c mtx_plain, but recursive)
-* @li @c mtx_timed | @c mtx_recursive (same as @c mtx_timed, but recursive)
-* @return @ref thrd_success on success, or @ref thrd_error if the request could
-* not be honored.
-*/
-int mtx_init(mtx_t *mtx, int type);
-
-/** Release any resources used by the given mutex.
-* @param mtx A mutex object.
-*/
-void mtx_destroy(mtx_t *mtx);
-
-/** Lock the given mutex.
-* Blocks until the given mutex can be locked. If the mutex is non-recursive, and
-* the calling thread already has a lock on the mutex, this call will block
-* forever.
-* @param mtx A mutex object.
-* @return @ref thrd_success on success, or @ref thrd_error if the request could
-* not be honored.
-*/
-int mtx_lock(mtx_t *mtx);
-
-/** Lock the given mutex, or block until a specific point in time.
-* Blocks until either the given mutex can be locked, or the specified TIME_UTC
-* based time.
-* @param mtx A mutex object.
-* @param ts A UTC based calendar time
-* @return @ref The mtx_timedlock function returns thrd_success on success, or
-* thrd_timedout if the time specified was reached without acquiring the
-* requested resource, or thrd_error if the request could not be honored.
-*/
-int mtx_timedlock(mtx_t *mtx, const struct timespec *ts);
-
-/** Try to lock the given mutex.
-* The specified mutex shall support either test and return or timeout. If the
-* mutex is already locked, the function returns without blocking.
-* @param mtx A mutex object.
-* @return @ref thrd_success on success, or @ref thrd_busy if the resource
-* requested is already in use, or @ref thrd_error if the request could not be
-* honored.
-*/
-int mtx_trylock(mtx_t *mtx);
-
-/** Unlock the given mutex.
-* @param mtx A mutex object.
-* @return @ref thrd_success on success, or @ref thrd_error if the request could
-* not be honored.
-*/
-int mtx_unlock(mtx_t *mtx);
-
-/* Condition variable */
-#if defined(_TTHREAD_WIN32_)
-typedef struct {
- HANDLE mEvents[2]; /* Signal and broadcast event HANDLEs. */
- unsigned int mWaitersCount; /* Count of the number of waiters. */
- CRITICAL_SECTION mWaitersCountLock; /* Serialize access to mWaitersCount. */
-} cnd_t;
-#else
-typedef pthread_cond_t cnd_t;
-#endif
-
-/** Create a condition variable object.
-* @param cond A condition variable object.
-* @return @ref thrd_success on success, or @ref thrd_error if the request could
-* not be honored.
-*/
-int cnd_init(cnd_t *cond);
-
-/** Release any resources used by the given condition variable.
-* @param cond A condition variable object.
-*/
-void cnd_destroy(cnd_t *cond);
-
-/** Signal a condition variable.
-* Unblocks one of the threads that are blocked on the given condition variable
-* at the time of the call. If no threads are blocked on the condition variable
-* at the time of the call, the function does nothing and return success.
-* @param cond A condition variable object.
-* @return @ref thrd_success on success, or @ref thrd_error if the request could
-* not be honored.
-*/
-int cnd_signal(cnd_t *cond);
-
-/** Broadcast a condition variable.
-* Unblocks all of the threads that are blocked on the given condition variable
-* at the time of the call. If no threads are blocked on the condition variable
-* at the time of the call, the function does nothing and return success.
-* @param cond A condition variable object.
-* @return @ref thrd_success on success, or @ref thrd_error if the request could
-* not be honored.
-*/
-int cnd_broadcast(cnd_t *cond);
-
-/** Wait for a condition variable to become signaled.
-* The function atomically unlocks the given mutex and endeavors to block until
-* the given condition variable is signaled by a call to cnd_signal or to
-* cnd_broadcast. When the calling thread becomes unblocked it locks the mutex
-* before it returns.
-* @param cond A condition variable object.
-* @param mtx A mutex object.
-* @return @ref thrd_success on success, or @ref thrd_error if the request could
-* not be honored.
-*/
-int cnd_wait(cnd_t *cond, mtx_t *mtx);
-
-/** Wait for a condition variable to become signaled.
-* The function atomically unlocks the given mutex and endeavors to block until
-* the given condition variable is signaled by a call to cnd_signal or to
-* cnd_broadcast, or until after the specified time. When the calling thread
-* becomes unblocked it locks the mutex before it returns.
-* @param cond A condition variable object.
-* @param mtx A mutex object.
-* @param xt A point in time at which the request will time out (absolute time).
-* @return @ref thrd_success upon success, or @ref thrd_timeout if the time
-* specified in the call was reached without acquiring the requested resource, or
-* @ref thrd_error if the request could not be honored.
-*/
-int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts);
-
-/* Thread */
-#if defined(_TTHREAD_WIN32_)
-typedef HANDLE thrd_t;
-#else
-typedef pthread_t thrd_t;
-#endif
-
-/** Thread start function.
-* Any thread that is started with the @ref thrd_create() function must be
-* started through a function of this type.
-* @param arg The thread argument (the @c arg argument of the corresponding
-* @ref thrd_create() call).
-* @return The thread return value, which can be obtained by another thread
-* by using the @ref thrd_join() function.
-*/
-typedef int (*thrd_start_t)(void *arg);
-
-/** Create a new thread.
-* @param thr Identifier of the newly created thread.
-* @param func A function pointer to the function that will be executed in
-* the new thread.
-* @param arg An argument to the thread function.
-* @return @ref thrd_success on success, or @ref thrd_nomem if no memory could
-* be allocated for the thread requested, or @ref thrd_error if the request
-* could not be honored.
-* @note A thread’s identifier may be reused for a different thread once the
-* original thread has exited and either been detached or joined to another
-* thread.
-*/
-int thrd_create(thrd_t *thr, thrd_start_t func, void *arg);
-
-/** Identify the calling thread.
-* @return The identifier of the calling thread.
-*/
-thrd_t thrd_current(void);
-
-/** Dispose of any resources allocated to the thread when that thread exits.
- * @return thrd_success, or thrd_error on error
-*/
-int thrd_detach(thrd_t thr);
-
-/** Compare two thread identifiers.
-* The function determines if two thread identifiers refer to the same thread.
-* @return Zero if the two thread identifiers refer to different threads.
-* Otherwise a nonzero value is returned.
-*/
-int thrd_equal(thrd_t thr0, thrd_t thr1);
-
-/** Terminate execution of the calling thread.
-* @param res Result code of the calling thread.
-*/
-TTHREAD_NORETURN void thrd_exit(int res);
-
-/** Wait for a thread to terminate.
-* The function joins the given thread with the current thread by blocking
-* until the other thread has terminated.
-* @param thr The thread to join with.
-* @param res If this pointer is not NULL, the function will store the result
-* code of the given thread in the integer pointed to by @c res.
-* @return @ref thrd_success on success, or @ref thrd_error if the request could
-* not be honored.
-*/
-int thrd_join(thrd_t thr, int *res);
-
-/** Put the calling thread to sleep.
-* Suspend execution of the calling thread.
-* @param duration Interval to sleep for
-* @param remaining If non-NULL, this parameter will hold the remaining
-* time until time_point upon return. This will
-* typically be zero, but if the thread was woken up
-* by a signal that is not ignored before duration was
-* reached @c remaining will hold a positive time.
-* @return 0 (zero) on successful sleep, -1 if an interrupt occurred,
-* or a negative value if the operation fails.
-*/
-int thrd_sleep(const struct timespec *duration, struct timespec *remaining);
-
-/** Yield execution to another thread.
-* Permit other threads to run, even if the current thread would ordinarily
-* continue to run.
-*/
-void thrd_yield(void);
-
-/* Thread local storage */
-#if defined(_TTHREAD_WIN32_)
-typedef DWORD tss_t;
-#else
-typedef pthread_key_t tss_t;
-#endif
-
-/** Destructor function for a thread-specific storage.
-* @param val The value of the destructed thread-specific storage.
-*/
-typedef void (*tss_dtor_t)(void *val);
-
-/** Create a thread-specific storage.
-* @param key The unique key identifier that will be set if the function is
-* successful.
-* @param dtor Destructor function. This can be NULL.
-* @return @ref thrd_success on success, or @ref thrd_error if the request could
-* not be honored.
-* @note On Windows, the @c dtor will definitely be called when
-* appropriate for threads created with @ref thrd_create. It will be
-* called for other threads in most cases, the possible exception being
-* for DLLs loaded with LoadLibraryEx. In order to be certain, you
-* should use @ref thrd_create whenever possible.
-*/
-int tss_create(tss_t *key, tss_dtor_t dtor);
-
-/** Delete a thread-specific storage.
-* The function releases any resources used by the given thread-specific
-* storage.
-* @param key The key that shall be deleted.
-*/
-void tss_delete(tss_t key);
-
-/** Get the value for a thread-specific storage.
-* @param key The thread-specific storage identifier.
-* @return The value for the current thread held in the given thread-specific
-* storage.
-*/
-void *tss_get(tss_t key);
-
-/** Set the value for a thread-specific storage.
-* @param key The thread-specific storage identifier.
-* @param val The value of the thread-specific storage to set for the current
-* thread.
-* @return @ref thrd_success on success, or @ref thrd_error if the request could
-* not be honored.
-*/
-int tss_set(tss_t key, void *val);
-
-#if defined(_TTHREAD_WIN32_)
- typedef struct {
- LONG volatile status;
- CRITICAL_SECTION lock;
- } once_flag;
- #define ONCE_FLAG_INIT {0,}
-#else
- #define once_flag pthread_once_t
- #define ONCE_FLAG_INIT PTHREAD_ONCE_INIT
-#endif
-
-/** Invoke a callback exactly once
- * @param flag Flag used to ensure the callback is invoked exactly
- * once.
- * @param func Callback to invoke.
- */
-#if defined(_TTHREAD_WIN32_)
- void call_once(once_flag *flag, void (*func)(void));
-#else
- #define call_once(flag,func) pthread_once(flag,func)
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _TINYTHREAD_H_ */
diff --git a/src/threads.c b/src/threads.c
deleted file mode 100644
index 92b966b1..00000000
--- a/src/threads.c
+++ /dev/null
@@ -1,932 +0,0 @@
-/* -*- mode: c; tab-width: 2; indent-tabs-mode: nil; -*-
-Copyright (c) 2012 Marcus Geelnard
-Copyright (c) 2013-2016 Evan Nemerson
-
-This software is provided 'as-is', without any express or implied
-warranty. In no event will the authors be held liable for any damages
-arising from the use of this software.
-
-Permission is granted to anyone to use this software for any purpose,
-including commercial applications, and to alter it and redistribute it
-freely, subject to the following restrictions:
-
- 1. The origin of this software must not be misrepresented; you must not
- claim that you wrote the original software. If you use this software
- in a product, an acknowledgment in the product documentation would be
- appreciated but is not required.
-
- 2. Altered source versions must be plainly marked as such, and must not be
- misrepresented as being the original software.
-
- 3. This notice may not be removed or altered from any source
- distribution.
-*/
-
-#include "threads.h"
-#include <stdlib.h>
-#include <stdint.h>
-
-/* Platform specific includes */
-#if defined(_TTHREAD_POSIX_)
- #include <signal.h>
- #include <sched.h>
- #include <unistd.h>
- #include <sys/time.h>
- #include <errno.h>
-#elif defined(_TTHREAD_WIN32_)
- #include <process.h>
- #include <sys/timeb.h>
-#endif
-
-/* Standard, good-to-have defines */
-#ifndef NULL
- #define NULL (void*)0
-#endif
-#ifndef TRUE
- #define TRUE 1
-#endif
-#ifndef FALSE
- #define FALSE 0
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-
-int mtx_init(mtx_t *mtx, int type)
-{
-#if defined(_TTHREAD_WIN32_)
- mtx->mAlreadyLocked = FALSE;
- mtx->mRecursive = type & mtx_recursive;
- mtx->mTimed = type & mtx_timed;
- if (!mtx->mTimed)
- {
- InitializeCriticalSection(&(mtx->mHandle.cs));
- }
- else
- {
- mtx->mHandle.mut = CreateMutex(NULL, FALSE, NULL);
- if (mtx->mHandle.mut == NULL)
- {
- return thrd_error;
- }
- }
- return thrd_success;
-#else
- int ret;
- pthread_mutexattr_t attr;
- pthread_mutexattr_init(&attr);
- if (type & mtx_recursive)
- {
- pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
- }
- ret = pthread_mutex_init(mtx, &attr);
- pthread_mutexattr_destroy(&attr);
- return ret == 0 ? thrd_success : thrd_error;
-#endif
-}
-
-void mtx_destroy(mtx_t *mtx)
-{
-#if defined(_TTHREAD_WIN32_)
- if (!mtx->mTimed)
- {
- DeleteCriticalSection(&(mtx->mHandle.cs));
- }
- else
- {
- CloseHandle(mtx->mHandle.mut);
- }
-#else
- pthread_mutex_destroy(mtx);
-#endif
-}
-
-int mtx_lock(mtx_t *mtx)
-{
-#if defined(_TTHREAD_WIN32_)
- if (!mtx->mTimed)
- {
- EnterCriticalSection(&(mtx->mHandle.cs));
- }
- else
- {
- switch (WaitForSingleObject(mtx->mHandle.mut, INFINITE))
- {
- case WAIT_OBJECT_0:
- break;
- case WAIT_ABANDONED:
- default:
- return thrd_error;
- }
- }
-
- if (!mtx->mRecursive)
- {
- while(mtx->mAlreadyLocked) Sleep(1); /* Simulate deadlock... */
- mtx->mAlreadyLocked = TRUE;
- }
- return thrd_success;
-#else
- return pthread_mutex_lock(mtx) == 0 ? thrd_success : thrd_error;
-#endif
-}
-
-int mtx_timedlock(mtx_t *mtx, const struct timespec *ts)
-{
-#if defined(_TTHREAD_WIN32_)
- struct timespec current_ts;
- DWORD timeoutMs;
-
- if (!mtx->mTimed)
- {
- return thrd_error;
- }
-
- timespec_get(&current_ts, TIME_UTC);
-
- if ((current_ts.tv_sec > ts->tv_sec) || ((current_ts.tv_sec == ts->tv_sec) && (current_ts.tv_nsec >= ts->tv_nsec)))
- {
- timeoutMs = 0;
- }
- else
- {
- timeoutMs = (DWORD)(ts->tv_sec - current_ts.tv_sec) * 1000;
- timeoutMs += (ts->tv_nsec - current_ts.tv_nsec) / 1000000;
- timeoutMs += 1;
- }
-
- /* TODO: the timeout for WaitForSingleObject doesn't include time
- while the computer is asleep. */
- switch (WaitForSingleObject(mtx->mHandle.mut, timeoutMs))
- {
- case WAIT_OBJECT_0:
- break;
- case WAIT_TIMEOUT:
- return thrd_timedout;
- case WAIT_ABANDONED:
- default:
- return thrd_error;
- }
-
- if (!mtx->mRecursive)
- {
- while(mtx->mAlreadyLocked) Sleep(1); /* Simulate deadlock... */
- mtx->mAlreadyLocked = TRUE;
- }
-
- return thrd_success;
-#elif defined(_POSIX_TIMEOUTS) && (_POSIX_TIMEOUTS >= 200112L) && defined(_POSIX_THREADS) && (_POSIX_THREADS >= 200112L)
- switch (pthread_mutex_timedlock(mtx, ts)) {
- case 0:
- return thrd_success;
- case ETIMEDOUT:
- return thrd_timedout;
- default:
- return thrd_error;
- }
-#else
- int rc;
- struct timespec cur, dur;
-
- /* Try to acquire the lock and, if we fail, sleep for 5ms. */
- while ((rc = pthread_mutex_trylock (mtx)) == EBUSY) {
- timespec_get(&cur, TIME_UTC);
-
- if ((cur.tv_sec > ts->tv_sec) || ((cur.tv_sec == ts->tv_sec) && (cur.tv_nsec >= ts->tv_nsec)))
- {
- break;
- }
-
- dur.tv_sec = ts->tv_sec - cur.tv_sec;
- dur.tv_nsec = ts->tv_nsec - cur.tv_nsec;
- if (dur.tv_nsec < 0)
- {
- dur.tv_sec--;
- dur.tv_nsec += 1000000000;
- }
-
- if ((dur.tv_sec != 0) || (dur.tv_nsec > 5000000))
- {
- dur.tv_sec = 0;
- dur.tv_nsec = 5000000;
- }
-
- nanosleep(&dur, NULL);
- }
-
- switch (rc) {
- case 0:
- return thrd_success;
- case ETIMEDOUT:
- case EBUSY:
- return thrd_timedout;
- default:
- return thrd_error;
- }
-#endif
-}
-
-int mtx_trylock(mtx_t *mtx)
-{
-#if defined(_TTHREAD_WIN32_)
- int ret;
-
- if (!mtx->mTimed)
- {
- ret = TryEnterCriticalSection(&(mtx->mHandle.cs)) ? thrd_success : thrd_busy;
- }
- else
- {
- ret = (WaitForSingleObject(mtx->mHandle.mut, 0) == WAIT_OBJECT_0) ? thrd_success : thrd_busy;
- }
-
- if ((!mtx->mRecursive) && (ret == thrd_success))
- {
- if (mtx->mAlreadyLocked)
- {
- LeaveCriticalSection(&(mtx->mHandle.cs));
- ret = thrd_busy;
- }
- else
- {
- mtx->mAlreadyLocked = TRUE;
- }
- }
- return ret;
-#else
- return (pthread_mutex_trylock(mtx) == 0) ? thrd_success : thrd_busy;
-#endif
-}
-
-int mtx_unlock(mtx_t *mtx)
-{
-#if defined(_TTHREAD_WIN32_)
- mtx->mAlreadyLocked = FALSE;
- if (!mtx->mTimed)
- {
- LeaveCriticalSection(&(mtx->mHandle.cs));
- }
- else
- {
- if (!ReleaseMutex(mtx->mHandle.mut))
- {
- return thrd_error;
- }
- }
- return thrd_success;
-#else
- return pthread_mutex_unlock(mtx) == 0 ? thrd_success : thrd_error;;
-#endif
-}
-
-#if defined(_TTHREAD_WIN32_)
-#define _CONDITION_EVENT_ONE 0
-#define _CONDITION_EVENT_ALL 1
-#endif
-
-int cnd_init(cnd_t *cond)
-{
-#if defined(_TTHREAD_WIN32_)
- cond->mWaitersCount = 0;
-
- /* Init critical section */
- InitializeCriticalSection(&cond->mWaitersCountLock);
-
- /* Init events */
- cond->mEvents[_CONDITION_EVENT_ONE] = CreateEvent(NULL, FALSE, FALSE, NULL);
- if (cond->mEvents[_CONDITION_EVENT_ONE] == NULL)
- {
- cond->mEvents[_CONDITION_EVENT_ALL] = NULL;
- return thrd_error;
- }
- cond->mEvents[_CONDITION_EVENT_ALL] = CreateEvent(NULL, TRUE, FALSE, NULL);
- if (cond->mEvents[_CONDITION_EVENT_ALL] == NULL)
- {
- CloseHandle(cond->mEvents[_CONDITION_EVENT_ONE]);
- cond->mEvents[_CONDITION_EVENT_ONE] = NULL;
- return thrd_error;
- }
-
- return thrd_success;
-#else
- return pthread_cond_init(cond, NULL) == 0 ? thrd_success : thrd_error;
-#endif
-}
-
-void cnd_destroy(cnd_t *cond)
-{
-#if defined(_TTHREAD_WIN32_)
- if (cond->mEvents[_CONDITION_EVENT_ONE] != NULL)
- {
- CloseHandle(cond->mEvents[_CONDITION_EVENT_ONE]);
- }
- if (cond->mEvents[_CONDITION_EVENT_ALL] != NULL)
- {
- CloseHandle(cond->mEvents[_CONDITION_EVENT_ALL]);
- }
- DeleteCriticalSection(&cond->mWaitersCountLock);
-#else
- pthread_cond_destroy(cond);
-#endif
-}
-
-int cnd_signal(cnd_t *cond)
-{
-#if defined(_TTHREAD_WIN32_)
- int haveWaiters;
-
- /* Are there any waiters? */
- EnterCriticalSection(&cond->mWaitersCountLock);
- haveWaiters = (cond->mWaitersCount > 0);
- LeaveCriticalSection(&cond->mWaitersCountLock);
-
- /* If we have any waiting threads, send them a signal */
- if(haveWaiters)
- {
- if (SetEvent(cond->mEvents[_CONDITION_EVENT_ONE]) == 0)
- {
- return thrd_error;
- }
- }
-
- return thrd_success;
-#else
- return pthread_cond_signal(cond) == 0 ? thrd_success : thrd_error;
-#endif
-}
-
-int cnd_broadcast(cnd_t *cond)
-{
-#if defined(_TTHREAD_WIN32_)
- int haveWaiters;
-
- /* Are there any waiters? */
- EnterCriticalSection(&cond->mWaitersCountLock);
- haveWaiters = (cond->mWaitersCount > 0);
- LeaveCriticalSection(&cond->mWaitersCountLock);
-
- /* If we have any waiting threads, send them a signal */
- if(haveWaiters)
- {
- if (SetEvent(cond->mEvents[_CONDITION_EVENT_ALL]) == 0)
- {
- return thrd_error;
- }
- }
-
- return thrd_success;
-#else
- return pthread_cond_broadcast(cond) == 0 ? thrd_success : thrd_error;
-#endif
-}
-
-#if defined(_TTHREAD_WIN32_)
-static int _cnd_timedwait_win32(cnd_t *cond, mtx_t *mtx, DWORD timeout)
-{
- DWORD result;
- int lastWaiter;
-
- /* Increment number of waiters */
- EnterCriticalSection(&cond->mWaitersCountLock);
- ++ cond->mWaitersCount;
- LeaveCriticalSection(&cond->mWaitersCountLock);
-
- /* Release the mutex while waiting for the condition (will decrease
- the number of waiters when done)... */
- mtx_unlock(mtx);
-
- /* Wait for either event to become signaled due to cnd_signal() or
- cnd_broadcast() being called */
- result = WaitForMultipleObjects(2, cond->mEvents, FALSE, timeout);
- if (result == WAIT_TIMEOUT)
- {
- /* The mutex is locked again before the function returns, even if an error occurred */
- mtx_lock(mtx);
- return thrd_timedout;
- }
- else if (result == WAIT_FAILED)
- {
- /* The mutex is locked again before the function returns, even if an error occurred */
- mtx_lock(mtx);
- return thrd_error;
- }
-
- /* Check if we are the last waiter */
- EnterCriticalSection(&cond->mWaitersCountLock);
- -- cond->mWaitersCount;
- lastWaiter = (result == (WAIT_OBJECT_0 + _CONDITION_EVENT_ALL)) &&
- (cond->mWaitersCount == 0);
- LeaveCriticalSection(&cond->mWaitersCountLock);
-
- /* If we are the last waiter to be notified to stop waiting, reset the event */
- if (lastWaiter)
- {
- if (ResetEvent(cond->mEvents[_CONDITION_EVENT_ALL]) == 0)
- {
- /* The mutex is locked again before the function returns, even if an error occurred */
- mtx_lock(mtx);
- return thrd_error;
- }
- }
-
- /* Re-acquire the mutex */
- mtx_lock(mtx);
-
- return thrd_success;
-}
-#endif
-
-int cnd_wait(cnd_t *cond, mtx_t *mtx)
-{
-#if defined(_TTHREAD_WIN32_)
- return _cnd_timedwait_win32(cond, mtx, INFINITE);
-#else
- return pthread_cond_wait(cond, mtx) == 0 ? thrd_success : thrd_error;
-#endif
-}
-
-int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts)
-{
-#if defined(_TTHREAD_WIN32_)
- struct timespec now;
- if (timespec_get(&now, TIME_UTC) == TIME_UTC)
- {
- unsigned long long nowInMilliseconds = now.tv_sec * 1000 + now.tv_nsec / 1000000;
- unsigned long long tsInMilliseconds = ts->tv_sec * 1000 + ts->tv_nsec / 1000000;
- DWORD delta = (tsInMilliseconds > nowInMilliseconds) ?
- (DWORD)(tsInMilliseconds - nowInMilliseconds) : 0;
- return _cnd_timedwait_win32(cond, mtx, delta);
- }
- else
- return thrd_error;
-#else
- int ret;
- ret = pthread_cond_timedwait(cond, mtx, ts);
- if (ret == ETIMEDOUT)
- {
- return thrd_timedout;
- }
- return ret == 0 ? thrd_success : thrd_error;
-#endif
-}
-
-#if defined(_TTHREAD_WIN32_)
-struct TinyCThreadTSSData {
- void* value;
- tss_t key;
- struct TinyCThreadTSSData* next;
-};
-
-static tss_dtor_t _tinycthread_tss_dtors[1088] = { NULL, };
-
-static _Thread_local struct TinyCThreadTSSData* _tinycthread_tss_head = NULL;
-static _Thread_local struct TinyCThreadTSSData* _tinycthread_tss_tail = NULL;
-
-static void _tinycthread_tss_cleanup (void);
-
-static void _tinycthread_tss_cleanup (void) {
- struct TinyCThreadTSSData* data;
- int iteration;
- unsigned int again = 1;
- void* value;
-
- for (iteration = 0 ; iteration < TSS_DTOR_ITERATIONS && again > 0 ; iteration++)
- {
- again = 0;
- for (data = _tinycthread_tss_head ; data != NULL ; data = data->next)
- {
- if (data->value != NULL)
- {
- value = data->value;
- data->value = NULL;
-
- if (_tinycthread_tss_dtors[data->key] != NULL)
- {
- again = 1;
- _tinycthread_tss_dtors[data->key](value);
- }
- }
- }
- }
-
- while (_tinycthread_tss_head != NULL) {
- data = _tinycthread_tss_head->next;
- free (_tinycthread_tss_head);
- _tinycthread_tss_head = data;
- }
- _tinycthread_tss_head = NULL;
- _tinycthread_tss_tail = NULL;
-}
-
-static void NTAPI _tinycthread_tss_callback(PVOID h, DWORD dwReason, PVOID pv)
-{
- (void)h;
- (void)pv;
-
- if (_tinycthread_tss_head != NULL && (dwReason == DLL_THREAD_DETACH || dwReason == DLL_PROCESS_DETACH))
- {
- _tinycthread_tss_cleanup();
- }
-}
-
-#if defined(_MSC_VER)
- #ifdef _M_X64
- #pragma const_seg(".CRT$XLB")
- #else
- #pragma data_seg(".CRT$XLB")
- #endif
- PIMAGE_TLS_CALLBACK p_thread_callback = _tinycthread_tss_callback;
- #ifdef _M_X64
- #pragma data_seg()
- #else
- #pragma const_seg()
- #endif
-#else
- PIMAGE_TLS_CALLBACK p_thread_callback __attribute__((section(".CRT$XLB"))) = _tinycthread_tss_callback;
-#endif
-
-#endif /* defined(_TTHREAD_WIN32_) */
-
-/** Information to pass to the new thread (what to run). */
-typedef struct {
- thrd_start_t mFunction; /**< Pointer to the function to be executed. */
- void * mArg; /**< Function argument for the thread function. */
-} _thread_start_info;
-
-/* Thread wrapper function. */
-#if defined(_TTHREAD_WIN32_)
-static DWORD WINAPI _thrd_wrapper_function(LPVOID aArg)
-#elif defined(_TTHREAD_POSIX_)
-static void * _thrd_wrapper_function(void * aArg)
-#endif
-{
- thrd_start_t fun;
- void *arg;
- int res;
-
- /* Get thread startup information */
- _thread_start_info *ti = (_thread_start_info *) aArg;
- fun = ti->mFunction;
- arg = ti->mArg;
-
- /* The thread is responsible for freeing the startup information */
- free((void *)ti);
-
- /* Call the actual client thread function */
- res = fun(arg);
-
-#if defined(_TTHREAD_WIN32_)
- if (_tinycthread_tss_head != NULL)
- {
- _tinycthread_tss_cleanup();
- }
-
- return (DWORD)res;
-#else
- return (void*)(intptr_t)res;
-#endif
-}
-
-int thrd_create(thrd_t *thr, thrd_start_t func, void *arg)
-{
- /* Fill out the thread startup information (passed to the thread wrapper,
- which will eventually free it) */
- _thread_start_info* ti = (_thread_start_info*)malloc(sizeof(_thread_start_info));
- if (ti == NULL)
- {
- return thrd_nomem;
- }
- ti->mFunction = func;
- ti->mArg = arg;
-
- /* Create the thread */
-#if defined(_TTHREAD_WIN32_)
- *thr = CreateThread(NULL, 0, _thrd_wrapper_function, (LPVOID) ti, 0, NULL);
-#elif defined(_TTHREAD_POSIX_)
- if(pthread_create(thr, NULL, _thrd_wrapper_function, (void *)ti) != 0)
- {
- *thr = 0;
- }
-#endif
-
- /* Did we fail to create the thread? */
- if(!*thr)
- {
- free(ti);
- return thrd_error;
- }
-
- return thrd_success;
-}
-
-thrd_t thrd_current(void)
-{
-#if defined(_TTHREAD_WIN32_)
- return GetCurrentThread();
-#else
- return pthread_self();
-#endif
-}
-
-int thrd_detach(thrd_t thr)
-{
-#if defined(_TTHREAD_WIN32_)
- /* https://stackoverflow.com/questions/12744324/how-to-detach-a-thread-on-windows-c#answer-12746081 */
- return CloseHandle(thr) != 0 ? thrd_success : thrd_error;
-#else
- return pthread_detach(thr) == 0 ? thrd_success : thrd_error;
-#endif
-}
-
-int thrd_equal(thrd_t thr0, thrd_t thr1)
-{
-#if defined(_TTHREAD_WIN32_)
- return GetThreadId(thr0) == GetThreadId(thr1);
-#else
- return pthread_equal(thr0, thr1);
-#endif
-}
-
-void thrd_exit(int res)
-{
-#if defined(_TTHREAD_WIN32_)
- if (_tinycthread_tss_head != NULL)
- {
- _tinycthread_tss_cleanup();
- }
-
- ExitThread((DWORD)res);
-#else
- pthread_exit((void*)(intptr_t)res);
-#endif
-}
-
-int thrd_join(thrd_t thr, int *res)
-{
-#if defined(_TTHREAD_WIN32_)
- DWORD dwRes;
-
- if (WaitForSingleObject(thr, INFINITE) == WAIT_FAILED)
- {
- return thrd_error;
- }
- if (res != NULL)
- {
- if (GetExitCodeThread(thr, &dwRes) != 0)
- {
- *res = (int) dwRes;
- }
- else
- {
- return thrd_error;
- }
- }
- CloseHandle(thr);
-#elif defined(_TTHREAD_POSIX_)
- void *pres;
- if (pthread_join(thr, &pres) != 0)
- {
- return thrd_error;
- }
- if (res != NULL)
- {
- *res = (int)(intptr_t)pres;
- }
-#endif
- return thrd_success;
-}
-
-int thrd_sleep(const struct timespec *duration, struct timespec *remaining)
-{
-#if !defined(_TTHREAD_WIN32_)
- int res = nanosleep(duration, remaining);
- if (res == 0) {
- return 0;
- } else if (errno == EINTR) {
- return -1;
- } else {
- return -2;
- }
-#else
- struct timespec start;
- DWORD t;
-
- timespec_get(&start, TIME_UTC);
-
- t = SleepEx((DWORD)(duration->tv_sec * 1000 +
- duration->tv_nsec / 1000000 +
- (((duration->tv_nsec % 1000000) == 0) ? 0 : 1)),
- TRUE);
-
- if (t == 0) {
- return 0;
- } else {
- if (remaining != NULL) {
- timespec_get(remaining, TIME_UTC);
- remaining->tv_sec -= start.tv_sec;
- remaining->tv_nsec -= start.tv_nsec;
- if (remaining->tv_nsec < 0)
- {
- remaining->tv_nsec += 1000000000;
- remaining->tv_sec -= 1;
- }
- }
-
- return (t == WAIT_IO_COMPLETION) ? -1 : -2;
- }
-#endif
-}
-
-void thrd_yield(void)
-{
-#if defined(_TTHREAD_WIN32_)
- Sleep(0);
-#else
- sched_yield();
-#endif
-}
-
-int tss_create(tss_t *key, tss_dtor_t dtor)
-{
-#if defined(_TTHREAD_WIN32_)
- *key = TlsAlloc();
- if (*key == TLS_OUT_OF_INDEXES)
- {
- return thrd_error;
- }
- _tinycthread_tss_dtors[*key] = dtor;
-#else
- if (pthread_key_create(key, dtor) != 0)
- {
- return thrd_error;
- }
-#endif
- return thrd_success;
-}
-
-void tss_delete(tss_t key)
-{
-#if defined(_TTHREAD_WIN32_)
- struct TinyCThreadTSSData* data = (struct TinyCThreadTSSData*) TlsGetValue (key);
- struct TinyCThreadTSSData* prev = NULL;
- if (data != NULL)
- {
- if (data == _tinycthread_tss_head)
- {
- _tinycthread_tss_head = data->next;
- }
- else
- {
- prev = _tinycthread_tss_head;
- if (prev != NULL)
- {
- while (prev->next != data)
- {
- prev = prev->next;
- }
- }
- }
-
- if (data == _tinycthread_tss_tail)
- {
- _tinycthread_tss_tail = prev;
- }
-
- free (data);
- }
- _tinycthread_tss_dtors[key] = NULL;
- TlsFree(key);
-#else
- pthread_key_delete(key);
-#endif
-}
-
-void *tss_get(tss_t key)
-{
-#if defined(_TTHREAD_WIN32_)
- struct TinyCThreadTSSData* data = (struct TinyCThreadTSSData*)TlsGetValue(key);
- if (data == NULL)
- {
- return NULL;
- }
- return data->value;
-#else
- return pthread_getspecific(key);
-#endif
-}
-
-int tss_set(tss_t key, void *val)
-{
-#if defined(_TTHREAD_WIN32_)
- struct TinyCThreadTSSData* data = (struct TinyCThreadTSSData*)TlsGetValue(key);
- if (data == NULL)
- {
- data = (struct TinyCThreadTSSData*)malloc(sizeof(struct TinyCThreadTSSData));
- if (data == NULL)
- {
- return thrd_error;
- }
-
- data->value = NULL;
- data->key = key;
- data->next = NULL;
-
- if (_tinycthread_tss_tail != NULL)
- {
- _tinycthread_tss_tail->next = data;
- }
- else
- {
- _tinycthread_tss_tail = data;
- }
-
- if (_tinycthread_tss_head == NULL)
- {
- _tinycthread_tss_head = data;
- }
-
- if (!TlsSetValue(key, data))
- {
- free (data);
- return thrd_error;
- }
- }
- data->value = val;
-#else
- if (pthread_setspecific(key, val) != 0)
- {
- return thrd_error;
- }
-#endif
- return thrd_success;
-}
-
-#if defined(_TTHREAD_EMULATE_TIMESPEC_GET_)
-int _tthread_timespec_get(struct timespec *ts, int base)
-{
-#if defined(_TTHREAD_WIN32_)
- struct _timeb tb;
-#elif !defined(CLOCK_REALTIME)
- struct timeval tv;
-#endif
-
- if (base != TIME_UTC)
- {
- return 0;
- }
-
-#if defined(_TTHREAD_WIN32_)
- _ftime_s(&tb);
- ts->tv_sec = (time_t)tb.time;
- ts->tv_nsec = 1000000L * (long)tb.millitm;
-#elif defined(CLOCK_REALTIME)
- base = (clock_gettime(CLOCK_REALTIME, ts) == 0) ? base : 0;
-#else
- gettimeofday(&tv, NULL);
- ts->tv_sec = (time_t)tv.tv_sec;
- ts->tv_nsec = 1000L * (long)tv.tv_usec;
-#endif
-
- return base;
-}
-#endif /* _TTHREAD_EMULATE_TIMESPEC_GET_ */
-
-#if defined(_TTHREAD_WIN32_)
-void call_once(once_flag *flag, void (*func)(void))
-{
- /* The idea here is that we use a spin lock (via the
- InterlockedCompareExchange function) to restrict access to the
- critical section until we have initialized it, then we use the
- critical section to block until the callback has completed
- execution. */
- while (flag->status < 3)
- {
- switch (flag->status)
- {
- case 0:
- if (InterlockedCompareExchange (&(flag->status), 1, 0) == 0) {
- InitializeCriticalSection(&(flag->lock));
- EnterCriticalSection(&(flag->lock));
- flag->status = 2;
- func();
- flag->status = 3;
- LeaveCriticalSection(&(flag->lock));
- return;
- }
- break;
- case 1:
- break;
- case 2:
- EnterCriticalSection(&(flag->lock));
- LeaveCriticalSection(&(flag->lock));
- break;
- }
- }
-}
-#endif /* defined(_TTHREAD_WIN32_) */
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/src/utf8code.c b/src/utf8code.c
index 543070c3..0f74ffb6 100644
--- a/src/utf8code.c
+++ b/src/utf8code.c
@@ -3,28 +3,21 @@
#include <stc/cstr.h>
#include "utf8tabs.inc"
-// https://news.ycombinator.com/item?id=15423674
-// https://gist.github.com/s4y/344a355f8c1f99c6a4cb2347ec4323cc
-
-void utf8_decode(utf8_decode_t *d, const uint8_t b)
-{
- switch (d->state) {
- case UTF8_OK:
- if (b < 0x80) d->codep = b, d->size = 1;
- else if (b < 0xC2) d->state = UTF8_ERROR, d->size = 0;
- else if (b < 0xE0) d->state = 1, d->codep = b & 0x1F, d->size = 2;
- else if (b < 0xF0) d->state = 2, d->codep = b & 0x0F, d->size = 3;
- else if (b < 0xF5) d->state = 3, d->codep = b & 0x07, d->size = 4;
- else d->state = UTF8_ERROR, d->size = 0;
- break;
- case 1: case 2: case 3:
- if ((b & 0xC0) == 0x80) {
- d->state -= 1;
- d->codep = (d->codep << 6) | (b & 0x3F);
- } else
- d->state = UTF8_ERROR, d->size = 0;
- }
-}
+const uint8_t utf8_dtab[] = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 8,8,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,
+ 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8,
+ 0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12,
+ 12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12,
+ 12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12,
+ 12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12,
+ 12,36,12,12,12,12,12,12,12,12,12,12,
+};
unsigned utf8_encode(char *out, uint32_t c)
{
@@ -52,27 +45,18 @@ unsigned utf8_encode(char *out, uint32_t c)
return 0;
}
-void utf8_peek(utf8_decode_t* d, const char *s) {
- utf8_decode(d, (uint8_t)*s++);
- switch (d->size) {
- case 4: utf8_decode(d, (uint8_t)*s++);
- case 3: utf8_decode(d, (uint8_t)*s++);
- case 2: utf8_decode(d, (uint8_t)*s++);
- }
-}
-
bool utf8_valid(const char* s) {
- utf8_decode_t d = {UTF8_OK};
+ utf8_decode_t d = {.state=0};
while (*s)
utf8_decode(&d, (uint8_t)*s++);
- return d.state == UTF8_OK;
+ return d.state == 0;
}
bool utf8_valid_n(const char* s, size_t n) {
- utf8_decode_t d = {UTF8_OK};
+ utf8_decode_t d = {.state=0};
while ((n-- != 0) & (*s != 0))
utf8_decode(&d, (uint8_t)*s++);
- return d.state == UTF8_OK;
+ return d.state == 0;
}
uint32_t utf8_tolower(uint32_t c) {
@@ -101,10 +85,10 @@ uint32_t utf8_toupper(uint32_t c) {
}
/*
int utf8_icmp(const char* s1, const char* s2) {
- utf8_decode_t d1 = {UTF8_OK}, d2 = {UTF8_OK};
- for (;; s1 += d1.size, s2 += d2.size) {
- utf8_peek(&d1, s1);
- utf8_peek(&d2, s2);
+ utf8_decode_t d1 = {.state=0}, d2 = {.state=0};
+ for (;;) {
+ do { utf8_decode(&d1, (uint8_t)s1[j1++]); } while (d1.state);
+ do { utf8_decode(&d2, (uint8_t)s2[j2++]); } while (d2.state);
int c = utf8_tolower(d1.codep) - utf8_tolower(d2.codep);
if (c || !*s2)
return c;
@@ -113,14 +97,14 @@ int utf8_icmp(const char* s1, const char* s2) {
*/
int utf8_icmp_n(size_t u8max, const char* s1, const size_t n1,
const char* s2, const size_t n2) {
- utf8_decode_t d1 = {UTF8_OK}, d2 = {UTF8_OK};
+ utf8_decode_t d1 = {.state=0}, d2 = {.state=0};
size_t j1 = 0, j2 = 0;
- for (; u8max-- && ((j1 < n1) & (j2 < n2)); j1 += d1.size, j2 += d2.size) {
- utf8_peek(&d1, s1 + j1);
- utf8_peek(&d2, s2 + j2);
+ while (u8max-- && ((j1 < n1) & (j2 < n2))) {
+ do { utf8_decode(&d1, (uint8_t)s1[j1++]); } while (d1.state);
+ do { utf8_decode(&d2, (uint8_t)s2[j2++]); } while (d2.state);
int c = utf8_tolower(d1.codep) - utf8_tolower(d2.codep);
if (c || !s2[j2])
- return c;
+ return c;
}
return (j2 < n2) - (j1 < n1);
}
@@ -178,15 +162,13 @@ static cstr cstr_casefold(const cstr* self, struct fnfold fold) {
cstr out = cstr_null;
char *buf = cstr_reserve(&out, sv.size*3/2);
uint32_t cp; size_t sz = 0;
- utf8_decode_t d = {UTF8_OK};
-
- for (; *sv.str; sv.str += d.size) {
- utf8_peek(&d, sv.str);
- switch (d.size) {
- case 1:
- buf[sz++] = (char)fold.conv_asc(*sv.str);
- break;
- default:
+ utf8_decode_t d = {.state=0};
+
+ while (*sv.str) {
+ do { utf8_decode(&d, (uint8_t)*sv.str++); } while (d.state);
+ if (d.codep < 128)
+ buf[sz++] = (char)fold.conv_asc(d.codep);
+ else {
cp = fold.conv_u8(d.codep);
sz += utf8_encode(buf + sz, cp);
}