diff options
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/algorithm_api.md (renamed from docs/ccommon_api.md) | 158 | ||||
| -rw-r--r-- | docs/carc_api.md | 24 | ||||
| -rw-r--r-- | docs/cbox_api.md | 29 | ||||
| -rw-r--r-- | docs/cdeq_api.md | 23 | ||||
| -rw-r--r-- | docs/clist_api.md | 21 | ||||
| -rw-r--r-- | docs/cmap_api.md | 39 | ||||
| -rw-r--r-- | docs/coroutine_api.md | 292 | ||||
| -rw-r--r-- | docs/cpque_api.md | 22 | ||||
| -rw-r--r-- | docs/cqueue_api.md | 16 | ||||
| -rw-r--r-- | docs/crandom_api.md | 47 | ||||
| -rw-r--r-- | docs/cset_api.md | 26 | ||||
| -rw-r--r-- | docs/csmap_api.md | 38 | ||||
| -rw-r--r-- | docs/cspan_api.md | 25 | ||||
| -rw-r--r-- | docs/csset_api.md | 18 | ||||
| -rw-r--r-- | docs/cstack_api.md | 16 | ||||
| -rw-r--r-- | docs/cvec_api.md | 23 | ||||
| -rw-r--r-- | docs/pics/Figure_1.png | bin | 0 -> 58425 bytes | |||
| -rw-r--r-- | docs/pics/coroutine.jpg | bin | 0 -> 81539 bytes |
18 files changed, 499 insertions, 318 deletions
diff --git a/docs/ccommon_api.md b/docs/algorithm_api.md index e053f743..490771b5 100644 --- a/docs/ccommon_api.md +++ b/docs/algorithm_api.md @@ -1,15 +1,17 @@ # STC Algorithms ---- +"No raw loops" - Sean Parent ## Ranged for-loops -### c_foreach, c_foreach_rv, c_forpair +### c_foreach, c_forpair +```c +#include <stc/ccommon.h> +``` | Usage | Description | |:-----------------------------------------|:------------------------------------------| | `c_foreach (it, ctype, container)` | Iteratate all elements | | `c_foreach (it, ctype, it1, it2)` | Iterate the range [it1, it2) | -| `c_foreach_rv (it, ctype, container)` | Iteratate in reverse (cstack, cvec, cdeq) | | `c_forpair (key, val, ctype, container)` | Iterate with structured binding | ```c @@ -54,9 +56,9 @@ c_forlist (i, cmap_ii_raw, { {4, 5}, {6, 7} }) c_forlist (i, const char*, {"Hello", "crazy", "world"}) cstack_str_emplace(&stk, *i.ref); ``` - --- -## Range algorithms + +## Integer range loops ### c_forrange Abstraction for iterating sequence of integers. Like python's **for** *i* **in** *range()* loop. @@ -79,19 +81,19 @@ c_forrange (i, 30, 0, -5) printf(" %lld", i); // 30 25 20 15 10 5 ``` -### crange +### crange: Integer range generator object A number sequence generator type, similar to [boost::irange](https://www.boost.org/doc/libs/release/libs/range/doc/html/range/reference/ranges/irange.html). The **crange_value** type is `long long`. Below *start*, *stop*, and *step* are of type *crange_value*: ```c -crange crange_make(stop); // will generate 0, 1, ..., stop-1 -crange crange_make(start, stop); // will generate start, start+1, ... stop-1 -crange crange_make(start, stop, step); // will generate start, start+step, ... upto-not-including stop +crange crange_init(stop); // will generate 0, 1, ..., stop-1 +crange crange_init(start, stop); // will generate start, start+1, ... stop-1 +crange crange_init(start, stop, step); // will generate start, start+step, ... upto-not-including stop // note that step may be negative. crange_iter crange_begin(crange* self); crange_iter crange_end(crange* self); void crange_next(crange_iter* it); // 1. All primes less than 32: -crange r1 = crange_make(3, 32, 2); +crange r1 = crange_init(3, 32, 2); printf("2"); // first prime c_forfilter (i, crange, r1, isPrime(*i.ref)) printf(" %lld", *i.ref); @@ -99,7 +101,7 @@ c_forfilter (i, crange, r1, isPrime(*i.ref)) // 2. The first 11 primes: printf("2"); -crange range = crange_make(3, INT64_MAX, 2); +crange range = crange_init(3, INT64_MAX, 2); c_forfilter (i, crange, range, isPrime(*i.ref) && c_flt_take(10) @@ -110,12 +112,12 @@ c_forfilter (i, crange, range, ``` ### c_forfilter -Iterate a container/range with chained range filtering. +Iterate a container or a crange with chained `&&` filtering. | Usage | Description | |:----------------------------------------------------|:---------------------------------------| | `c_forfilter (it, ctype, container, filter)` | Filter out items in chain with && | -| `c_forfilter_it (it, ctype, startit, filter)` | Filter from startit position | +| `c_forfilter_it (it, ctype, startit, filter)` | Filter from startit iterator position | | Built-in filter | Description | |:----------------------------------|:-------------------------------------------| @@ -140,7 +142,7 @@ bool isPrime(long long i) { int main(void) { // Get 10 prime numbers starting from 1000. Skip the first 15 primes, // then select every 25th prime (including the initial). - crange R = crange_make(1001, INT64_MAX, 2); // 1001, 1003, ... + crange R = crange_init(1001, INT64_MAX, 2); // 1001, 1003, ... c_forfilter (i, crange, R, isPrime(*i.ref) && @@ -216,7 +218,7 @@ There is a [benchmark/test file here](../misc/benchmarks/various/csort_bench.c). int main(void) { int nums[] = {5, 3, 5, 9, 7, 4, 7, 2, 4, 9, 3, 1, 2, 6, 4}; - intarray_sort_n(nums, c_arraylen(nums)); + ints_sort_n(nums, c_arraylen(nums)); // note: function name derived from i_key c_forrange (i, c_arraylen(arr)) printf(" %d", arr[i]); } ``` @@ -290,132 +292,6 @@ Type c_default_clone(Type val); // return val Type c_default_toraw(const Type* p); // return *p void c_default_drop(Type* p); // does nothing ``` - ---- -## Coroutines -This is a much improved implementation of -[Simon Tatham's coroutines](https://www.chiark.greenend.org.uk/~sgtatham/coroutines.html), -which utilizes the *Duff's device* trick. Tatham's implementation is not typesafe, -and it always allocates the coroutine's internal state dynamically. But crucially, -it does not let the coroutine do self-cleanup on early finish - i.e. it -only frees the initial dynamically allocated memory. - -In this implementation, a coroutine may have any signature, but it should -take a struct pointer as parameter, which must contain the member `int cco_state;` -The struct should normally store all the *local* variables to be used in the -coroutine. It can also store input and output data if desired. - -The coroutine example below generates Pythagorian triples, but the calling loop -skips the triples which are upscaled version of smaller ones, by checking -the gcd() function. It also ensures that it stops when the diagonal size >= 100: - -[ [Run this code](https://godbolt.org/z/coqqrfbd5) ] -```c -#include <stc/calgo.h> -#include <stdio.h> - -struct triples { - int n; // input: max number of triples to be generated. - int a, b, c; - int cco_state; // required member -}; - -int triples(struct triples* i) { // coroutine - cco_routine(i) { - for (i->c = 5; i->n; ++i->c) { - for (i->a = 1; i->a < i->c; ++i->a) { - for (i->b = i->a + 1; i->b < i->c; ++i->b) { - if ((int64_t)i->a*i->a + (int64_t)i->b*i->b == (int64_t)i->c*i->c) { - cco_yield(); - if (--i->n == 0) - cco_return; - } - } - } - } - cco_cleanup: - puts("done"); - } - return 0; -} - -int gcd(int a, int b) { // greatest common denominator - while (b) { - int t = a % b; - a = b; - b = t; - } - return a; -} - -int main(void) -{ - struct triples t = {.n=INT32_MAX}; - int n = 0; - - while (triples(&t)) { - // Skip triples with GCD(a,b) > 1 - if (gcd(t.a, t.b) > 1) - continue; - - // Stop when c >= 100 - if (t.c < 100) - printf("%d: [%d, %d, %d]\n", ++n, t.a, t.b, t.c); - else - cco_stop(&t); // cleanup in next coroutine call/resume - } -} -``` -### Coroutine API -To resume the coroutine from where it was suspended with *cco_yield()*: call the coroutine again. - -**Note**: *cco_yield()* / *cco_await()* may not be called inside a `switch` statement from a -cco_routine scope; Use `if-else-if` constructs instead. - -| | Function / operator | Description | -|:----------|:-------------------------------------|:----------------------------------------| -| | Function / 'keywords': | | -|`cco_result` | Enum `CCO_DONE=0`, `CCO_YIELD`, `CCO_AWAIT` | Recommended return values in coroutines | -| | Function / 'keywords': | | -| | `cco_cleanup:` | Label for cleanup position in coroutine | -| `bool` | `cco_done(co)` | Is coroutine done? | -| | `cco_routine(co) { }` | The coroutine scope | -| | `cco_yield();` | Yield/suspend execution (return CCO_YIELD)| -| | `cco_yield_v();` | Yield/suspend execution (return void) | -| | `cco_yield_v(ret);` | Yield/suspend execution (return ret) | -| | `cco_yield_final();` | Yield final time, enables cleanup-state | -| | `cco_yield_final(ret);` | Yield a final value (e.g. CCO_ERROR) | -| | `cco_await(condition);` | Suspend until condition is true (return CCO_AWAIT)| -| | `cco_await_v(condition);` | Suspend until condition is true (return void) | -| | `cco_await_v(condition, ret);` | Suspend until condition is true (return ret)| -| | `cco_await_on(cocall);` | Await on sub-coroutine to finish (return its ret) | -| | `cco_return;` | Return from coroutine (inside cco_routine) | -| | `cco_closure(Closure, ...);` | Define a coroutine closure struct (optional) | -| | Semaphores: | | -| | `cco_sem` | Semaphore type | -| `cco_sem` | `cco_sem_from(long value)` | Create semaphore | -| | `cco_sem_set(sem, long value)` | Set semaphore value | -| | `cco_sem_await(sem)` | Await for the semaphore count > 0 | -| | `cco_sem_await(sem, ret)` | Await with ret on the semaphore | -| | `cco_sem_release(sem)` | Signal the semaphore (count += 1) | -| | Timers: | | -| | `cco_timer` | Timer type | -| | `cco_timer_await(tm, double sec)` | Await secs for timer to expire (usec prec.)| -| | `cco_timer_await(tm, double sec, ret)`| Await secs for timer with ret value | -| | `cco_timer_start(tm, double sec)` | Start timer for secs duration | -| | `cco_timer_restart(tm)` | Restart timer with same duration | -| `bool` | `cco_timer_expired(tm)` | Return true if timer is expired | -| `double` | `cco_timer_elapsed(tm)` | Return seconds elapsed | -| `double` | `cco_timer_remaining(tm)` | Return seconds remaining | -| | From caller side: | | -| `void` | `cco_stop(co)` | Next call of coroutine finalizes | -| `void` | `cco_reset(co)` | Reset state to initial (for reuse) | -| `void` | `cco_block_on(cocall) { }` | Run blocking until cocall is finished | -| `void` | `cco_block_on(cocall, int *result) { }`| Run blocking until cocall is finished | -| | Time functions: | | -| `double` | `cco_time(void)` | Return secs with usec prec. since Epoch | -| | `cco_sleep(double sec)` | Sleep for seconds (msec or usec prec.) | - --- ## RAII scope macros General ***defer*** mechanics for resource acquisition. These macros allows you to specify the diff --git a/docs/carc_api.md b/docs/carc_api.md index 8b7b67a1..fb79019a 100644 --- a/docs/carc_api.md +++ b/docs/carc_api.md @@ -20,15 +20,21 @@ See similar c++ class [std::shared_ptr](https://en.cppreference.com/w/cpp/memory ## Header file and declaration ```c -#define i_type // full typename of the carc -#define i_key // element type: REQUIRED - -#define i_keyraw // convertion "raw" type - defaults to i_key -#define i_keyto // convertion func i_key* => i_keyraw: REQUIRED IF i_keyraw defined. -#define i_keyfrom // convertion func i_keyraw => i_key - -#define i_opt c_no_atomic // Non-atomic reference counting, like Rust Rc. -#define i_tag // alternative typename: carc_{i_tag}. i_tag defaults to i_key +#define i_key <t> // element type: REQUIRED. Note: i_val* may be specified instead of i_key*. +#define i_type <t> // carc container type name +#define i_cmp <f> // three-way compareison. REQUIRED IF i_key is a non-integral type + // Note that containers of carcs will "inherit" i_cmp + // when using carc in containers with i_valboxed MyArc - ie. the i_type. +#define i_cmp_native // define instead of i_cmp only when i_key is an integral/native-type. +#define i_keydrop <f> // destroy element func - defaults to empty destruct +#define i_keyclone <f> // REQUIRED if i_keydrop is defined, unless 'i_opt c_no_clone' is defined. + +#define i_keyraw <t> // convertion type (lookup): default to {i_key} +#define i_keyto <f> // convertion func i_key* => i_keyraw: REQUIRED IF i_keyraw defined. +#define i_keyfrom <f> // from-raw func. + +#define i_opt c_no_atomic // Non-atomic reference counting, like Rust Rc. +#define i_tag <s> // alternative typename: carc_{i_tag}. i_tag defaults to i_key #include <stc/carc.h> ``` `X` should be replaced by the value of `i_tag` in all of the following documentation. diff --git a/docs/cbox_api.md b/docs/cbox_api.md index b6c76d2f..0e6fca64 100644 --- a/docs/cbox_api.md +++ b/docs/cbox_api.md @@ -14,18 +14,23 @@ See similar c++ class [std::unique_ptr](https://en.cppreference.com/w/cpp/memory ## Header file and declaration ```c -#define i_type // full typename of the cbox -#define i_key // element type: REQUIRED -#define i_cmp // three-way compare two i_key* : REQUIRED IF i_key is a non-integral type -#define i_keydrop // destroy element func - defaults to empty destruct -#define i_keyclone // REQUIRED if i_keydrop is defined, unless 'i_opt c_no_clone' is defined. - -#define i_keyraw // convertion type (lookup): default to {i_key} -#define i_keyto // convertion func i_key* => i_keyraw: REQUIRED IF i_keyraw defined. -#define i_keyfrom // from-raw func. - -#define i_keyclass // alt. to i_key: REQUIRES that {i_key}_clone, {i_key}_drop, {i_keyraw}_cmp exist. -#define i_tag // alternative typename: cbox_{i_tag}. i_tag defaults to i_key +#define i_key <t> // element type: REQUIRED. Note: i_val* may be specified instead of i_key*. +#define i_type <t> // cbox container type name +#define i_cmp <f> // three-way compareison. REQUIRED IF i_key is a non-integral type + // Note that containers of carcs will "inherit" i_cmp + // when using carc in containers with i_valboxed MyArc - ie. the i_type. +#define i_cmp_native // define instead of i_cmp only when i_key is an integral/native-type. +#define i_keydrop <f> // destroy element func - defaults to empty destruct +#define i_keyclone <f> // REQUIRED if i_keydrop is defined, unless 'i_opt c_no_clone' is defined. + +#define i_keyraw <t> // convertion type (lookup): default to {i_key} +#define i_keyto <f> // convertion func i_key* => i_keyraw: REQUIRED IF i_keyraw defined. +#define i_keyfrom <f> // from-raw func. + +#define i_tag <s> // alternative typename: cbox_{i_tag}. i_tag defaults to i_key +#define i_keyclass <t> // Use instead of i_key when functions {i_key}_clone, + // {i_key}_drop and {i_keyraw}_cmp exist. +#define i_keyboxed <t> // Use instead of i_key when key is a carc- or a cbox-type. #include <stc/cbox.h> ``` `X` should be replaced by the value of `i_tag` in all of the following documentation. diff --git a/docs/cdeq_api.md b/docs/cdeq_api.md index c6de6cd6..38de7f66 100644 --- a/docs/cdeq_api.md +++ b/docs/cdeq_api.md @@ -10,17 +10,18 @@ See the c++ class [std::deque](https://en.cppreference.com/w/cpp/container/deque ## Header file and declaration ```c -#define i_type // full typename of the container -#define i_key // value: REQUIRED -#define i_cmp // three-way compare two i_keyraw* : REQUIRED IF i_keyraw is a non-integral type -#define i_keydrop // destroy value func - defaults to empty destruct -#define i_keyclone // REQUIRED IF i_keydrop defined - -#define i_keyraw // convertion "raw" type - defaults to i_key -#define i_keyfrom // convertion func i_keyraw => i_key -#define i_keyto // convertion func i_key* => i_keyraw - -#define i_tag // alternative typename: cdeq_{i_tag}. i_tag defaults to i_key +#define i_key <t> // element type: REQUIRED. Note: i_val* may be specified instead of i_key*. +#define i_type <t> // cdeq container type name +#define i_cmp <f> // three-way compare of two i_keyraw*. +#define i_cmp_native // define instead of i_cmp only when i_key is an integral/native-type. +#define i_keydrop <f> // destroy value func - defaults to empty destruct +#define i_keyclone <f> // REQUIRED IF i_keydrop is defined + +#define i_keyraw <t> // convertion "raw" type - defaults to i_key +#define i_keyfrom <f> // convertion func i_keyraw => i_key +#define i_keyto <f> // convertion func i_key* => i_keyraw + +#define i_tag <s> // alternative typename: cdeq_{i_tag}. i_tag defaults to i_key #include <stc/cdeq.h> ``` `X` should be replaced by the value of `i_tag` in all of the following documentation. diff --git a/docs/clist_api.md b/docs/clist_api.md index 3d785789..d8d614c2 100644 --- a/docs/clist_api.md +++ b/docs/clist_api.md @@ -22,16 +22,17 @@ See the c++ class [std::list](https://en.cppreference.com/w/cpp/container/list) ## Header file and declaration ```c -#define i_type // container type name (default: clist_{i_key}) -#define i_key // value: REQUIRED -#define i_cmp // three-way compare two i_keyraw* : REQUIRED IF i_keyraw is a non-integral type -#define i_keydrop // destroy value func - defaults to empty destruct -#define i_keyclone // REQUIRED IF i_keydrop defined - -#define i_keyraw // convertion "raw" type (default: {i_key}) -#define i_keyto // convertion func i_key* => i_keyraw -#define i_keyfrom // convertion func i_keyraw => i_key -#define i_tag // alternative typename: cpque_{i_tag}. i_tag defaults to i_key +#define i_key <t> // element type: REQUIRED. Note: i_val* may be specified instead of i_key*. +#define i_type <t> // clist container type name +#define i_cmp <f> // three-way compare two i_keyraw* +#define i_cmp_native // define instead of i_cmp only when i_key is an integral/native-type. +#define i_keydrop <f> // destroy value func - defaults to empty destruct +#define i_keyclone <f> // REQUIRED IF i_keydrop defined + +#define i_keyraw <t> // convertion "raw" type (default: {i_key}) +#define i_keyto <f> // convertion func i_key* => i_keyraw +#define i_keyfrom <f> // convertion func i_keyraw => i_key +#define i_tag <s> // alternative typename: cpque_{i_tag}. i_tag defaults to i_key #include <stc/clist.h> ``` diff --git a/docs/cmap_api.md b/docs/cmap_api.md index eca350b4..17f27662 100644 --- a/docs/cmap_api.md +++ b/docs/cmap_api.md @@ -17,26 +17,25 @@ See the c++ class [std::unordered_map](https://en.cppreference.com/w/cpp/contain ## Header file and declaration ```c -#define i_type // container type name (default: cmap_{i_key}) -#define i_key // hash key: REQUIRED -#define i_val // map value: REQUIRED -#define i_hash // hash func i_keyraw*: REQUIRED IF i_keyraw is non-pod type -#define i_eq // equality comparison two i_keyraw*: REQUIRED IF i_keyraw is a - // non-integral type. Three-way i_cmp may alternatively be specified. -#define i_keydrop // destroy key func - defaults to empty destruct -#define i_keyclone // REQUIRED IF i_keydrop defined -#define i_keyraw // convertion "raw" type - defaults to i_key -#define i_keyfrom // convertion func i_keyraw => i_key -#define i_keyto // convertion func i_key* => i_keyraw - -#define i_valdrop // destroy value func - defaults to empty destruct -#define i_valclone // REQUIRED IF i_valdrop defined -#define i_valraw // convertion "raw" type - defaults to i_val -#define i_valfrom // convertion func i_valraw => i_val -#define i_valto // convertion func i_val* => i_valraw - -#define i_tag // alternative typename: cmap_{i_tag}. i_tag defaults to i_val -#define i_expandby // default 1. If 2, table expand 2x (else 1.5x) +#define i_key <t> // key type: REQUIRED. +#define i_val <t> // mapped value type: REQUIRED. +#define i_type <t> // container type name (default: cmap_{i_key}) +#define i_hash <f> // hash func i_keyraw*: REQUIRED IF i_keyraw is non-pod type +#define i_eq <f> // equality comparison two i_keyraw*: REQUIRED IF i_keyraw is a + // non-integral type. Three-way i_cmp may alternatively be specified. +#define i_keydrop <f> // destroy key func - defaults to empty destruct +#define i_keyclone <f> // REQUIRED IF i_keydrop defined +#define i_keyraw <t> // convertion "raw" type - defaults to i_key +#define i_keyfrom <f> // convertion func i_keyraw => i_key +#define i_keyto <f> // convertion func i_key* => i_keyraw + +#define i_valdrop <f> // destroy value func - defaults to empty destruct +#define i_valclone <f> // REQUIRED IF i_valdrop defined +#define i_valraw <t> // convertion "raw" type - defaults to i_val +#define i_valfrom <f> // convertion func i_valraw => i_val +#define i_valto <f> // convertion func i_val* => i_valraw + +#define i_tag <s> // alternative typename: cmap_{i_tag}. i_tag defaults to i_val #include <stc/cmap.h> ``` `X` should be replaced by the value of `i_tag` in all of the following documentation. diff --git a/docs/coroutine_api.md b/docs/coroutine_api.md new file mode 100644 index 00000000..30b85640 --- /dev/null +++ b/docs/coroutine_api.md @@ -0,0 +1,292 @@ +# STC [coroutine](../include/stc/coroutine.h): Coroutines + + +An implementation of stackless coroutines, which are lightweight threads, providing a blocking +context cheaply using little memory per coroutine. + +Coroutines are used to accomplish a non-preempted form of concurrency known as cooperative multitasking and, therefore, do not incur context switching when yielding to another thread. Within a coroutine, **yield** and **await** is accomplished by utilizing Duff's device within a thread's function and an external variable used in within the switch statement. This allows jumping (resuming) from a yield upon another function call. In order to block threads, these yields may be guarded by a conditional so that successive calls to the same function will yield unless the guard conditional is true. + +Because these coroutines are stackless, local variables within the coroutine where usage crosses `cco_yield` or `cco_await` must be stored in a struct which is passed as pointer to the coroutine. This has the advantages that they become very lightweight and therefore useful on severely memory constrained systems like small microcontrollers where other solutions are impractical or less desirable. + +### Coroutine API + +NB! ***cco_yield\*()*** / ***cco_await\*()*** may not be called from within a `switch` statement in a +`cco_routine` scope; Use `if-else-if` constructs instead. + +| | Function / operator | Description | +|:----------|:-------------------------------------|:----------------------------------------| +|`cco_result` | `CCO_DONE`, `CCO_AWAIT`, `CCO_YIELD` | Default set of return values from coroutines | +| | `cco_cleanup:` | Label for cleanup position in coroutine | +| `bool` | `cco_done(co)` | Is coroutine done? | +| | `cco_routine(co) {}` | The coroutine scope | +| | `cco_yield();` | Yield/suspend execution (return CCO_YIELD)| +| | `cco_yield_v(ret);` | Yield/suspend execution (return ret) | +| | `cco_yield_final();` | Yield final suspend, enter cleanup-state | +| | `cco_yield_final(ret);` | Yield a final value | +| | `cco_await(condition);` | Suspend until condition is true (return CCO_AWAIT)| +| | `cco_call_await(cocall);` | Await for subcoro to finish (returns its ret value) | +| | `cco_call_await(cocall, retbit);` | Await for subcoro's return to be in (retbit \| CCO_DONE) | +| | `cco_return;` | Return from coroutine (inside cco_routine) | +| | Task objects: | | +| | `cco_task_struct(Name, ...);` | Define a coroutine task struct | +| | `cco_task_await(task, cco_runtime* rt);`| Await for task to finish | +| | `cco_task_await(task, rt, retbit);` | Await for task's return to be in (retbit \| CCO_DONE) | +|`cco_result`| `cco_task_resume(task, rt);` | Resume suspended task | +| | Semaphores: | | +| | `cco_sem` | Semaphore type | +| `cco_sem` | `cco_sem_from(long value)` | Create semaphore | +| | `cco_sem_set(sem, long value)` | Set semaphore value | +| | `cco_sem_await(sem)` | Await for the semaphore count > 0 | +| | `cco_sem_release(sem)` | Signal the semaphore (count += 1) | +| | Timers: | | +| | `cco_timer` | Timer type | +| | `cco_timer_await(tm, double sec)` | Await secs for timer to expire (usec prec.)| +| | `cco_timer_start(tm, double sec)` | Start timer for secs duration | +| | `cco_timer_restart(tm)` | Restart timer with same duration | +| `bool` | `cco_timer_expired(tm)` | Return true if timer is expired | +| `double` | `cco_timer_elapsed(tm)` | Return seconds elapsed | +| `double` | `cco_timer_remaining(tm)` | Return seconds remaining | +| | From caller side: | | +| `void` | `cco_stop(co)` | Next call of coroutine finalizes | +| `void` | `cco_reset(co)` | Reset state to initial (for reuse) | +| `void` | `cco_call_blocking(cocall) {}` | Run blocking until cocall is finished | +| `void` | `cco_call_blocking(cocall, int* outres) {}`| Run blocking until cocall is finished | +| | `cco_task_blocking(task) {}` | Run blocking until task is finished | +| | `cco_task_blocking(task, rt, STACKSZ) {}`| Run blocking until task is finished | +| | Time functions: | | +| `double` | `cco_time(void)` | Return secs with usec prec. since Epoch | +| | `cco_sleep(double sec)` | Sleep for seconds (msec or usec prec.) | + + +## Implementation and examples + +This small implementation of coroutines is inspired by +[Simon Tatham's coroutines](https://www.chiark.greenend.org.uk/~sgtatham/coroutines.html) and +[Adam Dunkel's photothreads](https://dunkels.com/adam/pt), but provides big improvements regarding +ergonomics, features, and type-safety. Crucially, it also allows coroutines to self-cleanup when +cancelled (not resumed until they are done). + +A coroutine function may have almost any signature, but the implementation adds support for +coroutines which returns an int, indicating CCO_DONE, CCO_AWAIT, or CCO_YIELD. It should also +take a struct pointer as parameter which must contains the member `int cco_state`. The struct should +normally store all *local* variables to be used within the coroutine, along with *input* and *output* data +for the coroutine. + +Note that this implementation is not limited to support a certain set of coroutine types, +like generators. It can even operate like stackfull coroutines, i.e. you can efficiently +yield or await from a (deeply) nested coroutine call using cco_task objects described later. + +The first example is a generator of Pythagorian triples, and stops when diagonal size > max_c. + +[ [Run this code](https://godbolt.org/z/3Efn17cP6) ] +```c +#include <stc/coroutine.h> +#include <stdio.h> +#include <stdio.h> +// https://quuxplusone.github.io/blog/2019/03/06/pythagorean-triples/ + +struct triples { + int max_c; // input: max c. + int a, b, c; // output + int cco_state; // required member +}; + +int triples(struct triples* i) { + cco_routine(i) { // the coroutine scope! + for (i->c = 5;; ++i->c) { + for (i->a = 1; i->a < i->c; ++i->a) { + for (i->b = i->a + 1; i->b < i->c; ++i->b) { + if ((int64_t)i->a * i->a + + (int64_t)i->b * i->b == + (int64_t)i->c * i->c) + { + if (i->c > i->max_c) + cco_return; // "jump" to cco_cleanup if defined, else exit scope. + cco_yield(); + } + } + } + } + cco_cleanup: + puts("done"); + } + return 0; // CCO_DONE +} + +int main(void) { + struct triples co = {.max_c = 25}; + int n = 0; + + cco_call_blocking(triples(&co)) { + printf("%d: [%d, %d, %d]\n", ++n, co.a, co.b, co.c); + } +} +``` +The next variant skips the triples which are upscaled version of smaller ones by checking +the gcd() function. Note that the gcd1_triples struct contains the triples struct so that +both functions have separate call frames: + +[ [Run this code](https://godbolt.org/z/ndhMq1haj) ] +```c +int gcd(int a, int b) { // greatest common denominator + while (b) { + int t = a % b; + a = b; + b = t; + } + return a; +} + +struct gcd1_triples { + int max_n, max_c, count; // input: max_n, max_c limit #triples to be generated. + struct triples tri; // triples call frame + int cco_state; +}; + +int gcd1_triples(struct gcd1_triples* i) +{ + cco_routine(i) { + cco_reset(&i->tri); + i->tri.max_c = i->max_c; + + while (triples(&i->tri) != CCO_DONE) { + // Skip triples with GCD(a,b) > 1 + if (gcd(i->tri.a, i->tri.b) > 1) + continue; + + // Done when count > max_n + if (++i->count > i->max_n) + cco_return; + else + cco_yield(); + } + cco_cleanup: + cco_stop(&i->tri); // to cleanup state if still active + triples(&i->tri); // do cleanup (or no-op if done) + } + return 0; +} + +int main(void) { + struct gcd1_triples co = {.max_n = 100, .max_c = 100}; + int n = 0; + + cco_call_blocking(gcd1_triples(&co)) { + printf("%d: [%d, %d, %d]\n", ++n, co.tri.a, co.tri.b, co.tri.c); + } +} +``` +When using ***cco_call_blocking()***, the coroutine is continuously resumed after each yield suspension. +However, this means that it first calls ***gcd1_triples()***, which immediately jumps to after the `cco_yield` +-statement and calls ***triples()***, which again jumps and resumes after its `cco_yield`-statement. This +is efficient only when yielding or awaiting from the top- or second-level call like here, but naturally not +when couroutine calls are more deeply nested or recursive. + +The STC coroutine implementation therefore also contains task-objects (`cco_task`), which are base-coroutine +objects/enclosures. These can be executed using ***cco_task_blocking()*** instead of ***cco_call_blocking()***. +Inner coroutine calls are done by ***cco_task_await()***, where you may await for a certain return value, normally CCO_YIELD or just CCO_DONE. It uses a stack of pointers of task-enclosures to call the current +inner-level function directly. The task-objects have the added benefit that coroutines can be managed +by a scheduler, which is useful when dealing with large numbers of coroutines (like in many games and +simulations). + +Note that these two modes may be mixed, and that for short-lived coroutines (only a few suspends), +it is often beneficial to call the sub-coroutine directly rather than via ***cco_task_await()*** +(which pushes the task on top of the runtime stack and yields - then executed on next resume). + +The following example uses task-objects with 3-levels deep coroutine calls. It emulates an async generator +by awaiting a few seconds before producing a number, using a timer. +```c +// https://mariusbancila.ro/blog/2020/06/22/a-cpp20-coroutine-example/ +#include <time.h> +#include <stdio.h> +#define i_static +#include <stc/cstr.h> +#include <stc/coroutine.h> + +cco_task_struct (next_value, + int val; + cco_timer tm; +); + +int next_value(struct next_value* co, cco_runtime* rt) +{ + cco_routine (co) { + while (true) { + cco_timer_await(&co->tm, 1 + rand() % 2); // suspend with CCO_AWAIT + co->val = rand(); + cco_yield(); // suspend with CCO_YIELD + } + } + return 0; +} + +void print_time() +{ + time_t now = time(NULL); + char mbstr[64]; + strftime(mbstr, sizeof(mbstr), "[%H:%M:%S]", localtime(&now)); + printf("%s ", mbstr); +} + +// PRODUCER +cco_task_struct (produce_items, + struct next_value next; + cstr str; +); + +int produce_items(struct produce_items* p, cco_runtime* rt) +{ + cco_routine (p) { + p->str = cstr_null; + p->next.cco_func = next_value; + while (true) + { + // await for CCO_YIELD (or CCO_DONE) + cco_task_await(&p->next, rt, CCO_YIELD); + cstr_printf(&p->str, "item %d", p->next.val); + print_time(); + printf("produced %s\n", cstr_str(&p->str)); + cco_yield(); + } + cco_cleanup: + cstr_drop(&p->str); + puts("done produce"); + } + return 0; +} + +// CONSUMER +cco_task_struct (consume_items, + int n, i; + struct produce_items produce; +); + +int consume_items(struct consume_items* c, cco_runtime* rt) +{ + cco_routine (c) { + c->produce.cco_func = produce_items; + + for (c->i = 1; c->i <= c->n; ++c->i) + { + printf("consume #%d\n", c->i); + cco_task_await(&c->produce, rt, CCO_YIELD); + print_time(); + printf("consumed %s\n", cstr_str(&c->produce.str)); + } + cco_cleanup: + cco_stop(&c->produce); + cco_task_resume(&c->produce, rt); + puts("done consume"); + } + return 0; +} + +int main(void) +{ + struct consume_items consume = { + .n = 5, + .cco_func = consume_items, + }; + cco_task_blocking(&consume); +} +```
\ No newline at end of file diff --git a/docs/cpque_api.md b/docs/cpque_api.md index 5b63dfd1..247424b4 100644 --- a/docs/cpque_api.md +++ b/docs/cpque_api.md @@ -8,17 +8,17 @@ See the c++ class [std::priority_queue](https://en.cppreference.com/w/cpp/contai ## Header file and declaration ```c -#define i_type // define type name of the container (default cpque_{i_key}) -#define i_key // value: REQUIRED -#define i_less // compare two i_key* : REQUIRED IF i_key/i_keyraw is a non-integral type -#define i_keydrop // destroy value func - defaults to empty destruct -#define i_keyclone // REQUIRED IF i_keydrop defined +#define i_key <t> // element type: REQUIRED. Note: i_val* may be specified instead of i_key*. +#define i_type <t> // cpque container type name +#define i_less <f> // compare two i_key* : REQUIRED IF i_key/i_keyraw is a non-integral type +#define i_keydrop <f> // destroy value func - defaults to empty destruct +#define i_keyclone <f> // REQUIRED IF i_keydrop defined -#define i_keyraw // convertion type -#define i_keyfrom // convertion func i_keyraw => i_key -#define i_keyto // convertion func i_key* => i_keyraw. +#define i_keyraw <t> // convertion type +#define i_keyfrom <f> // convertion func i_keyraw => i_key +#define i_keyto <f> // convertion func i_key* => i_keyraw. -#define i_tag // alternative typename: cpque_{i_tag}. i_tag defaults to i_key +#define i_tag <s> // alternative typename: cpque_{i_tag}. i_tag defaults to i_key #include <stc/cpque.h> ``` `X` should be replaced by the value of `i_tag` in all of the following documentation. @@ -72,14 +72,14 @@ int main(void) { intptr_t N = 10000000; crand_t rng = crand_init(1234); - crand_unif_t dist = crand_unif_init(0, N * 10); + crand_uniform_t dist = crand_uniform_init(0, N * 10); // Define heap cpque_i heap = {0}; // Push ten million random numbers to priority queue. c_forrange (N) - cpque_i_push(&heap, crand_unif(&rng, &dist)); + cpque_i_push(&heap, crand_uniform(&rng, &dist)); // Add some negative ones. int nums[] = {-231, -32, -873, -4, -343}; diff --git a/docs/cqueue_api.md b/docs/cqueue_api.md index b324e5fc..1834baf9 100644 --- a/docs/cqueue_api.md +++ b/docs/cqueue_api.md @@ -7,16 +7,16 @@ See the c++ class [std::queue](https://en.cppreference.com/w/cpp/container/queue ## Header file and declaration ```c -#define i_type // container type name (default: cqueue_{i_key}) -#define i_key // value: REQUIRED -#define i_keydrop // destroy value func - defaults to empty destruct -#define i_keyclone // REQUIRED IF i_keydrop defined +#define i_key <t> // element type: REQUIRED. Note: i_val* may be specified instead of i_key*. +#define i_type <t> // cqueue container type name +#define i_keydrop <f> // destroy value func - defaults to empty destruct +#define i_keyclone <f> // REQUIRED IF i_keydrop defined -#define i_keyraw // convertion "raw" type - defaults to i_key -#define i_keyfrom // convertion func i_keyraw => i_key -#define i_keyto // convertion func i_key* => i_keyraw +#define i_keyraw <t> // convertion "raw" type - defaults to i_key +#define i_keyfrom <f> // convertion func i_keyraw => i_key +#define i_keyto <f> // convertion func i_key* => i_keyraw -#define i_tag // alternative typename: cqueue_{i_tag}. i_tag defaults to i_key +#define i_tag <s> // alternative typename: cqueue_{i_tag}. i_tag defaults to i_key #include <stc/cqueue.h> ``` `X` should be replaced by the value of `i_tag` in all of the following documentation. diff --git a/docs/crandom_api.md b/docs/crandom_api.md index 22a4f4dd..88924784 100644 --- a/docs/crandom_api.md +++ b/docs/crandom_api.md @@ -1,30 +1,29 @@ # STC [crand](../include/stc/crand.h): Pseudo Random Number Generator  -This features a *64-bit PRNG* named **stc64**, and can generate bounded uniform and normal +This features a *64-bit PRNG* named **crand64**, and can generate bounded uniform and normal distributed random numbers. See [random](https://en.cppreference.com/w/cpp/header/random) for similar c++ functionality. ## Description -**stc64** is a novel, extremely fast PRNG by Tyge Løvset, suited for parallel usage. It features -Weyl-sequences as part of its state. It is inspired on *sfc64*, but has a different output function +**crand64** is a novel, very fast PRNG, suited for parallel usage. It features a +Weyl-sequence as part of its state. It is based on *sfc64*, but has a different output function and state size. -**sfc64** is the fastest among *pcg*, *xoshiro`**`*, and *lehmer*. It is equally fast as *sfc64* on -most platforms. *wyrand* is faster on platforms with fast 128-bit multiplication, and has 2^64 period -length (https://github.com/lemire/SwiftWyhash/issues/10). However, *wyrand* is not suited for massive -parallel usage due to its limited total minimal period length. +**sfc64** is the fastest among *pcg*, *xoshiro`**`*, and *lehmer*. It is equally fast or faster than +*sfc64* on most platforms. *wyrand* is faster on platforms with fast 128-bit multiplication, and has +2^64 period (https://github.com/lemire/SwiftWyhash/issues/10). *wyrand* is not suited for massive +parallel usage due to its limited minimal period. -**stc64** does not require multiplication or 128-bit integer operations. It has 320 bit state, -but updates only 256 bit per generated number. +**crand64** does not require multiplication or 128-bit integer operations. It has 320 bit state, +where 64-bits are constant per prng instance created. There is no *jump function*, but each odd number Weyl-increment (state[4]) starts a new -unique 2^64 *minimum* length period. For a single thread, a minimum period of 2^127 is generated -when the Weyl-increment is incremented by 2 every 2^64 output. +unique 2^64 *minimum* length period, i.e. virtually unlimitied number of unique threads. -**stc64** passes *PractRand* (tested up to 8TB output), Vigna's Hamming weight test, and simple +**crand64** passes *PractRand* (tested up to 8TB output), Vigna's Hamming weight test, and simple correlation tests, i.e. *n* interleaved streams with only one-bit differences in initial state. Also 32-bit and 16-bit versions passes PractRand up to their size limits. @@ -41,27 +40,27 @@ All crand definitions and prototypes are available by including a single header ## Methods ```c -void csrand(uint64_t seed); // seed global stc64 prng +void csrand(uint64_t seed); // seed global crand64 prng uint64_t crand(void); // global crand_u64(rng) double crandf(void); // global crand_f64(rng) -crand_t crand_init(uint64_t seed); // stc64_init(s) is deprecated +crand_t crand_init(uint64_t seed); uint64_t crand_u64(crand_t* rng); // range [0, 2^64 - 1] double crand_f64(crand_t* rng); // range [0.0, 1.0) -crand_unif_t crand_unif_init(int64_t low, int64_t high); // uniform-distribution -int64_t crand_unif(crand_t* rng, crand_unif_t* dist); // range [low, high] +crand_uniform_t crand_uniform_init(int64_t low, int64_t high); // uniform-distribution range +int64_t crand_uniform(crand_t* rng, crand_uniform_t* dist); -crand_norm_t crand_norm_init(double mean, double stddev); // normal-distribution -double crand_norm(crand_t* rng, crand_norm_t* dist); +crand_normal_t crand_normal_init(double mean, double stddev); // normal-gauss distribution +double crand_normal(crand_t* rng, crand_normal_t* dist); ``` ## Types | Name | Type definition | Used to represent... | |:-------------------|:------------------------------------------|:-----------------------------| | `crand_t` | `struct {uint64_t state[4];}` | The PRNG engine type | -| `crand_unif_t` | `struct {int64_t lower; uint64_t range;}` | Integer uniform distribution | -| `crand_norm_t` | `struct {double mean, stddev;}` | Normal distribution type | +| `crand_uniform_t` | `struct {int64_t lower; uint64_t range;}` | Integer uniform distribution | +| `crand_normal_t` | `struct {double mean, stddev;}` | Normal distribution type | ## Example ```c @@ -86,17 +85,17 @@ int main(void) // Setup random engine with normal distribution. uint64_t seed = time(NULL); crand_t rng = crand_init(seed); - crand_norm_t dist = crand_norm_init(Mean, StdDev); + crand_normal_t dist = crand_normal_init(Mean, StdDev); // Create histogram map - csmap_i mhist = csmap_i_init(); + csmap_i mhist = {0}; c_forrange (N) { - int index = (int)round(crand_norm(&rng, &dist)); + int index = (int)round(crand_normal(&rng, &dist)); csmap_i_emplace(&mhist, index, 0).ref->second += 1; } // Print the gaussian bar chart - cstr bar = cstr_init(); + cstr bar = {0}; c_foreach (i, csmap_i, mhist) { int n = (int)(i.ref->second * StdDev * Scale * 2.5 / N); if (n > 0) { diff --git a/docs/cset_api.md b/docs/cset_api.md index e894ad4f..928d63a8 100644 --- a/docs/cset_api.md +++ b/docs/cset_api.md @@ -7,19 +7,19 @@ A **cset** is an associative container that contains a set of unique objects of ## Header file and declaration ```c -#define i_type // container type name (default: cset_{i_key}) -#define i_key // hash key: REQUIRED. -#define i_hash // hash func: REQUIRED IF i_keyraw is a non-pod type. -#define i_eq // equality comparison two i_keyraw*: !i_cmp is used if not defined. -#define i_keydrop // destroy key func - defaults to empty destruct -#define i_keyclone // REQUIRED IF i_keydrop defined - -#define i_keyraw // convertion "raw" type - defaults to i_key -#define i_keyfrom // convertion func i_keyraw => i_key - defaults to plain copy -#define i_keyto // convertion func i_key* => i_keyraw - defaults to plain copy - -#define i_tag // alternative typename: cmap_{i_tag}. i_tag defaults to i_key -#define i_expandby // default 1. If 2, table expand 2x (else 1.5x) +#define i_key <t> // element type: REQUIRED. Note: i_val* may be specified instead of i_key*. +#define i_type <t> // container type name +#define i_hash <f> // hash func i_keyraw*: REQUIRED IF i_keyraw is non-pod type +#define i_eq <f> // equality comparison two i_keyraw*: REQUIRED IF i_keyraw is a + // non-integral type. Three-way i_cmp may alternatively be specified. +#define i_keydrop <f> // destroy key func: defaults to empty destruct +#define i_keyclone <f> // clone func: REQUIRED IF i_keydrop defined + +#define i_keyraw <t> // convertion "raw" type - defaults to i_key +#define i_keyfrom <f> // convertion func i_keyraw => i_key - defaults to plain copy +#define i_keyto <f> // convertion func i_key* => i_keyraw - defaults to plain copy + +#define i_tag <s> // alternative typename: cmap_{i_tag}. i_tag defaults to i_key #include <stc/cset.h> ``` `X` should be replaced by the value of `i_tag` in all of the following documentation. diff --git a/docs/csmap_api.md b/docs/csmap_api.md index 099d7dfc..164b0f8a 100644 --- a/docs/csmap_api.md +++ b/docs/csmap_api.md @@ -15,24 +15,24 @@ See the c++ class [std::map](https://en.cppreference.com/w/cpp/container/map) fo ## Header file and declaration ```c -#define i_type // container type name (default: cmap_{i_key}) -#define i_key // key: REQUIRED -#define i_val // value: REQUIRED -#define i_cmp // three-way compare two i_keyraw* : REQUIRED IF i_keyraw is a non-integral type - -#define i_keydrop // destroy key func - defaults to empty destruct -#define i_keyclone // REQUIRED IF i_valdrop defined -#define i_keyraw // convertion "raw" type - defaults to i_key -#define i_keyfrom // convertion func i_keyraw => i_key -#define i_keyto // convertion func i_key* => i_keyraw - -#define i_valdrop // destroy value func - defaults to empty destruct -#define i_valclone // REQUIRED IF i_valdrop defined -#define i_valraw // convertion "raw" type - defaults to i_val -#define i_valfrom // convertion func i_valraw => i_val -#define i_valto // convertion func i_val* => i_valraw - -#define i_tag // alternative typename: csmap_{i_tag}. i_tag defaults to i_val +#define i_key <t> // key type: REQUIRED. +#define i_val <t> // mapped value type: REQUIRED. +#define i_type <t> // container type name (default: cmap_{i_key}) +#define i_cmp <f> // three-way compare two i_keyraw* : REQUIRED IF i_keyraw is a non-integral type + +#define i_keydrop <f> // destroy key func - defaults to empty destruct +#define i_keyclone <f> // REQUIRED IF i_valdrop defined +#define i_keyraw <t> // convertion "raw" type - defaults to i_key +#define i_keyfrom <f> // convertion func i_keyraw => i_key +#define i_keyto <f> // convertion func i_key* => i_keyraw + +#define i_valdrop <f> // destroy value func - defaults to empty destruct +#define i_valclone <f> // REQUIRED IF i_valdrop defined +#define i_valraw <t> // convertion "raw" type - defaults to i_val +#define i_valfrom <f> // convertion func i_valraw => i_val +#define i_valto <f> // convertion func i_val* => i_valraw + +#define i_tag <s> // alternative typename: csmap_{i_tag}. i_tag defaults to i_val #include <stc/csmap.h> ``` `X` should be replaced by the value of `i_tag` in all of the following documentation. @@ -148,7 +148,7 @@ Translate a [C++ example using *insert* and *emplace*](https://en.cppreference.com/w/cpp/container/map/try_emplace) to STC: -[ [Run this code](https://godbolt.org/z/9d1PP77Pa) ] +[ [Run this code](https://godbolt.org/z/b46W5Ezrb) ] ```c #define i_implement #include <stc/cstr.h> diff --git a/docs/cspan_api.md b/docs/cspan_api.md index 09821450..1312ae6d 100644 --- a/docs/cspan_api.md +++ b/docs/cspan_api.md @@ -21,19 +21,20 @@ using_cspan4(S, ValueType); // define span types S, S2, S3, S4 with ``` ## Methods -All functions are type-safe. Note that the span argument itself is generally not side-effect safe, -i.e., it may be expanded multiple times. However, all index arguments are safe, e.g. -`cspan_at(&ms3, i++, j++, k++)` is allowed. If the number of arguments does not match the span rank, -a compile error is issued. Runtime bounds checks are enabled by default (define `STC_NDEBUG` or `NDEBUG` to disable). +All functions are type-safe. NOTE: the span argument itself is generally **not** side-effect safe - +it may be expanded multiple times. However, all index arguments are safe, e.g. +`cspan_at(&ms3, i++, j++, k++)` is safe, but `cspan_at(&spans[n++], i, j)` is an error! If the number +of arguments does not match the span rank, a compile error is issued. Runtime bounds checks are enabled +by default (define `STC_NDEBUG` or `NDEBUG` to disable). ```c -SpanType cspan_init(T SpanType, {v1, v2, ...}); // make a 1-d cspan from values -SpanType cspan_from(STCContainer* cnt); // make a 1-d cspan from compatible STC container -SpanType cspan_from_array(ValueType array[]); // make a 1-d cspan from C array - +SpanType cspan_init(TYPE SpanType, {v1, v2, ...}); // make a 1-d cspan from values +SpanType cspan_from(STCContainer* cnt); // make a 1-d cspan from a cvec, cstack, cpque (heap) +SpanType cspan_from_array(ValueType array[]); // make a 1-d cspan from a C array + intptr_t cspan_size(const SpanTypeN* self); // return number of elements intptr_t cspan_rank(const SpanTypeN* self); // dimensions; compile time constant intptr_t cspan_index(const SpanTypeN* self, intptr_t x, ..); // index of element - + ValueType* cspan_at(const SpanTypeN* self, intptr_t x, ...); // #args must match input span rank ValueType* cspan_front(const SpanTypeN* self); ValueType* cspan_back(const SpanTypeN* self); @@ -45,7 +46,7 @@ void SpanType_next(SpanTypeN_iter* it); SpanTypeN cspan_md(ValueType* data, d1, d2, ...); // make a multi-dim cspan, row-major order. SpanTypeN cspan_md_order(char order, ValueType* data, d1, d2, ...); // order='C': row-major, 'F': column-major (FORTRAN). - // transpose a md span (inverse axes). no changes to the underlying array. + // transpose a md span (inverse axes). No changes to the underlying array. void cspan_transpose(const SpanTypeN* self); bool cspan_is_order_F(const SpanTypeN* self); @@ -55,13 +56,13 @@ SpanType2 cspan_subspan2(const SpanType2* span, intptr_t offset, intptr_t SpanType3 cspan_subspan3(const SpanType3* span, intptr_t offset, intptr_t count); // create a sub md span of lower rank. Like e.g. cspan_slice(Span2, &ms4, {x}, {y}, {c_ALL}, {c_ALL}); -OutSpan1 cspan_submd2(const SpanType2* parent, intptr_t x); // return a 1d subspan from a 2d span. +OutSpan cspan_submd2(const SpanType2* parent, intptr_t x); // return a 1d subspan from a 2d span. OutSpanN cspan_submd3(const SpanType3* parent, intptr_t x, ...); // return a 1d or 2d subspan from a 3d span. OutSpanN cspan_submd4(const SpanType4* parent, intptr_t x, ...); // number of args decides rank of output span. // general slicing of an md span. // {i}: reduce rank. {i,c_END}: slice to end. {c_ALL}: use full extent. -OutSpanN cspan_slice(TYPE OutSpanN, const SpanTypeN* parent, {x0,x1}, {y0,y1}.., {N0,N1}); +OutSpanN cspan_slice(TYPE OutSpanN, const SpanTypeM* parent, {x0,x1}, {y0,y1}.., {N0,N1}); ``` ## TypesPd | Type name | Type definition / usage | Used to represent... | diff --git a/docs/csset_api.md b/docs/csset_api.md index aef3af3c..21e38f61 100644 --- a/docs/csset_api.md +++ b/docs/csset_api.md @@ -8,17 +8,17 @@ See the c++ class [std::set](https://en.cppreference.com/w/cpp/container/set) fo ## Header file and declaration ```c -#define i_type // full typename of the container -#define i_key // key: REQUIRED -#define i_cmp // three-way compare two i_keyraw* : REQUIRED IF i_keyraw is a non-integral type -#define i_keydrop // destroy key func - defaults to empty destruct -#define i_keyclone // REQUIRED IF i_keydrop defined +#define i_key <t> // element type: REQUIRED. Note: i_val* may be specified instead of i_key*. +#define i_type <t> // container type name +#define i_cmp <f> // three-way compare two i_keyraw* : REQUIRED IF i_keyraw is a non-integral type +#define i_keydrop <f> // destroy key func - defaults to empty destruct +#define i_keyclone <f> // REQUIRED IF i_keydrop defined -#define i_keyraw // convertion "raw" type - defaults to i_key -#define i_keyfrom // convertion func i_keyraw => i_key - defaults to plain copy -#define i_keyto // convertion func i_key* => i_keyraw - defaults to plain copy +#define i_keyraw <t> // convertion "raw" type - defaults to i_key +#define i_keyfrom <f> // convertion func i_keyraw => i_key - defaults to plain copy +#define i_keyto <f> // convertion func i_key* => i_keyraw - defaults to plain copy -#define i_tag // alternative typename: csset_{i_tag}. i_tag defaults to i_key +#define i_tag <s> // alternative typename: csset_{i_tag}. i_tag defaults to i_key #include <stc/csset.h> ``` `X` should be replaced by the value of `i_tag` in all of the following documentation. diff --git a/docs/cstack_api.md b/docs/cstack_api.md index e799b152..fb629392 100644 --- a/docs/cstack_api.md +++ b/docs/cstack_api.md @@ -8,16 +8,16 @@ See the c++ class [std::stack](https://en.cppreference.com/w/cpp/container/stack ## Header file and declaration ```c -#define i_type // full typename of the container -#define i_key // value: REQUIRED -#define i_keydrop // destroy value func - defaults to empty destruct -#define i_keyclone // REQUIRED IF i_keydrop defined +#define i_key <t> // element type: REQUIRED. Note: i_val* may be specified instead of i_key*. +#define i_type <t> // container type name +#define i_keydrop <f> // destroy value func - defaults to empty destruct +#define i_keyclone <f> // REQUIRED IF i_keydrop defined -#define i_keyraw // convertion "raw" type - defaults to i_key -#define i_keyfrom // convertion func i_keyraw => i_key -#define i_keyto // convertion func i_key* => i_keyraw +#define i_keyraw <t> // convertion "raw" type - defaults to i_key +#define i_keyfrom <f> // convertion func i_keyraw => i_key +#define i_keyto <f> // convertion func i_key* => i_keyraw -#define i_tag // alternative typename: cstack_{i_tag}. i_tag defaults to i_key +#define i_tag <s> // alternative typename: cstack_{i_tag}. i_tag defaults to i_key #include <stc/cstack.h> ``` `X` should be replaced by the value of `i_tag` in all of the following documentation. diff --git a/docs/cvec_api.md b/docs/cvec_api.md index d38ef23f..9cba74b5 100644 --- a/docs/cvec_api.md +++ b/docs/cvec_api.md @@ -12,17 +12,18 @@ See the c++ class [std::vector](https://en.cppreference.com/w/cpp/container/vect ## Header file and declaration ```c -#define i_type // full typename of the container -#define i_key // value: REQUIRED -#define i_cmp // three-way compare two i_keyraw* : REQUIRED IF i_keyraw is a non-integral type -#define i_keydrop // destroy value func - defaults to empty destruct -#define i_keyclone // REQUIRED IF i_keydrop defined - -#define i_keyraw // convertion "raw" type - defaults to i_key -#define i_keyfrom // convertion func i_keyraw => i_key -#define i_keyto // convertion func i_key* => i_keyraw - -#define i_tag // alternative typename: cvec_{i_tag}. i_tag defaults to i_key +#define i_type <t> // container type name +#define i_key <t> // element type: REQUIRED. Note: i_val* may be specified instead of i_key*. +#define i_cmp <f> // three-way compare two i_keyraw* +#define i_cmp_native // define instead of i_cmp only when i_key is an integral/native-type. +#define i_keydrop <f> // destroy value func - defaults to empty destruct +#define i_keyclone <f> // REQUIRED IF i_keydrop defined + +#define i_keyraw <t> // convertion "raw" type - defaults to i_key +#define i_keyfrom <f> // convertion func i_keyraw => i_key +#define i_keyto <f> // convertion func i_key* => i_keyraw + +#define i_tag <s> // alternative typename: cvec_{i_tag}. i_tag defaults to i_key #include <stc/cvec.h> ``` `X` should be replaced by the value of `i_tag` in all of the following documentation. diff --git a/docs/pics/Figure_1.png b/docs/pics/Figure_1.png Binary files differnew file mode 100644 index 00000000..263303d2 --- /dev/null +++ b/docs/pics/Figure_1.png diff --git a/docs/pics/coroutine.jpg b/docs/pics/coroutine.jpg Binary files differnew file mode 100644 index 00000000..e5fceab3 --- /dev/null +++ b/docs/pics/coroutine.jpg |
