From eb85069b669e754836b9d4587ba03d3af1a5e975 Mon Sep 17 00:00:00 2001 From: Tyge Løvset Date: Sun, 26 Mar 2023 00:27:45 +0100 Subject: development branch for 4.2 Removed uses of c_auto and c_with in documentation examples and code examples. Still using c_defer a few places. Renamed c11/fmt.h to c11/print.h. Some additions in ccommon.h, e.g. c_const_cast(T, x). Improved docs. --- docs/carc_api.md | 96 +++++++++++++------------- docs/cbox_api.md | 11 +-- docs/ccommon_api.md | 195 ++++++++++++++++++++++++++-------------------------- docs/clist_api.md | 23 +++---- docs/cmap_api.md | 191 ++++++++++++++++++++++++-------------------------- docs/coption_api.md | 2 - docs/cpque_api.md | 34 ++++----- docs/cset_api.md | 50 +++++++------- docs/csmap_api.md | 109 ++++++++++++++--------------- docs/cspan_api.md | 19 +++-- docs/csset_api.md | 49 +++++++------ docs/cstr_api.md | 7 +- docs/csview_api.md | 28 ++++---- docs/cvec_api.md | 46 ++++++------- 14 files changed, 426 insertions(+), 434 deletions(-) (limited to 'docs') diff --git a/docs/carc_api.md b/docs/carc_api.md index cc6c9c32..48b64ff0 100644 --- a/docs/carc_api.md +++ b/docs/carc_api.md @@ -98,56 +98,56 @@ bool carc_X_value_eq(const i_val* x, const i_val* y); int main() { - c_auto (Stack, s1, s2) // RAII - { - // POPULATE s1 with shared pointers to Map: - Map *map; - - map = Stack_push(&s1, Arc_make(Map_init()))->get; // push empty map to s1. - c_forlist (i, Map_raw, { {"Joey", 1990}, {"Mary", 1995}, {"Joanna", 1992} }) { - Map_emplace(map, c_PAIR(i.ref)); // populate it. - } - - map = Stack_push(&s1, Arc_make(Map_init()))->get; - c_forlist (i, Map_raw, { {"Rosanna", 2001}, {"Brad", 1999}, {"Jack", 1980} }) { - Map_emplace(map, c_PAIR(i.ref)); - } - - // POPULATE s2: - map = Stack_push(&s2, Arc_make(Map_init()))->get; - c_forlist (i, Map_raw, { {"Steve", 1979}, {"Rick", 1974}, {"Tracy", 2003} }) { - Map_emplace(map, c_PAIR(i.ref)); - } - - // Share two Maps from s1 with s2 by cloning(=sharing) the carcs: - Stack_push(&s2, Arc_clone(s1.data[0])); - Stack_push(&s2, Arc_clone(s1.data[1])); - - // Deep-copy (does not share) a Map from s1 to s2. - // s2 will contain two shared and two unshared maps. - map = Stack_push(&s2, Arc_from(Map_clone(*s1.data[1].get)))->get; - - // Add one more element to the cloned map: - Map_emplace_or_assign(map, "Cloned", 2022); - - // Add one more element to the shared map: - Map_emplace_or_assign(s1.data[1].get, "Shared", 2022); - - puts("S1"); - c_foreach (i, Stack, s1) { - c_forpair (name, year, Map, *i.ref->get) - printf(" %s:%d", cstr_str(_.name), *_.year); - puts(""); - } - - puts("S2"); - c_foreach (i, Stack, s2) { - c_forpair (name, year, Map, *i.ref->get) - printf(" %s:%d", cstr_str(_.name), *_.year); - puts(""); - } + Stack s1 = {0}, s2 = {0}; + Map *map; + + // POPULATE s1 with shared pointers to Map: + map = Stack_push(&s1, Arc_make(Map_init()))->get; // push empty map to s1. + Map_emplace(map, "Joey", 1990); + Map_emplace(map, "Mary", 1995); + Map_emplace(map, "Joanna", 1992); + + map = Stack_push(&s1, Arc_make(Map_init()))->get; + Map_emplace(map, "Rosanna", 2001); + Map_emplace(map, "Brad", 1999); + Map_emplace(map, "Jack", 1980); + + // POPULATE s2: + map = Stack_push(&s2, Arc_make(Map_init()))->get; + Map_emplace(map, "Steve", 1979); + Map_emplace(map, "Rick", 1974); + Map_emplace(map, "Tracy", 2003); + + // Share two Maps from s1 with s2 by cloning(=sharing) the carcs: + Stack_push(&s2, Arc_clone(s1.data[0])); + Stack_push(&s2, Arc_clone(s1.data[1])); + + // Deep-copy (does not share) a Map from s1 to s2. + // s2 will contain two shared and two unshared maps. + map = Stack_push(&s2, Arc_from(Map_clone(*s1.data[1].get)))->get; + + // Add one more element to the cloned map: + Map_emplace_or_assign(map, "Cloned", 2022); + + // Add one more element to the shared map: + Map_emplace_or_assign(s1.data[1].get, "Shared", 2022); + + puts("S1"); + c_foreach (i, Stack, s1) { + c_forpair (name, year, Map, *i.ref->get) + printf(" %s:%d", cstr_str(_.name), *_.year); puts(""); } + + puts("S2"); + c_foreach (i, Stack, s2) { + c_forpair (name, year, Map, *i.ref->get) + printf(" %s:%d", cstr_str(_.name), *_.year); + puts(""); + } + puts(""); + + c_drop(Stack, &s1, &s2); } ``` Output: diff --git a/docs/cbox_api.md b/docs/cbox_api.md index 8b03d004..ca4d90da 100644 --- a/docs/cbox_api.md +++ b/docs/cbox_api.md @@ -92,11 +92,12 @@ void int_drop(int* x) { int main() { - c_auto (IVec, vec) // declare and init vec, call drop at scope exit - c_auto (ISet, set) // similar - { - vec = c_make(Vec, {2021, 2012, 2022, 2015}); - + IVec vec = c_make(Vec, {2021, 2012, 2022, 2015}); + ISet set = {0}; + c_defer( + IVec_drop(&vec), + ISet_drop(&set) + ){ printf("vec:"); c_foreach (i, IVec, vec) printf(" %d", *i.ref->get); diff --git a/docs/ccommon_api.md b/docs/ccommon_api.md index 64daad42..010ea204 100644 --- a/docs/ccommon_api.md +++ b/docs/ccommon_api.md @@ -2,96 +2,6 @@ The following macros are recommended to use, and they safe/have no side-effects. -## Scope macros (RAII) -### c_auto, c_with, c_scope, c_defer -General ***defer*** mechanics for resource acquisition. These macros allows you to specify the -freeing of the resources at the point where the acquisition takes place. -The **checkauto** utility described below, ensures that the `c_auto*` macros are used correctly. - -| Usage | Description | -|:---------------------------------------|:----------------------------------------------------------| -| `c_with (Type var=init, drop)` | Declare `var`. Defer `drop...` to end of scope | -| `c_with (Type var=init, pred, drop)` | Adds a predicate in order to exit early if init failed | -| `c_auto (Type, var1,...,var4)` | `c_with (Type var1=Type_init(), Type_drop(&var1))` ... | -| `c_scope (init, drop)` | Execute `init` and defer `drop` to end of scope | -| `c_defer (drop...)` | Defer `drop...` to end of scope | -| `continue` | Exit a block above without memory leaks | - -For multiple variables, use either multiple **c_with** in sequence, or declare variable outside -scope and use **c_scope**. For convenience, **c_auto** support up to 4 variables. -```c -// `c_with` is similar to python `with`: it declares and can drop a variable after going out of scope. -bool ok = false; -c_with (uint8_t* buf = malloc(BUF_SIZE), buf != NULL, free(buf)) -c_with (FILE* fp = fopen(fname, "rb"), fp != NULL, fclose(fp)) -{ - int n = fread(buf, 1, BUF_SIZE, fp); - if (n <= 0) continue; // auto cleanup! NB do not break or return here. - ... - ok = true; -} -return ok; - -// `c_auto` automatically initialize and destruct up to 4 variables: -c_auto (cstr, s1, s2) -{ - cstr_append(&s1, "Hello"); - cstr_append(&s1, " world"); - - cstr_append(&s2, "Cool"); - cstr_append(&s2, " stuff"); - - printf("%s %s\n", cstr_str(&s1), cstr_str(&s2)); -} - -// `c_with` is a general variant of `c_auto`: -c_with (cstr str = cstr_lit("Hello"), cstr_drop(&str)) -{ - cstr_append(&str, " world"); - printf("%s\n", cstr_str(&str)); -} - -// `c_scope` is like `c_with` but works with an already declared variable. -static pthread_mutex_t mut; -c_scope (pthread_mutex_lock(&mut), pthread_mutex_unlock(&mut)) -{ - /* Do syncronized work. */ -} - -// `c_defer` executes the expressions when leaving scope. Prefer c_with or c_scope. -cstr s1 = cstr_lit("Hello"), s2 = cstr_lit("world"); -c_defer (cstr_drop(&s1), cstr_drop(&s2)) -{ - printf("%s %s\n", cstr_str(&s1), cstr_str(&s2)); -} -``` -**Example**: Load each line of a text file into a vector of strings: -```c -#include -#include - -#define i_val_str -#include - -// receiver should check errno variable -cvec_str readFile(const char* name) -{ - cvec_str vec = cvec_str_init(); // returned - - c_with (FILE* fp = fopen(name, "r"), fp != NULL, fclose(fp)) - c_with (cstr line = cstr_NULL, cstr_drop(&line)) - while (cstr_getline(&line, fp)) - cvec_str_emplace_back(&vec, cstr_str(&line)); - return vec; -} - -int main() -{ - c_with (cvec_str x = readFile(__FILE__), cvec_str_drop(&x)) - c_foreach (i, cvec_str, x) - printf("%s\n", cstr_str(i.ref)); -} -``` ## Loop abstraction macros ### c_foreach, c_foreach_r, c_forpair @@ -165,10 +75,6 @@ c_forlist (i, cmap_ii_raw, { {4, 5}, {6, 7} }) // string literals pushed to a stack of cstr: c_forlist (i, const char*, {"Hello", "crazy", "world"}) cstack_str_emplace(&stk, *i.ref); - -// reverse the list: -c_forlist (i, int, {1, 2, 3}) - cvec_i_push_back(&vec, i.data[i.size - 1 - i.index]); ``` ### c_forfilter @@ -297,13 +203,18 @@ if (it.ref) cmap_str_erase_at(&map, it); c_erase_if(i, csmap_str, map, cstr_contains(i.ref, "hello")); ``` -### c_swap, c_drop +### c_swap, c_drop, c_const_cast ```c // Safe macro for swapping internals of two objects of same type: c_swap(cmap_int, &map1, &map2); // Drop multiple containers of same type: c_drop(cvec_i, &vec1, &vec2, &vec3); + +// Type-safe casting a from const (pointer): +const char* cs = "Hello"; +char* s = c_const_cast(char*, cs); // OK +int* ip = c_const_cast(int*, cs); // issues a warning! ``` ### General predefined template parameter functions @@ -329,7 +240,99 @@ int array[] = {1, 2, 3, 4}; intptr_t n = c_arraylen(array); ``` -## The **checkauto** utility program (for RAII) +## Scope macros (RAII) +### c_auto, c_with, c_scope, c_defer +General ***defer*** mechanics for resource acquisition. These macros allows you to specify the +freeing of the resources at the point where the acquisition takes place. +The **checkauto** utility described below, ensures that the `c_auto*` macros are used correctly. + +| Usage | Description | +|:---------------------------------------|:----------------------------------------------------------| +| `c_defer (drop...)` | Defer `drop...` to end of scope | +| `c_scope (init, drop)` | Execute `init` and defer `drop` to end of scope | +| `c_scope (init, pred, drop)` | Adds a predicate in order to exit early if init failed | +| `c_with (Type var=init, drop)` | Declare `var`. Defer `drop...` to end of scope | +| `c_with (Type var=init, pred, drop)` | Adds a predicate in order to exit early if init failed | +| `c_auto (Type, var1,...,var4)` | `c_with (Type var1=Type_init(), Type_drop(&var1))` ... | +| `continue` | Exit a block above without memory leaks | + +For multiple variables, use either multiple **c_with** in sequence, or declare variable outside +scope and use **c_scope**. For convenience, **c_auto** support up to 4 variables. +```c +// `c_with` is similar to python `with`: it declares and can drop a variable after going out of scope. +bool ok = false; +c_with (uint8_t* buf = malloc(BUF_SIZE), buf != NULL, free(buf)) +c_with (FILE* fp = fopen(fname, "rb"), fp != NULL, fclose(fp)) +{ + int n = fread(buf, 1, BUF_SIZE, fp); + if (n <= 0) continue; // auto cleanup! NB do not break or return here. + ... + ok = true; +} +return ok; + +// `c_auto` automatically initialize and destruct up to 4 variables: +c_auto (cstr, s1, s2) +{ + cstr_append(&s1, "Hello"); + cstr_append(&s1, " world"); + + cstr_append(&s2, "Cool"); + cstr_append(&s2, " stuff"); + + printf("%s %s\n", cstr_str(&s1), cstr_str(&s2)); +} + +// `c_with` is a general variant of `c_auto`: +c_with (cstr str = cstr_lit("Hello"), cstr_drop(&str)) +{ + cstr_append(&str, " world"); + printf("%s\n", cstr_str(&str)); +} + +// `c_scope` is like `c_with` but works with an already declared variable. +static pthread_mutex_t mut; +c_scope (pthread_mutex_lock(&mut), pthread_mutex_unlock(&mut)) +{ + /* Do syncronized work. */ +} + +// `c_defer` executes the expressions when leaving scope. Prefer c_with or c_scope. +cstr s1 = cstr_lit("Hello"), s2 = cstr_lit("world"); +c_defer (cstr_drop(&s1), cstr_drop(&s2)) +{ + printf("%s %s\n", cstr_str(&s1), cstr_str(&s2)); +} +``` +**Example**: Load each line of a text file into a vector of strings: +```c +#include +#include + +#define i_val_str +#include + +// receiver should check errno variable +cvec_str readFile(const char* name) +{ + cvec_str vec = cvec_str_init(); // returned + + c_with (FILE* fp = fopen(name, "r"), fp != NULL, fclose(fp)) + c_with (cstr line = cstr_NULL, cstr_drop(&line)) + while (cstr_getline(&line, fp)) + cvec_str_emplace_back(&vec, cstr_str(&line)); + return vec; +} + +int main() +{ + c_with (cvec_str x = readFile(__FILE__), cvec_str_drop(&x)) + c_foreach (i, cvec_str, x) + printf("%s\n", cstr_str(i.ref)); +} +``` + +### The **checkauto** utility program (for RAII) The **checkauto** program will check the source code for any misuses of the `c_auto*` macros which may lead to resource leakages. The `c_auto*`- macros are implemented as one-time executed **for-loops**, so any `return` or `break` appearing within such a block will lead to resource leaks, as it will disable diff --git a/docs/clist_api.md b/docs/clist_api.md index be6949ec..a1dbe105 100644 --- a/docs/clist_api.md +++ b/docs/clist_api.md @@ -193,21 +193,20 @@ Splice `[30, 40]` from *L2* into *L1* before `3`: #include int main() { - c_auto (clist_i, L1, L2) - { - L1 = c_make(clist_i, {1, 2, 3, 4, 5}); - L2 = c_make(clist_i, {10, 20, 30, 40, 50}); + clist_i L1 = c_make(clist_i, {1, 2, 3, 4, 5}); + clist_i L2 = c_make(clist_i, {10, 20, 30, 40, 50}); - clist_i_iter i = clist_i_advance(clist_i_begin(&L1), 2); - clist_i_iter j1 = clist_i_advance(clist_i_begin(&L2), 2), j2 = clist_i_advance(j1, 2); + clist_i_iter i = clist_i_advance(clist_i_begin(&L1), 2); + clist_i_iter j1 = clist_i_advance(clist_i_begin(&L2), 2), j2 = clist_i_advance(j1, 2); - clist_i_splice_range(&L1, i, &L2, j1, j2); + clist_i_splice_range(&L1, i, &L2, j1, j2); - c_foreach (i, clist_i, L1) - printf(" %d", *i.ref); puts(""); - c_foreach (i, clist_i, L2) - printf(" %d", *i.ref); puts(""); - } + c_foreach (i, clist_i, L1) + printf(" %d", *i.ref); puts(""); + c_foreach (i, clist_i, L2) + printf(" %d", *i.ref); puts(""); + + c_drop(clist_i, &L1, &L2); } ``` Output: diff --git a/docs/cmap_api.md b/docs/cmap_api.md index bf3dddcc..71e00265 100644 --- a/docs/cmap_api.md +++ b/docs/cmap_api.md @@ -125,27 +125,26 @@ bool c_memcmp_eq(const i_keyraw* a, const i_keyraw* b); // ! int main() { // Create an unordered_map of three strings (that map to strings) - c_auto (cmap_str, u) - { - u = c_make(cmap_str, { - {"RED", "#FF0000"}, - {"GREEN", "#00FF00"}, - {"BLUE", "#0000FF"} - }); - - // Iterate and print keys and values of unordered map - c_foreach (n, cmap_str, u) { - printf("Key:[%s] Value:[%s]\n", cstr_str(&n.ref->first), cstr_str(&n.ref->second)); - } - - // Add two new entries to the unordered map - cmap_str_emplace(&u, "BLACK", "#000000"); - cmap_str_emplace(&u, "WHITE", "#FFFFFF"); - - // Output values by key - printf("The HEX of color RED is:[%s]\n", cstr_str(cmap_str_at(&u, "RED"))); - printf("The HEX of color BLACK is:[%s]\n", cstr_str(cmap_str_at(&u, "BLACK"))); + cmap_str umap = c_make(cmap_str, { + {"RED", "#FF0000"}, + {"GREEN", "#00FF00"}, + {"BLUE", "#0000FF"} + }); + + // Iterate and print keys and values of unordered map + c_foreach (n, cmap_str, umap) { + printf("Key:[%s] Value:[%s]\n", cstr_str(&n.ref->first), cstr_str(&n.ref->second)); } + + // Add two new entries to the unordered map + cmap_str_emplace(&umap, "BLACK", "#000000"); + cmap_str_emplace(&umap, "WHITE", "#FFFFFF"); + + // Output values by key + printf("The HEX of color RED is:[%s]\n", cstr_str(cmap_str_at(&umap, "RED"))); + printf("The HEX of color BLACK is:[%s]\n", cstr_str(cmap_str_at(&umap, "BLACK"))); + + cmap_str_drop(&umap); } ``` Output: @@ -161,33 +160,33 @@ The HEX of color BLACK is:[#000000] This example uses a cmap with cstr as mapped value. ```c #include - +#define i_type IDMap #define i_key int #define i_val_str -#define i_tag id #include int main() { uint32_t col = 0xcc7744ff; - c_auto (cmap_id, idnames) - { - c_forlist (i, cmap_id_raw, { {100, "Red"}, {110, "Blue"} }) - cmap_id_emplace(&idnames, c_PAIR(i.ref)); - - // replace existing mapped value: - cmap_id_emplace_or_assign(&idnames, 110, "White"); - - // insert a new constructed mapped string into map: - cmap_id_insert_or_assign(&idnames, 120, cstr_from_fmt("#%08x", col)); - - // emplace/insert does nothing if key already exist: - cmap_id_emplace(&idnames, 100, "Green"); - - c_foreach (i, cmap_id, idnames) - printf("%d: %s\n", i.ref->first, cstr_str(&i.ref->second)); - } + IDMap idnames = {0}; + + c_forlist (i, IDMap_raw, { {100, "Red"}, {110, "Blue"} }) + IDMap_emplace(&idnames, i.ref->first, i.ref->second); + + // replace existing mapped value: + IDMap_emplace_or_assign(&idnames, 110, "White"); + + // insert a new constructed mapped string into map: + IDMap_insert_or_assign(&idnames, 120, cstr_from_fmt("#%08x", col)); + + // emplace/insert does nothing if key already exist: + IDMap_emplace(&idnames, 100, "Green"); + + c_foreach (i, IDMap, idnames) + printf("%d: %s\n", i.ref->first, cstr_str(&i.ref->second)); + + IDMap_drop(&idnames); } ``` Output: @@ -212,16 +211,17 @@ typedef struct { int x, y, z; } Vec3i; int main() { // Define map with defered destruct - c_with (cmap_vi vecs = cmap_vi_init(), cmap_vi_drop(&vecs)) - { - cmap_vi_insert(&vecs, (Vec3i){100, 0, 0}, 1); - cmap_vi_insert(&vecs, (Vec3i){ 0, 100, 0}, 2); - cmap_vi_insert(&vecs, (Vec3i){ 0, 0, 100}, 3); - cmap_vi_insert(&vecs, (Vec3i){100, 100, 100}, 4); - - c_forpair (v3, num, cmap_vi, vecs) - printf("{ %3d, %3d, %3d }: %d\n", _.v3->x, _.v3->y, _.v3->z, *_.num); - } + cmap_vi vecs = {0}; + + cmap_vi_insert(&vecs, (Vec3i){100, 0, 0}, 1); + cmap_vi_insert(&vecs, (Vec3i){ 0, 100, 0}, 2); + cmap_vi_insert(&vecs, (Vec3i){ 0, 0, 100}, 3); + cmap_vi_insert(&vecs, (Vec3i){100, 100, 100}, 4); + + c_forpair (v3, num, cmap_vi, vecs) + printf("{ %3d, %3d, %3d }: %d\n", _.v3->x, _.v3->y, _.v3->z, *_.num); + + cmap_vi_drop(&vecs); } ``` Output: @@ -245,16 +245,17 @@ typedef struct { int x, y, z; } Vec3i; int main() { - c_auto (cmap_iv, vecs) // shorthand for c_with with _init(), _drop(). - { - cmap_iv_insert(&vecs, 1, (Vec3i){100, 0, 0}); - cmap_iv_insert(&vecs, 2, (Vec3i){ 0, 100, 0}); - cmap_iv_insert(&vecs, 3, (Vec3i){ 0, 0, 100}); - cmap_iv_insert(&vecs, 4, (Vec3i){100, 100, 100}); - - c_forpair (num, v3, cmap_iv, vecs) - printf("%d: { %3d, %3d, %3d }\n", *_.num, _.v3->x, _.v3->y, _.v3->z); - } + cmap_iv vecs = {0} + + cmap_iv_insert(&vecs, 1, (Vec3i){100, 0, 0}); + cmap_iv_insert(&vecs, 2, (Vec3i){ 0, 100, 0}); + cmap_iv_insert(&vecs, 3, (Vec3i){ 0, 0, 100}); + cmap_iv_insert(&vecs, 4, (Vec3i){100, 100, 100}); + + c_forpair (num, v3, cmap_iv, vecs) + printf("%d: { %3d, %3d, %3d }\n", *_.num, _.v3->x, _.v3->y, _.v3->z); + + cmap_iv_drop(&vecs); } ``` Output: @@ -300,35 +301,27 @@ static inline void Viking_drop(Viking* vk) { #define i_type Vikings #define i_keyclass Viking #define i_val int -/* - i_keyclass implies these defines, unless they are already defined: - #define i_cmp Viking_cmp - #define i_hash Viking_hash - #define i_keyclone Viking_clone - #define i_keydrop Viking_drop -*/ #include int main() { // Use a HashMap to store the vikings' health points. - c_auto (Vikings, vikings) // uses Vikings_init(), Vikings_drop() - { - Vikings_insert(&vikings, (Viking){cstr_lit("Einar"), cstr_lit("Norway")}, 25); - Vikings_insert(&vikings, (Viking){cstr_lit("Olaf"), cstr_lit("Denmark")}, 24); - Vikings_insert(&vikings, (Viking){cstr_lit("Harald"), cstr_lit("Iceland")}, 12); - Vikings_insert(&vikings, (Viking){cstr_lit("Einar"), cstr_lit("Denmark")}, 21); - - c_auto (Viking, lookup) { - lookup = (Viking){cstr_lit("Einar"), cstr_lit("Norway")}; - printf("Lookup: Einar of Norway has %d hp\n\n", *Vikings_at(&vikings, lookup)); - } - - // Print the status of the vikings. - c_forpair (vik, hp, Vikings, vikings) { - printf("%s of %s has %d hp\n", cstr_str(&_.vik->name), cstr_str(&_.vik->country), *_.hp); - } + Vikings vikings = {0}; + + Vikings_insert(&vikings, (Viking){cstr_lit("Einar"), cstr_lit("Norway")}, 25); + Vikings_insert(&vikings, (Viking){cstr_lit("Olaf"), cstr_lit("Denmark")}, 24); + Vikings_insert(&vikings, (Viking){cstr_lit("Harald"), cstr_lit("Iceland")}, 12); + Vikings_insert(&vikings, (Viking){cstr_lit("Einar"), cstr_lit("Denmark")}, 21); + + Viking lookup = (Viking){cstr_lit("Einar"), cstr_lit("Norway")}; + printf("Lookup: Einar of Norway has %d hp\n\n", *Vikings_at(&vikings, lookup)); + Viking_drop(&lookup); + + // Print the status of the vikings. + c_forpair (vik, hp, Vikings, vikings) { + printf("%s of %s has %d hp\n", cstr_str(&_.vik->name), cstr_str(&_.vik->country), *_.hp); } + Vikings_drop(&vikings); } ``` Output: @@ -384,30 +377,22 @@ static inline RViking Viking_toraw(const Viking* vp) { #define i_hash(rp) (cstrhash(rp->name) ^ cstrhash(rp->country)) #define i_val int #include -/* - i_keyclass implies these defines, unless they are already defined: - #define i_cmp RViking_cmp - //#define i_hash RViking_hash // already defined above. - //#define i_keyclone Viking_clone // not used because c_no_clone - #define i_keyto Viking_toraw // because i_keyraw type is defined - #define i_keydrop Viking_drop -*/ int main() { - c_auto (Vikings, vikings) - { - Vikings_emplace(&vikings, (RViking){"Einar", "Norway"}, 20); - Vikings_emplace(&vikings, (RViking){"Olaf", "Denmark"}, 24); - Vikings_emplace(&vikings, (RViking){"Harald", "Iceland"}, 12); - Vikings_emplace(&vikings, (RViking){"Björn", "Sweden"}, 10); - - Vikings_value *v = Vikings_get_mut(&vikings, (RViking){"Einar", "Norway"}); - if (v) v->second += 3; // add 3 hp points to Einar - - c_forpair (vk, hp, Vikings, vikings) { - printf("%s of %s has %d hp\n", cstr_str(&_.vk->name), cstr_str(&_.vk->country), *_.hp); - } + Vikings vikings = {0}; + + Vikings_emplace(&vikings, (RViking){"Einar", "Norway"}, 20); + Vikings_emplace(&vikings, (RViking){"Olaf", "Denmark"}, 24); + Vikings_emplace(&vikings, (RViking){"Harald", "Iceland"}, 12); + Vikings_emplace(&vikings, (RViking){"Björn", "Sweden"}, 10); + + Vikings_value *v = Vikings_get_mut(&vikings, (RViking){"Einar", "Norway"}); + if (v) v->second += 3; // add 3 hp points to Einar + + c_forpair (vk, hp, Vikings, vikings) { + printf("%s of %s has %d hp\n", cstr_str(&_.vk->name), cstr_str(&_.vk->country), *_.hp); } + Vikings_drop(&vikings); } ``` diff --git a/docs/coption_api.md b/docs/coption_api.md index be0d0978..1e85ac2a 100644 --- a/docs/coption_api.md +++ b/docs/coption_api.md @@ -65,7 +65,5 @@ int main(int argc, char *argv[]) { for (int i = opt.ind; i < argc; ++i) printf(" %s", argv[i]); putchar('\n'); -} - return 0; } ``` diff --git a/docs/cpque_api.md b/docs/cpque_api.md index 991623d7..b3a1a9ec 100644 --- a/docs/cpque_api.md +++ b/docs/cpque_api.md @@ -75,24 +75,24 @@ int main() stc64_t rng = stc64_new(1234); stc64_uniform_t dist = stc64_uniform_new(0, N * 10); - // Declare heap, with defered drop() - c_auto (cpque_i, heap) - { - // Push ten million random numbers to priority queue. - c_forrange (N) - cpque_i_push(&heap, stc64_uniform(&rng, &dist)); - - // Add some negative ones. - int nums[] = {-231, -32, -873, -4, -343}; - c_forrange (i, c_ARRAYLEN(nums)) - cpque_i_push(&heap, nums[i]); - - // Extract and display the fifty smallest. - c_forrange (50) { - printf("%" PRId64 " ", *cpque_i_top(&heap)); - cpque_i_pop(&heap); - } + // Define heap + cpque_i heap = {0}; + + // Push ten million random numbers to priority queue. + c_forrange (N) + cpque_i_push(&heap, stc64_uniform(&rng, &dist)); + + // Add some negative ones. + int nums[] = {-231, -32, -873, -4, -343}; + c_forrange (i, c_ARRAYLEN(nums)) + cpque_i_push(&heap, nums[i]); + + // Extract and display the fifty smallest. + c_forrange (50) { + printf("%" PRId64 " ", *cpque_i_top(&heap)); + cpque_i_pop(&heap); } + cpque_i_drop(&heap); } ``` Output: diff --git a/docs/cset_api.md b/docs/cset_api.md index 287f7636..a0af357f 100644 --- a/docs/cset_api.md +++ b/docs/cset_api.md @@ -80,37 +80,35 @@ cset_X_value cset_X_value_clone(cset_X_value val); ## Example ```c #include - +#define i_type Strset #define i_key_str #include int main () { - c_auto (cset_str, fifth) - { - c_auto (cset_str, first, second) - c_auto (cset_str, third, fourth) - { - second = c_make(cset_str, {"red", "green", "blue"}); - - c_forlist (i, const char*, {"orange", "pink", "yellow"}) - cset_str_emplace(&third, *i.ref); - - cset_str_emplace(&fourth, "potatoes"); - cset_str_emplace(&fourth, "milk"); - cset_str_emplace(&fourth, "flour"); - - fifth = cset_str_clone(second); - c_foreach (i, cset_str, third) - cset_str_emplace(&fifth, cstr_str(i.ref)); - - c_foreach (i, cset_str, fourth) - cset_str_emplace(&fifth, cstr_str(i.ref)); - } - printf("fifth contains:\n\n"); - c_foreach (i, cset_str, fifth) - printf("%s\n", cstr_str(i.ref)); - } + Strset first, second={0}, third={0}, fourth={0}, fifth; + + first = c_make(Strset, {"red", "green", "blue"}); + fifth = Strset_clone(second); + + c_forlist (i, const char*, {"orange", "pink", "yellow"}) + Strset_emplace(&third, *i.ref); + + c_foreach (i, Strset, third) + Strset_insert(&fifth, cstr_clone(*i.ref)); + + Strset_emplace(&fourth, "potatoes"); + Strset_emplace(&fourth, "milk"); + Strset_emplace(&fourth, "flour"); + + c_foreach (i, Strset, fourth) + Strset_emplace(&fifth, cstr_str(i.ref)); + + printf("fifth contains:\n\n"); + c_foreach (i, Strset, fifth) + printf("%s\n", cstr_str(i.ref)); + + c_drop(Strset, &first, &second, &third, &fourth, &fifth); } ``` Output: diff --git a/docs/csmap_api.md b/docs/csmap_api.md index 8e5780e3..da0e6915 100644 --- a/docs/csmap_api.md +++ b/docs/csmap_api.md @@ -112,27 +112,26 @@ csmap_X_raw csmap_X_value_toraw(csmap_X_value* pval); int main() { // Create a sorted map of three strings (maps to string) - c_auto (csmap_str, colors) // RAII - { - colors = c_make(csmap_str, { - {"RED", "#FF0000"}, - {"GREEN", "#00FF00"}, - {"BLUE", "#0000FF"} - }); - - // Iterate and print keys and values of sorted map - c_foreach (i, csmap_str, colors) { - printf("Key:[%s] Value:[%s]\n", cstr_str(&i.ref->first), cstr_str(&i.ref->second)); - } - - // Add two new entries to the sorted map - csmap_str_emplace(&colors, "BLACK", "#000000"); - csmap_str_emplace(&colors, "WHITE", "#FFFFFF"); - - // Output values by key - printf("The HEX of color RED is:[%s]\n", cstr_str(csmap_str_at(&colors, "RED"))); - printf("The HEX of color BLACK is:[%s]\n", cstr_str(csmap_str_at(&colors, "BLACK"))); + csmap_str colors = c_make(csmap_str, { + {"RED", "#FF0000"}, + {"GREEN", "#00FF00"}, + {"BLUE", "#0000FF"} + }); + + // Iterate and print keys and values of sorted map + c_foreach (i, csmap_str, colors) { + printf("Key:[%s] Value:[%s]\n", cstr_str(&i.ref->first), cstr_str(&i.ref->second)); } + + // Add two new entries to the sorted map + csmap_str_emplace(&colors, "BLACK", "#000000"); + csmap_str_emplace(&colors, "WHITE", "#FFFFFF"); + + // Output values by key + printf("The HEX of color RED is:[%s]\n", cstr_str(csmap_str_at(&colors, "RED"))); + printf("The HEX of color BLACK is:[%s]\n", cstr_str(csmap_str_at(&colors, "BLACK"))); + + csmap_str_drop(&colors); } ``` Output: @@ -149,32 +148,29 @@ This example uses a csmap with cstr as mapped value. ```c #include +#define i_type IDSMap #define i_key int #define i_val_str -#define i_tag id #include int main() { uint32_t col = 0xcc7744ff; - csmap_id idnames = csmap_id_init(); - c_defer (csmap_id_drop(&idnames)) - { - c_forlist (i, csmap_id_raw, { {100, "Red"}, {110, "Blue"} }) - csmap_id_emplace(&idnames, c_PAIR(i.ref)); + IDSMap idnames = c_make(IDSMap, { {100, "Red"}, {110, "Blue"} }); - // put replaces existing mapped value: - csmap_id_emplace_or_assign(&idnames, 110, "White"); + // Assign/overwrite an existing mapped value with a const char* + IDSMap_emplace_or_assign(&idnames, 110, "White"); - // put a constructed mapped value into map: - csmap_id_insert_or_assign(&idnames, 120, cstr_from_fmt("#%08x", col)); + // Insert (or assign) a new cstr + IDSMap_insert_or_assign(&idnames, 120, cstr_from_fmt("#%08x", col)); - // emplace adds only when key does not exist: - csmap_id_emplace(&idnames, 100, "Green"); + // emplace() adds only when key does not already exist: + IDSMap_emplace(&idnames, 100, "Green"); // ignored - c_foreach (i, csmap_id, idnames) - printf("%d: %s\n", i.ref->first, cstr_str(&i.ref->second)); - } + c_foreach (i, IDSMap, idnames) + printf("%d: %s\n", i.ref->first, cstr_str(&i.ref->second)); + + IDSMap_drop(&idnames); } ``` Output: @@ -205,16 +201,17 @@ static int Vec3i_cmp(const Vec3i* a, const Vec3i* b) { int main() { - c_auto (csmap_vi, vecs) - { - csmap_vi_insert(&vecs, (Vec3i){100, 0, 0}, 1); - csmap_vi_insert(&vecs, (Vec3i){0, 100, 0}, 2); - csmap_vi_insert(&vecs, (Vec3i){0, 0, 100}, 3); - csmap_vi_insert(&vecs, (Vec3i){100, 100, 100}, 4); - - c_foreach (i, csmap_vi, vecs) - printf("{ %3d, %3d, %3d }: %d\n", i.ref->first.x, i.ref->first.y, i.ref->first.z, i.ref->second); - } + csmap_vi vmap = {0}; + + csmap_vi_insert(&vmap, (Vec3i){100, 0, 0}, 1); + csmap_vi_insert(&vmap, (Vec3i){0, 100, 0}, 2); + csmap_vi_insert(&vmap, (Vec3i){0, 0, 100}, 3); + csmap_vi_insert(&vmap, (Vec3i){100, 100, 100}, 4); + + c_forpair (v, n, csmap_vi, vmap) + printf("{ %3d, %3d, %3d }: %d\n", _.v->x, _.v->y, _.v->z, *_.n); + + csmap_vi_drop(&vmap) } ``` Output: @@ -238,17 +235,17 @@ typedef struct { int x, y, z; } Vec3i; int main() { - // equivalent to: c_auto (csmap_iv, vecs) - c_with (csmap_iv vecs = csmap_iv_init(), csmap_iv_drop(&vecs)) - { - csmap_iv_insert(&vecs, 1, (Vec3i){100, 0, 0}); - csmap_iv_insert(&vecs, 2, (Vec3i){0, 100, 0}); - csmap_iv_insert(&vecs, 3, (Vec3i){0, 0, 100}); - csmap_iv_insert(&vecs, 4, (Vec3i){100, 100, 100}); - - c_foreach (i, csmap_iv, vecs) - printf("%d: { %3d, %3d, %3d }\n", i.ref->first, i.ref->second.x, i.ref->second.y, i.ref->second.z); - } + csmap_iv imap = {0}; + + csmap_iv_insert(&imap, 1, (Vec3i){100, 0, 0}); + csmap_iv_insert(&imap, 2, (Vec3i){0, 100, 0}); + csmap_iv_insert(&imap, 3, (Vec3i){0, 0, 100}); + csmap_iv_insert(&imap, 4, (Vec3i){100, 100, 100}); + + c_forpair (n, v, csmap_iv, imap) + printf("%d: { %3d, %3d, %3d }\n", *_.n, _.v->x, _.v->y, _.v->z); + + csmap_iv_drop(&imap); } ``` Output: diff --git a/docs/cspan_api.md b/docs/cspan_api.md index 1e60a526..10565b0f 100644 --- a/docs/cspan_api.md +++ b/docs/cspan_api.md @@ -136,7 +136,7 @@ int main() { ## Example 2 Slicing cspan without and with reducing the rank: ```c -#include +#include #include using_cspan3(Span, int); // Shorthand to define Span, Span2, and Span3 @@ -154,7 +154,7 @@ int main() puts("\niterate span2 flat:"); c_foreach (i, Span2, span2) - fmt_print(" {}", *i.ref); + print(" {}", *i.ref); puts(""); // slice without reducing rank: @@ -164,26 +164,23 @@ int main() c_forrange (i, ss3.shape[0]) { c_forrange (j, ss3.shape[1]) { c_forrange (k, ss3.shape[2]) - fmt_print(" {:2}", *cspan_at(&ss3, i, j, k)); - fmt_print(" |"); + print(" {:2}", *cspan_at(&ss3, i, j, k)); + print(" |"); } - puts(""); } // slice and reduce rank: Span2 ss2 = cspan_slice(Span2, &span3, {c_ALL}, {3}, {c_ALL}); puts("\niterate ss2 by dimensions:"); c_forrange (i, ss2.shape[0]) { - c_forrange (j, ss2.shape[1]) { - fmt_print(" {:2}", *cspan_at(&ss2, i, j)); - fmt_print(" |"); - } - puts(""); + c_forrange (j, ss2.shape[1]) + print(" {:2}", *cspan_at(&ss2, i, j)); + print(" |"); } puts("\niterate ss2 flat:"); c_foreach (i, Span2, ss2) - fmt_print(" {:2}", *i.ref); + print(" {:2}", *i.ref); puts(""); } ``` diff --git a/docs/csset_api.md b/docs/csset_api.md index 80ee1844..0989fd9b 100644 --- a/docs/csset_api.md +++ b/docs/csset_api.md @@ -80,33 +80,38 @@ csset_X_value csset_X_value_clone(csset_X_value val); ```c #include +#define i_type SSet #define i_key_str #include int main () { - c_auto (csset_str, first, second, third) - c_auto (csset_str, fourth, fifth) - { - second = c_make(csset_str, {"red", "green", "blue"}); - - c_forlist (i, const char*, {"orange", "pink", "yellow"}) - csset_str_emplace(&third, *i.ref); - - csset_str_emplace(&fourth, "potatoes"); - csset_str_emplace(&fourth, "milk"); - csset_str_emplace(&fourth, "flour"); - - fifth = csset_str_clone(second); - c_foreach (i, csset_str, third) - csset_str_emplace(&fifth, cstr_str(i.ref)); - c_foreach (i, csset_str, fourth) - csset_str_emplace(&fifth, cstr_str(i.ref)); - - printf("fifth contains:\n\n"); - c_foreach (i, csset_str, fifth) - printf("%s\n", cstr_str(i.ref)); - } + SSet second={0}, third={0}, fourth={0}, fifth={0}; + + second = c_make(SSet, {"red", "green", "blue"}); + + c_forlist (i, const char*, {"orange", "pink", "yellow"}) + SSet_emplace(&third, *i.ref); + + SSet_emplace(&fourth, "potatoes"); + SSet_emplace(&fourth, "milk"); + SSet_emplace(&fourth, "flour"); + + // Copy all to fifth: + + fifth = SSet_clone(second); + + c_foreach (i, SSet, third) + SSet_emplace(&fifth, cstr_str(i.ref)); + + c_foreach (i, SSet, fourth) + SSet_emplace(&fifth, cstr_str(i.ref)); + + printf("fifth contains:\n\n"); + c_foreach (i, SSet, fifth) + printf("%s\n", cstr_str(i.ref)); + + c_drop(SSet, &second, &third, &fourth, &fifth); } ``` Output: diff --git a/docs/cstr_api.md b/docs/cstr_api.md index 64099675..64ad002c 100644 --- a/docs/cstr_api.md +++ b/docs/cstr_api.md @@ -160,7 +160,12 @@ char* cstrnstrn(const char* str, const char* search, intptr_t slen, intpt #include int main() { - c_auto (cstr, s0, s1, full_path) { + cstr s0, s1, full_path; + c_defer( + cstr_drop(&s0), + cstr_drop(&s1), + cstr_drop(&full_path) + ){ s0 = cstr_lit("Initialization without using strlen()."); printf("%s\nLength: %" c_ZI "\n\n", cstr_str(&s0), cstr_size(&s0)); diff --git a/docs/csview_api.md b/docs/csview_api.md index 33e61f0e..ec3bf121 100644 --- a/docs/csview_api.md +++ b/docs/csview_api.md @@ -151,14 +151,15 @@ red Apples int main() { - c_auto (cstr, s1) { - s1 = cstr_lit("hell😀 w😀rld"); - cstr_u8_replace_at(&s1, cstr_find(&s1, "😀rld"), 1, c_sv("ø")); - printf("%s\n", cstr_str(&s1)); - - c_foreach (i, cstr, s1) - printf("%.*s,", c_SV(i.u8.chr)); - } + cstr s1 = cstr_lit("hell😀 w😀rld"); + + cstr_u8_replace_at(&s1, cstr_find(&s1, "😀rld"), 1, c_sv("ø")); + printf("%s\n", cstr_str(&s1)); + + c_foreach (i, cstr, s1) + printf("%.*s,", c_SV(i.u8.chr)); + + cstr_drop(&s1); } ``` Output: @@ -178,6 +179,7 @@ void print_split(csview input, const char* sep) { c_fortoken_sv (i, input, sep) printf("[%.*s]\n", c_SV(i.token)); + puts(""); } #include @@ -197,13 +199,15 @@ cstack_str string_split(csview input, const char* sep) int main() { print_split(c_sv("//This is a//double-slash//separated//string"), "//"); - puts(""); print_split(c_sv("This has no matching separator"), "xx"); + + cstack_str s = string_split(c_sv("Split,this,,string,now,"), ","); + + c_foreach (i, cstack_str, s) + printf("[%s]\n", cstr_str(i.ref)); puts(""); - c_with (cstack_str s = string_split(c_sv("Split,this,,string,now,"), ","), cstack_str_drop(&s)) - c_foreach (i, cstack_str, s) - printf("[%s]\n", cstr_str(i.ref)); + cstack_str_drop(&s); } ``` Output: diff --git a/docs/cvec_api.md b/docs/cvec_api.md index 68e08cb2..5879bc1f 100644 --- a/docs/cvec_api.md +++ b/docs/cvec_api.md @@ -119,29 +119,29 @@ cvec_X_value cvec_X_value_clone(cvec_X_value val); int main() { // Create a vector containing integers - c_auto (cvec_int, vec) - { - // Add two integers to vector - cvec_int_push(&vec, 25); - cvec_int_push(&vec, 13); - - // Append a set of numbers - c_forlist (i, int, {7, 5, 16, 8}) - cvec_int_push(&vec, *i.ref); - - printf("initial:"); - c_foreach (k, cvec_int, vec) { - printf(" %d", *k.ref); - } - - // Sort the vector - cvec_int_sort(&vec); - - printf("\nsorted:"); - c_foreach (k, cvec_int, vec) { - printf(" %d", *k.ref); - } + cvec_int vec = {0}; + + // Add two integers to vector + cvec_int_push(&vec, 25); + cvec_int_push(&vec, 13); + + // Append a set of numbers + c_forlist (i, int, {7, 5, 16, 8}) + cvec_int_push(&vec, *i.ref); + + printf("initial:"); + c_foreach (k, cvec_int, vec) { + printf(" %d", *k.ref); + } + + // Sort the vector + cvec_int_sort(&vec); + + printf("\nsorted:"); + c_foreach (k, cvec_int, vec) { + printf(" %d", *k.ref); } + cvec_int_drop(&vec); } ``` Output: @@ -212,7 +212,7 @@ User User_clone(User user) { #include int main(void) { - UVec vec = UVec_init(); + UVec vec = {0}; UVec_push(&vec, (User){cstr_lit("mary"), 0}); UVec_push(&vec, (User){cstr_lit("joe"), 1}); UVec_push(&vec, (User){cstr_lit("admin"), 2}); -- cgit v1.2.3 From 26cd0a73422cdbcd4998170e179fa0f3ce48e9a5 Mon Sep 17 00:00:00 2001 From: Tyge Løvset Date: Mon, 27 Mar 2023 20:17:57 +0200 Subject: Some missing files. --- README.md | 98 ++++++++++++++++++----------------------------- docs/ccommon_api.md | 14 ++++--- docs/cpque_api.md | 2 +- include/stc/algo/crange.h | 4 +- include/stc/cspan.h | 8 ++-- src/utf8code.c | 2 +- 6 files changed, 55 insertions(+), 73 deletions(-) (limited to 'docs') diff --git a/README.md b/README.md index 8d816bc6..75a5357e 100644 --- a/README.md +++ b/README.md @@ -165,13 +165,13 @@ int main(void) Floats_sort(&nums); - c_foreach (i, Floats, nums) // Alternative way to iterate nums. + c_foreach (i, Floats, nums) // Alternative and recommended way to iterate nums. printf(" %g", *i.ref); // i.ref is a pointer to the current element. Floats_drop(&nums); // cleanup memory } ``` -To switch to a different container type is easy when using `c_foreach`: +Switching to a different container type is easy: ```c #define i_type Floats #define i_val float @@ -185,7 +185,7 @@ int main() Floats_push(&nums, 40.f); // print the sorted numbers - c_foreach (i, Floats, nums) + c_foreach (i, Floats, nums) // c_foreach works with any container printf(" %g", *i.ref); Floats_drop(&nums); @@ -197,21 +197,22 @@ only works for integral types. *Alternatively, `#define i_opt c_no_cmp` to disab Let's make a vector of vectors that can be cloned. All of its element vectors will be destroyed when destroying the Vec2D. ```c +#include + #define i_type Vec #define i_val float #include -#include #define i_type Vec2D -#define i_valclass Vec // Use i_valclass when element type has "member" functions Vec_clone(), Vec_drop() and Vec_cmp(). -#define i_opt c_no_cmp // Disable search/sort for Vec2D because Vec_cmp() is not defined. +#define i_valclass Vec // Use i_valclass when the element type has "member" functions _clone(), _drop() and _cmp(). +#define i_opt c_no_cmp // However, disable search/sort for Vec2D because Vec_cmp() is not defined. #include int main(void) { Vec* v; Vec2D vec = {0}; // All containers in STC can be initialized with {0}. - v = Vec2D_push(&vec, Vec_init()); // Returns a pointer to the new element in vec. + v = Vec2D_push(&vec, Vec_init()); // push() returns a pointer to the new element in vec. Vec_push(v, 10.f); Vec_push(v, 20.f); @@ -228,84 +229,69 @@ int main(void) c_drop(Vec2D, &vec, &clone); // Cleanup all (6) vectors. } ``` -Here is an example of using six different container types: +Here is an example of using four different container types: ```c #include -#include - -struct Point { float x, y; }; #define i_key int #include // cset_int: unordered set +struct Point { float x, y; }; +// Define cvec_pnt with a less-comparison function for Point. #define i_val struct Point -// Define a i_less template parameter (alternative to i_cmp) for Point. #define i_less(a, b) a->x < b->x || (a->x == b->x && a->y < b->y) #define i_tag pnt #include // cvec_pnt: vector of struct Point -#define i_val int -#include // cdeq_int: deque of int - #define i_val int #include // clist_int: singly linked list -#define i_val int -#include // cstack_int - #define i_key int #define i_val int #include // csmap_int: sorted map int => int int main(void) { - // Define six empty containers + // Define four empty containers cset_int set = {0}; cvec_pnt vec = {0}; - cdeq_int deq = {0}; clist_int lst = {0}; - cstack_int stk = {0}; csmap_int map = {0}; - c_defer( // Drop the containers after the scope is executed + c_defer( // Drop the containers at scope exit cset_int_drop(&set), cvec_pnt_drop(&vec), - cdeq_int_drop(&deq), clist_int_drop(&lst), - cstack_int_drop(&stk), csmap_int_drop(&map) ){ - int nums[4] = {10, 20, 30, 40}; - struct Point pts[4] = { {10, 1}, {20, 2}, {30, 3}, {40, 4} }; - int pairs[4][2] = { {20, 2}, {10, 1}, {30, 3}, {40, 4} }; + enum{N = 5}; + int nums[N] = {10, 20, 30, 40, 50}; + struct Point pts[N] = { {10, 1}, {20, 2}, {30, 3}, {40, 4}, {50, 5} }; + int pairs[N][2] = { {20, 2}, {10, 1}, {30, 3}, {40, 4}, {50, 5} }; // Add some elements to each container - for (int i = 0; i < 4; ++i) { + for (int i = 0; i < N; ++i) { cset_int_insert(&set, nums[i]); cvec_pnt_push(&vec, pts[i]); - cdeq_int_push_back(&deq, nums[i]); clist_int_push_back(&lst, nums[i]); - cstack_int_push(&stk, nums[i]); csmap_int_insert(&map, pairs[i][0], pairs[i][1]); } - // Find an element in each container (except cstack) + // Find an element in each container cset_int_iter i1 = cset_int_find(&set, 20); cvec_pnt_iter i2 = cvec_pnt_find(&vec, (struct Point){20, 2}); - cdeq_int_iter i3 = cdeq_int_find(&deq, 20); - clist_int_iter i4 = clist_int_find(&lst, 20); - csmap_int_iter i5 = csmap_int_find(&map, 20); + clist_int_iter i3 = clist_int_find(&lst, 20); + csmap_int_iter i4 = csmap_int_find(&map, 20); - printf("\nFound: %d, (%g, %g), %d, %d, [%d: %d]\n", - *i1.ref, i2.ref->x, i2.ref->y, *i3.ref, - *i4.ref, i5.ref->first, i5.ref->second); + printf("\nFound: %d, (%g, %g), %d, [%d: %d]\n", + *i1.ref, i2.ref->x, i2.ref->y, *i3.ref, + i4.ref->first, i4.ref->second); // Erase all the elements found cset_int_erase_at(&set, i1); cvec_pnt_erase_at(&vec, i2); - cdeq_int_erase_at(&deq, i3); - clist_int_erase_at(&lst, i4); - csmap_int_erase_at(&map, i5); + clist_int_erase_at(&lst, i3); + csmap_int_erase_at(&map, i4); printf("After erasing the elements found:"); printf("\n set:"); @@ -316,18 +302,10 @@ int main(void) c_foreach (i, cvec_pnt, vec) printf(" (%g, %g)", i.ref->x, i.ref->y); - printf("\n deq:"); - c_foreach (i, cdeq_int, deq) - printf(" %d", *i.ref); - printf("\n lst:"); c_foreach (i, clist_int, lst) printf(" %d", *i.ref); - printf("\n stk:"); - c_foreach (i, cstack_int, stk) - printf(" %d", *i.ref); - printf("\n map:"); c_foreach (i, csmap_int, map) printf(" [%d: %d]", i.ref->first, i.ref->second); @@ -337,14 +315,12 @@ int main(void) Output ``` -Found: 20, (20, 2), 20, 20, [20: 2] -After erasing elements found: - set: 10 30 40 - vec: (10, 1) (30, 3) (40, 4) - deq: 5 10 30 - lst: 5 10 30 - stk: 10 20 30 40 - map: [10: 1] [30: 3] [40: 4] +Found: 20, (20, 2), 20, [20: 2] +After erasing the elements found: + set: 40 10 30 50 + vec: (10, 1) (30, 3) (40, 4) (50, 5) + lst: 10 30 40 50 + map: [10: 1] [30: 3] [40: 4] [50: 5] ``` Installation @@ -480,12 +456,12 @@ cvec_str vec = {0}; cstr s = cstr_lit("a string literal"); const char* hello = "Hello"; -cvec_str_push_back(&vec, cstr_from(hello); // construct and add string from const char* -cvec_str_push_back(&vec, cstr_clone(s)); // clone and append a cstr +cvec_str_push(&vec, cstr_from(hello); // make a cstr from const char* and move it onto vec +cvec_str_push(&vec, cstr_clone(s)); // make a cstr clone and move it onto vec -cvec_str_emplace_back(&vec, "Yay, literal"); // internally constructs cstr from const char* -cvec_str_emplace_back(&vec, cstr_clone(s)); // <-- COMPILE ERROR: expects const char* -cvec_str_emplace_back(&vec, cstr_str(&s)); // Ok: const char* input type. +cvec_str_emplace(&vec, "Yay, literal"); // internally make a cstr from const char* +cvec_str_emplace(&vec, cstr_clone(s)); // <-- COMPILE ERROR: expects const char* +cvec_str_emplace(&vec, cstr_str(&s)); // Ok: const char* input type. cstr_drop(&s) cvec_str_drop(&vec); diff --git a/docs/ccommon_api.md b/docs/ccommon_api.md index 010ea204..53ba096a 100644 --- a/docs/ccommon_api.md +++ b/docs/ccommon_api.md @@ -113,8 +113,10 @@ int main() { isPrime(*i.ref) && c_flt_skip(i, 24) && c_flt_count(i) % 15 == 1 && - c_flt_take(i, 10)) + c_flt_take(i, 10) + ){ printf(" %lld", *i.ref); + } puts(""); } // out: 1171 1283 1409 1493 1607 1721 1847 1973 2081 2203 @@ -126,7 +128,7 @@ Note that `c_flt_take()` and `c_flt_takewhile()`breaks the loop on false. ### crange 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_obj(...) // create a compound literal crange object +crange& crange_object(...) // create a compound literal crange object 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 @@ -144,10 +146,12 @@ c_forfilter (i, crange, r1, isPrime(*i.ref)) // 2. The 11 first primes: printf("2"); -c_forfilter (i, crange, crange_obj(3, INT64_MAX, 2), - isPrime(*i.ref) && - c_flt_take(10)) +c_forfilter (i, crange, crange_object(3, INT64_MAX, 2), + isPrime(*i.ref) && + c_flt_take(10) +){ printf(" %lld", *i.ref); +} // 2 3 5 7 11 13 17 19 23 29 31 ``` ## Algorithms diff --git a/docs/cpque_api.md b/docs/cpque_api.md index b3a1a9ec..134a920f 100644 --- a/docs/cpque_api.md +++ b/docs/cpque_api.md @@ -84,7 +84,7 @@ int main() // Add some negative ones. int nums[] = {-231, -32, -873, -4, -343}; - c_forrange (i, c_ARRAYLEN(nums)) + c_forrange (i, c_arraylen(nums)) cpque_i_push(&heap, nums[i]); // Extract and display the fifty smallest. diff --git a/include/stc/algo/crange.h b/include/stc/algo/crange.h index ca06c258..91ffdd56 100644 --- a/include/stc/algo/crange.h +++ b/include/stc/algo/crange.h @@ -34,7 +34,7 @@ int main() // use a temporary crange object. int a = 100, b = INT32_MAX; - c_forfilter (i, crange, crange_obj(a, b, 8), + c_forfilter (i, crange, crange_object(a, b, 8), c_flt_skip(i, 10) && c_flt_take(i, 3)) printf(" %lld", *i.ref); @@ -46,7 +46,7 @@ int main() #include -#define crange_obj(...) \ +#define crange_object(...) \ (*(crange[]){crange_make(__VA_ARGS__)}) typedef long long crange_value; diff --git a/include/stc/cspan.h b/include/stc/cspan.h index d5482200..ac3e9206 100644 --- a/include/stc/cspan.h +++ b/include/stc/cspan.h @@ -48,10 +48,12 @@ int demo2() { puts(""); c_forfilter (i, Intspan, span, - c_flt_skipwhile(i, *i.ref < 25) && - (*i.ref & 1) == 0 && // even only - c_flt_take(i, 2)) // break after 2 + c_flt_skipwhile(i, *i.ref < 25) && + (*i.ref & 1) == 0 && // even only + c_flt_take(i, 2) // break after 2 + ){ printf(" %d", *i.ref); + } puts(""); } */ diff --git a/src/utf8code.c b/src/utf8code.c index ecfdd24d..496f5eef 100644 --- a/src/utf8code.c +++ b/src/utf8code.c @@ -142,7 +142,7 @@ bool utf8_isalpha(uint32_t c) { static int16_t groups[] = {U8G_Latin, U8G_Nl, U8G_Greek, U8G_Cyrillic, U8G_Han, U8G_Devanagari, U8G_Arabic}; if (c < 128) return isalpha((int)c) != 0; - for (int j=0; j < c_ARRAYLEN(groups); ++j) + for (int j=0; j < c_arraylen(groups); ++j) if (utf8_isgroup(groups[j], c)) return true; return false; -- cgit v1.2.3 From 59d74d181e44dd05a8570b42fc6284745e225664 Mon Sep 17 00:00:00 2001 From: Tyge Løvset Date: Tue, 28 Mar 2023 19:29:05 +0200 Subject: Example changes. Added crand.h possible replacement for crandom.h --- docs/ccommon_api.md | 2 +- include/stc/crand.h | 194 +++++++++++++++++++++++++++++++++++++++++++++++++ misc/examples/gauss2.c | 11 +-- misc/examples/prime.c | 5 +- misc/examples/shape.c | 19 ++--- 5 files changed, 211 insertions(+), 20 deletions(-) create mode 100644 include/stc/crand.h (limited to 'docs') diff --git a/docs/ccommon_api.md b/docs/ccommon_api.md index 53ba096a..46966fd9 100644 --- a/docs/ccommon_api.md +++ b/docs/ccommon_api.md @@ -92,7 +92,7 @@ Iterate containers with stop-criteria and chained range filtering. | `c_flt_skipwhile(it, predicate)` | Skip items until predicate is false | | `c_flt_takewhile(it, predicate)` | Take items until predicate is false | | `c_flt_count(it)` | Increment current and return value | -| `c_flt_last(it)` | Get value of last count/skip/take | +| `c_flt_last(it)` | Get value of last count/skip*/take* | ```c // Example: #include diff --git a/include/stc/crand.h b/include/stc/crand.h new file mode 100644 index 00000000..4ebc402d --- /dev/null +++ b/include/stc/crand.h @@ -0,0 +1,194 @@ +/* MIT License + * + * Copyright (c) 2023 Tyge Løvset + * + * 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. + */ +#include "ccommon.h" + +#ifndef CRAND_H_INCLUDED +#define CRAND_H_INCLUDED +/* +// crand: Pseudo-random number generator +#include "stc/crand.h" + +int main() { + uint64_t seed = 123456789; + crand_t rng = crand_from(seed); + crand_unif_t dist1 = crand_unif_from(1, 6); + crandf_unif_t dist2 = crandf_unif_from(1.0, 10.0); + crandf_norm_t dist3 = crandf_norm_from(1.0, 10.0); + + uint64_t i = crand_r(&rng); + int64_t iu = crand_unif(&rng, &dist1); + double xu = crandf_unif(&rng, &dist2); + double xn = crandf_norm(&rng, &dist3); +} +*/ +#include +#include + +typedef struct crand { uint64_t state[5]; } crand_t; +typedef struct crand_unif { int64_t lower; uint64_t range, threshold; } crand_unif_t; +typedef struct crandf_unif { double lower, range; } crandf_unif_t; +typedef struct crandf_norm { double mean, stddev, next; int has_next; } crandf_norm_t; + +/* PRNG crand_t. + * Very fast PRNG suited for parallel usage with Weyl-sequence parameter. + * 320-bit state, 256 bit is mutable. + * Noticable faster than xoshiro and pcg, slighly slower than wyrand64 and + * Romu, but these have restricted capacity for larger parallel jobs or unknown minimum periods. + * crand_t supports 2^63 unique threads with a minimum 2^64 period lengths each. + * Passes all statistical tests, e.g PractRand and correlation tests, i.e. interleaved + * streams with one-bit diff state. Even the 16-bit version (LR=6, RS=5, LS=3) passes + * PractRand to multiple TB input. + */ + +/* Global crand_t PRNGs */ +STC_API void csrand(uint64_t seed); +STC_API uint64_t crand(void); +STC_API double crandf(void); + +/* Init crand_t prng with a seed */ +STC_API crand_t crand_from(uint64_t seed); + +/* Unbiased bounded uniform distribution. range [low, high] */ +STC_API crand_unif_t crand_unif_from(int64_t low, int64_t high); +STC_API int64_t crand_unif(crand_t* rng, crand_unif_t* dist); + +/* Normal distribution PRNG */ +STC_API double crandf_norm(crand_t* rng, crandf_norm_t* dist); + + +/* Main crand_t prng */ +STC_INLINE uint64_t crand_r(crand_t* rng) { + enum {LR=24, RS=11, LS=3}; uint64_t *s = rng->state; + const uint64_t out = (s[0] ^ (s[3] += s[4])) + s[1]; + s[0] = s[1] ^ (s[1] >> RS); + s[1] = s[2] + (s[2] << LS); + s[2] = ((s[2] << LR) | (s[2] >> (64 - LR))) + out; + return out; +} + +/* Float64 random number in range [0.0, 1.0). */ +STC_INLINE double crandf_r(crand_t* rng) { + union {uint64_t i; double f;} u = {0x3FF0000000000000U | (crand_r(rng) >> 12)}; + return u.f - 1.0; +} + +/* Float64 uniform distributed RNG, range [low, high). */ +STC_INLINE double crandf_unif(crand_t* rng, crandf_unif_t* dist) { + return crandf_r(rng)*dist->range + dist->lower; +} + +/* Init uniform distributed float64 RNG, range [low, high). */ +STC_INLINE crandf_unif_t crandf_unif_from(double low, double high) { + return c_LITERAL(crandf_unif_t){low, high - low}; +} + +/* Marsaglia polar method for gaussian/normal distribution, float64. */ +STC_INLINE crandf_norm_t crandf_norm_from(double mean, double stddev) { + return c_LITERAL(crandf_norm_t){mean, stddev, 0.0, 0}; +} + +/* -------------------------- IMPLEMENTATION ------------------------- */ +#if defined(i_implement) + +/* Global random() */ +static crand_t crand_global = {{ + 0x26aa069ea2fb1a4d, 0x70c72c95cd592d04, + 0x504f333d3aa0b359, 0x9e3779b97f4a7c15, + 0x6a09e667a754166b +}}; + +STC_DEF void csrand(uint64_t seed) { + crand_global = crand_from(seed); +} + +STC_DEF uint64_t crand(void) { + return crand_r(&crand_global); +} + +STC_DEF double crandf(void) { + return crandf_r(&crand_global); +} + +/* rng.state[4] must be odd */ +STC_DEF crand_t crand_from(uint64_t seed) { + crand_t rng = {{seed + 0x26aa069ea2fb1a4d, + seed*0x9e3779b97f4a7c15 + 0x70c72c95cd592d04, + seed + 0x504f333d3aa0b359, + seed*0x6a09e667a754166b, seed<<1 | 1}}; + return rng; +} + +/* Init unbiased uniform uint RNG with bounds [low, high] */ +STC_DEF crand_unif_t crand_unif_from(int64_t low, int64_t high) { + crand_unif_t dist = {low, (uint64_t) (high - low + 1)}; + dist.threshold = (uint64_t)(0 - dist.range) % dist.range; + return dist; +} + +#if defined(__SIZEOF_INT128__) + #define c_umul128(a, b, lo, hi) \ + do { __uint128_t _z = (__uint128_t)(a)*(b); \ + *(lo) = (uint64_t)_z, *(hi) = (uint64_t)(_z >> 64U); } while(0) +#elif defined(_MSC_VER) && defined(_WIN64) + #include + #define c_umul128(a, b, lo, hi) ((void)(*(lo) = _umul128(a, b, hi))) +#elif defined(__x86_64__) + #define c_umul128(a, b, lo, hi) \ + asm("mulq %3" : "=a"(*(lo)), "=d"(*(hi)) : "a"(a), "rm"(b)) +#endif + +/* Int uniform distributed RNG, range [low, high]. */ +STC_DEF int64_t crand_unif(crand_t* rng, crand_unif_t* d) { +#ifdef c_umul128 + uint64_t lo, hi; + do { c_umul128(crand_r(rng), d->range, &lo, &hi); } while (lo < d->threshold); + return d->lower + (int64_t)hi; +#else + uint64_t x, r; + do { x = crand_r(rng); r = x % d->range; } while (x - r > -d->range); + return d->lower + r; +#endif +} + +/* Normal distribution PRNG */ +STC_DEF double crandf_norm(crand_t* rng, crandf_norm_t* dist) { + double u1, u2, s, m; + if (dist->has_next++ & 1) + return dist->next * dist->stddev + dist->mean; + do { + u1 = 2.0 * crandf_r(rng) - 1.0; + u2 = 2.0 * crandf_r(rng) - 1.0; + s = u1*u1 + u2*u2; + } while (s >= 1.0 || s == 0.0); + m = sqrt(-2.0 * log(s) / s); + dist->next = u2 * m; + return (u1 * m) * dist->stddev + dist->mean; +} + +#endif +#endif +#undef i_opt +#undef i_static +#undef i_header +#undef i_implement +#undef i_extern diff --git a/misc/examples/gauss2.c b/misc/examples/gauss2.c index be514c12..ce29f786 100644 --- a/misc/examples/gauss2.c +++ b/misc/examples/gauss2.c @@ -11,14 +11,15 @@ int main() { - enum {N = 10000000}; - const double Mean = -12.0, StdDev = 6.0, Scale = 74; + enum {N = 5000000}; + uint64_t seed = (uint64_t)time(NULL); + stc64_t rng = stc64_new(seed); + const float Mean = round(stc64_randf(&rng)*98.f - 49.f), StdDev = stc64_randf(&rng)*10.f + 1.f, Scale = 74.f; printf("Demo of gaussian / normal distribution of %d random samples\n", N); + printf("Mean %f, StdDev %f\n", Mean, StdDev); // Setup random engine with normal distribution. - uint64_t seed = (uint64_t)time(NULL); - stc64_t rng = stc64_new(seed); stc64_normalf_t dist = stc64_normalf_new(Mean, StdDev); // Create and init histogram map with defered destruct @@ -32,7 +33,7 @@ int main() // Print the gaussian bar chart c_forpair (index, count, csmap_int, hist) { - int n = (int)((float)*_.count * StdDev * Scale * 2.5f / (float)N); + int n = (int)round((float)*_.count * StdDev * Scale * 2.5f / (float)N); if (n > 0) { cstr_resize(&bar, n, '*'); printf("%4d %s\n", *_.index, cstr_str(&bar)); diff --git a/misc/examples/prime.c b/misc/examples/prime.c index 9ffb2f53..34d64f10 100644 --- a/misc/examples/prime.c +++ b/misc/examples/prime.c @@ -42,9 +42,8 @@ int main(void) puts("\n"); puts("Show the last 50 primes using a temporary crange generator:"); - crange R = crange_make(n - 1, 0, -2); - c_forfilter (i, crange, R, - cbits_test(&primes, *i.ref>>1) && + c_forfilter (i, crange, crange_object(n - 1, 0, -2), + cbits_test(&primes, *i.ref/2) && c_flt_take(i, 50) ){ printf("%lld ", *i.ref); diff --git a/misc/examples/shape.c b/misc/examples/shape.c index e24f7fd7..d7116039 100644 --- a/misc/examples/shape.c +++ b/misc/examples/shape.c @@ -4,7 +4,7 @@ #include #include -#define c_dyn_cast(T, s) \ +#define DYN_CAST(T, s) \ (&T##_api == (s)->api ? (T*)(s) : (T*)0) // Shape definition @@ -53,15 +53,14 @@ typedef struct { extern struct ShapeAPI Triangle_api; -Triangle Triangle_from(Point a, Point b, Point c) -{ - Triangle t = {.shape={.api=&Triangle_api}, .p={a, b, c}}; +Triangle Triangle_from(Point a, Point b, Point c) { + Triangle t = {{&Triangle_api}, {a, b, c}}; return t; } static void Triangle_draw(const Shape* shape) { - const Triangle* self = c_dyn_cast(Triangle, shape); + const Triangle* self = DYN_CAST(Triangle, shape); printf("Triangle : (%g,%g), (%g,%g), (%g,%g)\n", self->p[0].x, self->p[0].y, self->p[1].x, self->p[1].y, @@ -88,9 +87,8 @@ typedef struct { extern struct ShapeAPI Polygon_api; -Polygon Polygon_init(void) -{ - Polygon p = {.shape={.api=&Polygon_api}, .points=PointVec_init()}; +Polygon Polygon_init(void) { + Polygon p = {{&Polygon_api}, {0}}; return p; } @@ -101,15 +99,14 @@ void Polygon_addPoint(Polygon* self, Point p) static void Polygon_drop(Shape* shape) { - Polygon* self = c_dyn_cast(Polygon, shape); + Polygon* self = DYN_CAST(Polygon, shape); printf("poly destructed\n"); PointVec_drop(&self->points); - Shape_drop(shape); } static void Polygon_draw(const Shape* shape) { - const Polygon* self = c_dyn_cast(Polygon, shape); + const Polygon* self = DYN_CAST(Polygon, shape); printf("Polygon :"); c_foreach (i, PointVec, self->points) printf(" (%g,%g)", i.ref->x, i.ref->y); -- cgit v1.2.3 From 97d205f7ba096a9872afbce5e696a8806d7b72d1 Mon Sep 17 00:00:00 2001 From: Tyge Lovset Date: Wed, 29 Mar 2023 08:45:28 +0200 Subject: Removed i_less_functor, i_cmp_functor, i_eq_functor and i_hash_functor: not needed. Simplified cvec_X_eq() and cdeq_X_eq() --- docs/cmap_api.md | 4 +--- docs/cpque_api.md | 1 - docs/cset_api.md | 2 -- docs/csmap_api.md | 1 - docs/csset_api.md | 1 - include/stc/cdeq.h | 5 ++--- include/stc/cmap.h | 14 +++----------- include/stc/cpque.h | 10 +++------- include/stc/csmap.h | 18 +++++++----------- include/stc/cvec.h | 5 ++--- misc/examples/functor.c | 8 ++++---- 11 files changed, 22 insertions(+), 47 deletions(-) (limited to 'docs') diff --git a/docs/cmap_api.md b/docs/cmap_api.md index 71e00265..8749bd09 100644 --- a/docs/cmap_api.md +++ b/docs/cmap_api.md @@ -36,9 +36,7 @@ See the c++ class [std::unordered_map](https://en.cppreference.com/w/cpp/contain #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_hash_functor // advanced, see examples/functor.c for similar usage. -#define i_eq_functor // advanced, see examples/functor.c for similar usage. -#define i_ssize // internal; default int32_t. If defined, table expand 2x (else 1.5x) +#define i_ssize // internal; default int32_t. If defined, table expand 2x (else 1.5x) #include ``` `X` should be replaced by the value of `i_tag` in all of the following documentation. diff --git a/docs/cpque_api.md b/docs/cpque_api.md index 134a920f..1396dc1f 100644 --- a/docs/cpque_api.md +++ b/docs/cpque_api.md @@ -18,7 +18,6 @@ See the c++ class [std::priority_queue](https://en.cppreference.com/w/cpp/contai #define i_valfrom // convertion func i_valraw => i_val #define i_valto // convertion func i_val* => i_valraw. -#define i_less_functor // takes self as first argument. See examples/functor.c for usage. #define i_tag // alternative typename: cpque_{i_tag}. i_tag defaults to i_val #include ``` diff --git a/docs/cset_api.md b/docs/cset_api.md index a0af357f..db9fb802 100644 --- a/docs/cset_api.md +++ b/docs/cset_api.md @@ -19,8 +19,6 @@ A **cset** is an associative container that contains a set of unique objects of #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_val -#define i_hash_functor // advanced, see examples/functor.c for similar usage. -#define i_eq_functor // advanced, see examples/functor.c for similar usage. #define i_ssize // default int32_t. If defined, table expand 2x (else 1.5x) #include ``` diff --git a/docs/csmap_api.md b/docs/csmap_api.md index da0e6915..0eff8c26 100644 --- a/docs/csmap_api.md +++ b/docs/csmap_api.md @@ -33,7 +33,6 @@ See the c++ class [std::map](https://en.cppreference.com/w/cpp/container/map) fo #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_cmp_functor // advanced, see examples/functor.c for similar usage. #define i_ssize // internal size rep. defaults to int32_t #include ``` diff --git a/docs/csset_api.md b/docs/csset_api.md index 0989fd9b..1869327c 100644 --- a/docs/csset_api.md +++ b/docs/csset_api.md @@ -18,7 +18,6 @@ See the c++ class [std::set](https://en.cppreference.com/w/cpp/container/set) fo #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_cmp_functor // advanced, see examples/functor.c for similar usage. #define i_tag // alternative typename: csset_{i_tag}. i_tag defaults to i_val #define i_ssize // defaults to int32_t #include diff --git a/include/stc/cdeq.h b/include/stc/cdeq.h index c4f84a1b..09c0a7f8 100644 --- a/include/stc/cdeq.h +++ b/include/stc/cdeq.h @@ -198,9 +198,8 @@ _cx_memb(_get_mut)(_cx_self* self, _cx_raw raw) STC_INLINE bool _cx_memb(_eq)(const _cx_self* x, const _cx_self* y) { if (x->_len != y->_len) return false; - _cx_iter i = _cx_memb(_begin)(x), j = _cx_memb(_begin)(y); - for (; i.ref; _cx_memb(_next)(&i), _cx_memb(_next)(&j)) { - const _cx_raw _rx = i_keyto(i.ref), _ry = i_keyto(j.ref); + for (intptr_t i = 0; i < x->_len; ++i) { + const _cx_raw _rx = i_keyto(x->data+i), _ry = i_keyto(y->data+i); if (!(i_eq((&_rx), (&_ry)))) return false; } return true; diff --git a/include/stc/cmap.h b/include/stc/cmap.h index 402038cb..c840523f 100644 --- a/include/stc/cmap.h +++ b/include/stc/cmap.h @@ -82,12 +82,6 @@ typedef struct { int64_t idx; uint8_t hx; } chash_bucket_t; #define _i_size i_ssize #endif #include "priv/template.h" -#ifndef i_hash_functor - #define i_hash_functor(self, x) i_hash(x) -#endif -#ifndef i_eq_functor - #define i_eq_functor(self, x, y) i_eq(x, y) -#endif #if !c_option(c_is_forward) _cx_deftypes(_c_chash_types, _cx_self, i_key, i_val, i_ssize, _i_MAP_ONLY, _i_SET_ONLY); #endif @@ -371,14 +365,14 @@ STC_DEF void _cx_memb(_clear)(_cx_self* self) { STC_DEF chash_bucket_t _cx_memb(_bucket_)(const _cx_self* self, const _cx_rawkey* rkeyptr) { - const uint64_t _hash = i_hash_functor(self, rkeyptr); + const uint64_t _hash = i_hash(rkeyptr); int64_t _cap = self->bucket_count; chash_bucket_t b = {c_PASTE(fastrange_,_i_expandby)(_hash, (uint64_t)_cap), (uint8_t)(_hash | 0x80)}; const uint8_t* _hx = self->_hashx; while (_hx[b.idx]) { if (_hx[b.idx] == b.hx) { const _cx_rawkey _raw = i_keyto(_i_keyref(self->table + b.idx)); - if (i_eq_functor(self, (&_raw), rkeyptr)) + if (i_eq((&_raw), rkeyptr)) break; } if (++b.idx == _cap) @@ -469,7 +463,7 @@ _cx_memb(_erase_entry)(_cx_self* self, _cx_value* _val) { if (! _hashx[j]) break; const _cx_rawkey _raw = i_keyto(_i_keyref(_slot + j)); - k = (i_ssize)c_PASTE(fastrange_,_i_expandby)(i_hash_functor(self, (&_raw)), (uint64_t)_cap); + k = (i_ssize)c_PASTE(fastrange_,_i_expandby)(i_hash((&_raw)), (uint64_t)_cap); if ((j < i) ^ (k <= i) ^ (k > j)) /* is k outside (i, j]? */ _slot[i] = _slot[j], _hashx[i] = _hashx[j], i = j; } @@ -479,8 +473,6 @@ _cx_memb(_erase_entry)(_cx_self* self, _cx_value* _val) { #endif // i_implement #undef i_max_load_factor -#undef i_hash_functor -#undef i_eq_functor #undef _i_size #undef _i_isset #undef _i_ismap diff --git a/include/stc/cpque.h b/include/stc/cpque.h index 4955f2e0..00eaa49e 100644 --- a/include/stc/cpque.h +++ b/include/stc/cpque.h @@ -32,9 +32,6 @@ #endif #include "priv/template.h" -#ifndef i_less_functor - #define i_less_functor(self, x, y) i_less(x, y) -#endif #if !c_option(c_is_forward) _cx_deftypes(_c_cpque_types, _cx_self, i_key); #endif @@ -120,8 +117,8 @@ STC_DEF void _cx_memb(_sift_down_)(_cx_self* self, const intptr_t idx, const intptr_t n) { _cx_value t, *arr = self->data - 1; for (intptr_t r = idx, c = idx*2; c <= n; c *= 2) { - c += i_less_functor(self, (&arr[c]), (&arr[c + (c < n)])); - if (!(i_less_functor(self, (&arr[r]), (&arr[c])))) return; + c += i_less((&arr[c]), (&arr[c + (c < n)])); + if (!(i_less((&arr[r]), (&arr[c])))) return; t = arr[r], arr[r] = arr[c], arr[r = c] = t; } } @@ -156,12 +153,11 @@ _cx_memb(_push)(_cx_self* self, _cx_value value) { _cx_memb(_reserve)(self, self->_len*3/2 + 4); _cx_value *arr = self->data - 1; /* base 1 */ intptr_t c = ++self->_len; - for (; c > 1 && (i_less_functor(self, (&arr[c/2]), (&value))); c /= 2) + for (; c > 1 && (i_less((&arr[c/2]), (&value))); c /= 2) arr[c] = arr[c/2]; arr[c] = value; } #endif #define CPQUE_H_INCLUDED -#undef i_less_functor #include "priv/template.h" diff --git a/include/stc/csmap.h b/include/stc/csmap.h index 2b1910e9..50593ba3 100644 --- a/include/stc/csmap.h +++ b/include/stc/csmap.h @@ -80,9 +80,6 @@ int main(void) { #define _i_size i_ssize #endif #include "priv/template.h" -#ifndef i_cmp_functor - #define i_cmp_functor(self, x, y) i_cmp(x, y) -#endif #if !c_option(c_is_forward) _cx_deftypes(_c_aatree_types, _cx_self, i_key, i_val, i_ssize, _i_MAP_ONLY, _i_SET_ONLY); #endif @@ -222,12 +219,12 @@ _cx_memb(_advance)(_cx_iter it, size_t n) { } STC_INLINE bool -_cx_memb(_eq)(const _cx_self* m1, const _cx_self* m2) { - if (_cx_memb(_size)(m1) != _cx_memb(_size)(m2)) return false; - _cx_iter i = _cx_memb(_begin)(m1), j = _cx_memb(_begin)(m2); +_cx_memb(_eq)(const _cx_self* self, const _cx_self* other) { + if (_cx_memb(_size)(self) != _cx_memb(_size)(other)) return false; + _cx_iter i = _cx_memb(_begin)(self), j = _cx_memb(_begin)(other); for (; i.ref; _cx_memb(_next)(&i), _cx_memb(_next)(&j)) { const _cx_rawkey _rx = i_keyto(_i_keyref(i.ref)), _ry = i_keyto(_i_keyref(j.ref)); - if ((i_cmp_functor(m1, (&_rx), (&_ry))) != 0) return false; + if (!(i_eq((&_rx), (&_ry)))) return false; } return true; } @@ -357,7 +354,7 @@ _cx_memb(_find_it)(const _cx_self* self, _cx_rawkey rkey, _cx_iter* out) { out->_top = 0; while (tn) { int c; const _cx_rawkey _raw = i_keyto(_i_keyref(&d[tn].value)); - if ((c = i_cmp_functor(self, (&_raw), (&rkey))) < 0) + if ((c = i_cmp((&_raw), (&rkey))) < 0) tn = d[tn].link[1]; else if (c > 0) { out->_st[out->_top++] = tn; tn = d[tn].link[0]; } @@ -425,7 +422,7 @@ _cx_memb(_insert_entry_i_)(_cx_self* self, i_ssize tn, const _cx_rawkey* rkey, _ while (tx) { up[top++] = tx; const _cx_rawkey _raw = i_keyto(_i_keyref(&d[tx].value)); - if (!(c = i_cmp_functor(self, (&_raw), rkey))) + if (!(c = i_cmp((&_raw), rkey))) { _res->ref = &d[tx].value; return tn; } dir = (c < 0); tx = d[tx].link[dir]; @@ -464,7 +461,7 @@ _cx_memb(_erase_r_)(_cx_self *self, i_ssize tn, const _cx_rawkey* rkey, int *era if (tn == 0) return 0; _cx_rawkey raw = i_keyto(_i_keyref(&d[tn].value)); - i_ssize tx; int c = i_cmp_functor(self, (&raw), rkey); + i_ssize tx; int c = i_cmp((&raw), rkey); if (c != 0) d[tn].link[c < 0] = _cx_memb(_erase_r_)(self, d[tn].link[c < 0], rkey, erased); else { @@ -594,7 +591,6 @@ _cx_memb(_drop)(_cx_self* self) { } #endif // i_implement -#undef i_cmp_functor #undef _i_size #undef _i_isset #undef _i_ismap diff --git a/include/stc/cvec.h b/include/stc/cvec.h index 91cdb25c..8f35e5fc 100644 --- a/include/stc/cvec.h +++ b/include/stc/cvec.h @@ -236,9 +236,8 @@ _cx_memb(_get_mut)(const _cx_self* self, _cx_raw raw) STC_INLINE bool _cx_memb(_eq)(const _cx_self* x, const _cx_self* y) { if (x->_len != y->_len) return false; - _cx_iter i = _cx_memb(_begin)(x), j = _cx_memb(_begin)(y); - for (; i.ref; _cx_memb(_next)(&i), _cx_memb(_next)(&j)) { - const _cx_raw _rx = i_keyto(i.ref), _ry = i_keyto(j.ref); + for (intptr_t i = 0; i < x->_len; ++i) { + const _cx_raw _rx = i_keyto(x->data+i), _ry = i_keyto(y->data+i); if (!(i_eq((&_rx), (&_ry)))) return false; } return true; diff --git a/misc/examples/functor.c b/misc/examples/functor.c index f37e41d5..f8074c3a 100644 --- a/misc/examples/functor.c +++ b/misc/examples/functor.c @@ -1,9 +1,9 @@ // Implements c++ example: https://en.cppreference.com/w/cpp/container/priority_queue // Example of per-instance less-function on a single priority queue type // -// Note: i_less_functor: available for cpque types only -// i_cmp_functor: available for csmap and csset types only -// i_hash_functor/i_eq_functor: available for cmap and cset types only +// Note: i_less: has self for cpque types only +// i_cmp: has self for csmap and csset types only +// i_hash/i_eq: has self for cmap and cset types only #include #include @@ -23,7 +23,7 @@ struct { #define i_type ipque #define i_val int #define i_opt c_is_forward // needed to avoid re-type-define container type -#define i_less_functor(self, x, y) c_container_of(self, IPQueue, Q)->less(x, y) +#define i_less(x, y) c_container_of(self, IPQueue, Q)->less(x, y) #include void print_queue(const char* name, IPQueue q) { -- cgit v1.2.3 From a0a290645828c88597efce80f6b0f5a958cefa89 Mon Sep 17 00:00:00 2001 From: Tyge Løvset Date: Thu, 30 Mar 2023 17:59:08 +0200 Subject: Added crand.h - Alternative API to crandom.h, which will be deprecated. --- README.md | 6 +-- docs/cpque_api.md | 8 +-- docs/crandom_api.md | 45 ++++++++--------- include/stc/clist.h | 4 +- include/stc/cqueue.h | 10 ++-- misc/benchmarks/picobench/picobench_cmap.cpp | 68 ++++++++++++------------- misc/benchmarks/picobench/picobench_csmap.cpp | 72 +++++++++++++-------------- misc/benchmarks/plotbench/cdeq_benchmark.cpp | 30 +++++------ misc/benchmarks/plotbench/clist_benchmark.cpp | 26 +++++----- misc/benchmarks/plotbench/cmap_benchmark.cpp | 34 ++++++------- misc/benchmarks/plotbench/cpque_benchmark.cpp | 32 ++++++------ misc/benchmarks/plotbench/csmap_benchmark.cpp | 34 ++++++------- misc/benchmarks/plotbench/cvec_benchmark.cpp | 22 ++++---- misc/benchmarks/shootout_hashmaps.cpp | 8 +-- misc/benchmarks/various/cbits_benchmark.cpp | 14 +++--- misc/benchmarks/various/prng_bench.cpp | 6 +-- misc/benchmarks/various/sso_bench.cpp | 14 +++--- misc/examples/birthday.c | 10 ++-- misc/examples/gauss2.c | 10 ++-- misc/examples/list.c | 7 ++- misc/examples/new_queue.c | 10 ++-- misc/examples/priority.c | 10 ++-- misc/examples/queue.c | 12 ++--- misc/examples/random.c | 16 +++--- 24 files changed, 249 insertions(+), 259 deletions(-) (limited to 'docs') diff --git a/README.md b/README.md index 75a5357e..b5f9ceb7 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ Others ------ - [***ccommon*** - Generic safe macros and algorithms](docs/ccommon_api.md) - [***cregex*** - Regular expressions (extended from Rob Pike's regexp9)](docs/cregex_api.md) -- [***crandom*** - A novel very fast *PRNG* named **stc64**](docs/crandom_api.md) +- [***crand*** - A novel very fast *PRNG* named **stc64**](docs/crandom_api.md) - [***coption*** - getopt() alike command line args parser](docs/coption_api.md) Highlights @@ -343,7 +343,7 @@ once and if needed. Currently, define `i_extern` before including **clist** for It is possible to generate single headers by executing the python script `src/singleheader.py header-file > single`. Conveniently, `src\libstc.c` implements non-templated functions as shared symbols for **cstr**, **csview**, -**cbits** and **crandom**. When building in shared mode (-DSTC_HEADER), you may include this file in your project, +**cbits** and **crand**. When building in shared mode (-DSTC_HEADER), you may include this file in your project, or define your own as descibed above. ```c // stc_libs.c @@ -592,7 +592,7 @@ STC is generally very memory efficient. Type sizes: - Allows for `i_key*` template parameters instead of `i_val*` for all containers, not only for **cset** and **csset**. - Optimized *c_default_hash()*. Therefore *c_hash32()* and *c_hash64()* are removed (same speed). - Added *.._push()* and *.._emplace()* function to all containers to allow for more generic coding. -- Renamed global PRNGs *stc64_random()* and *stc64_srandom()* to *crandom()* and *csrandom()*. +- Renamed global PRNGs *stc64_random()* and *stc64_srandom()* to *crand()* and *csrand()*. - Added some examples and benchmarks for SSO and heterogenous lookup comparison with c++20 (string_bench_*.cpp). ## Brief summary of changes from version 2.x to 3.0 diff --git a/docs/cpque_api.md b/docs/cpque_api.md index 1396dc1f..962ee162 100644 --- a/docs/cpque_api.md +++ b/docs/cpque_api.md @@ -60,7 +60,7 @@ i_val cpque_X_value_clone(i_val value); ## Example ```c -#include +#include #include #define i_val int64_t @@ -71,15 +71,15 @@ i_val cpque_X_value_clone(i_val value); int main() { intptr_t N = 10000000; - stc64_t rng = stc64_new(1234); - stc64_uniform_t dist = stc64_uniform_new(0, N * 10); + crand_t rng = crand_init(1234); + crand_unif_t dist = crand_unif_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, stc64_uniform(&rng, &dist)); + cpque_i_push(&heap, crand_unif(&rng, &dist)); // Add some negative ones. int nums[] = {-231, -32, -873, -4, -343}; diff --git a/docs/crandom_api.md b/docs/crandom_api.md index e69cc539..7281b2d7 100644 --- a/docs/crandom_api.md +++ b/docs/crandom_api.md @@ -1,4 +1,4 @@ -# STC [crandom](../include/stc/crandom.h): Pseudo Random Number Generator +# STC [crand](../include/stc/crand.h): Pseudo Random Number Generator ![Random](pics/random.jpg) This features a *64-bit PRNG* named **stc64**, and can generate bounded uniform and normal @@ -33,45 +33,40 @@ xoshiro and pcg (Vigna/O'Neill) PRNGs: https://www.pcg-random.org/posts/on-vigna ## Header file -All crandom definitions and prototypes are available by including a single header file. +All crand definitions and prototypes are available by including a single header file. ```c -#include +#include ``` ## Methods ```c -void csrandom(uint64_t seed); // seed global stc64 prng -uint64_t crandom(void); // global stc64_rand(rng) -double crandomf(void); // global stc64_randf(rng) +void csrand(uint64_t seed); // seed global stc64 prng +uint64_t crand(void); // global crand_u64(rng) +double crandf(void); // global crand_f64(rng) -stc64_t stc64_new(uint64_t seed); // stc64_init(s) is deprecated -stc64_t stc64_with_seq(uint64_t seed, uint64_t seq); // with unique stream +crand_t crand_init(uint64_t seed); // stc64_init(s) is deprecated +uint64_t crand_u64(crand_t* rng); // range [0, 2^64 - 1] +double crand_f64(crand_t* rng); // range [0.0, 1.0) -uint64_t stc64_rand(stc64_t* rng); // range [0, 2^64 - 1] -double stc64_randf(stc64_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] -stc64_uniform_t stc64_uniform_new(int64_t low, int64_t high); // uniform-distribution -int64_t stc64_uniform(stc64_t* rng, stc64_uniform_t* dist); // range [low, high] -stc64_uniformf_t stc64_uniformf_new(double lowf, double highf); -double stc64_uniformf(stc64_t* rng, stc64_uniformf_t* dist); // range [lowf, highf) - -stc64_normalf_t stc64_normalf_new(double mean, double stddev); // normal-distribution -double stc64_normalf(stc64_t* rng, stc64_normalf_t* dist); +crand_norm_t crand_norm_init(double mean, double stddev); // normal-distribution +double crand_norm(crand_t* rng, crand_norm_t* dist); ``` ## Types | Name | Type definition | Used to represent... | |:-------------------|:------------------------------------------|:-----------------------------| -| `stc64_t` | `struct {uint64_t state[4];}` | The PRNG engine type | -| `stc64_uniform_t` | `struct {int64_t lower; uint64_t range;}` | Integer uniform distribution | -| `stc64_uniformf_t` | `struct {double lower, range;}` | Real number uniform distr. | -| `stc64_normalf_t` | `struct {double mean, stddev;}` | Normal distribution type | +| `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 | ## Example ```c #include -#include +#include #include // Declare int -> int sorted map. Uses typetag 'i' for ints. @@ -89,13 +84,13 @@ int main() // Setup random engine with normal distribution. uint64_t seed = time(NULL); - stc64_t rng = stc64_new(seed); - stc64_normalf_t dist = stc64_normalf_new(Mean, StdDev); + crand_t rng = crand_init(seed); + crand_norm_t dist = crand_norm_init(Mean, StdDev); // Create histogram map csmap_i mhist = csmap_i_init(); c_forrange (N) { - int index = (int) round( stc64_normalf(&rng, &dist) ); + int index = (int)round(crand_norm(&rng, &dist)); csmap_i_emplace(&mhist, index, 0).ref->second += 1; } diff --git a/include/stc/clist.h b/include/stc/clist.h index eb72b888..fa26fd65 100644 --- a/include/stc/clist.h +++ b/include/stc/clist.h @@ -26,7 +26,7 @@ it also support both push_back() and push_front(), unlike std::forward_list: #include - #include + #include #define i_key int64_t #define i_tag ix @@ -38,7 +38,7 @@ { int n; for (int i = 0; i < 1000000; ++i) // one million - clist_ix_push_back(&list, crandom() >> 32); + clist_ix_push_back(&list, crand() >> 32); n = 0; c_foreach (i, clist_ix, list) if (++n % 10000 == 0) printf("%8d: %10zu\n", n, *i.ref); diff --git a/include/stc/cqueue.h b/include/stc/cqueue.h index 67909f8e..1934305a 100644 --- a/include/stc/cqueue.h +++ b/include/stc/cqueue.h @@ -22,7 +22,7 @@ */ // STC queue /* -#include +#include #include #define i_key int @@ -30,19 +30,19 @@ int main() { int n = 10000000; - stc64_t rng = stc64_new(1234); - stc64_uniform_t dist = stc64_uniform_new(0, n); + crand_t rng = crand_init(1234); + crand_unif_t dist = crand_unif_init(0, n); c_auto (cqueue_int, Q) { // Push ten million random numbers onto the queue. for (int i=0; i0; --i) { - int r = stc64_uniform(&rng, &dist); + int r = crand_unif(&rng, &dist); if (r & 1) ++n, cqueue_int_push(&Q, r); else diff --git a/misc/benchmarks/picobench/picobench_cmap.cpp b/misc/benchmarks/picobench/picobench_cmap.cpp index 3ffba5b9..bccbe70c 100644 --- a/misc/benchmarks/picobench/picobench_cmap.cpp +++ b/misc/benchmarks/picobench/picobench_cmap.cpp @@ -1,5 +1,5 @@ #define i_static -#include +#include #define i_static #include #include @@ -54,36 +54,36 @@ static void ins_and_erase_i(picobench::state& s) { MapInt map; map.max_load_factor((int)MaxLoadFactor100 / 100.0); - csrandom(seed); + csrand(seed); picobench::scope scope(s); c_forrange (s.iterations()) - map[crandom()]; + map[crand()]; map.clear(); - csrandom(seed); + csrand(seed); c_forrange (s.iterations()) - map[crandom()]; - csrandom(seed); + map[crand()]; + csrand(seed); c_forrange (s.iterations()) - map.erase(crandom()); + map.erase(crand()); s.set_result(map.size()); } /* static void ins_and_erase_cmap_i(picobench::state& s) { cmap_i map = cmap_i_init(); - csrandom(seed); + csrand(seed); picobench::scope scope(s); c_forrange (s.iterations()) - cmap_i_insert(&map, crandom(), 0); + cmap_i_insert(&map, crand(), 0); cmap_i_clear(&map); - csrandom(seed); + csrand(seed); c_forrange (s.iterations()) - cmap_i_insert(&map, crandom(), 0); - csrandom(seed); + cmap_i_insert(&map, crand(), 0); + csrand(seed); c_forrange (s.iterations()) - cmap_i_erase(&map, crandom()); + cmap_i_erase(&map, crand()); s.set_result(cmap_i_size(&map)); cmap_i_drop(&map); } @@ -91,18 +91,18 @@ static void ins_and_erase_cmap_i(picobench::state& s) static void ins_and_erase_cmap_x(picobench::state& s) { cmap_x map = cmap_x_init(); - csrandom(seed); + csrand(seed); picobench::scope scope(s); c_forrange (s.iterations()) - cmap_x_insert(&map, crandom(), 0); + cmap_x_insert(&map, crand(), 0); cmap_x_clear(&map); - csrandom(seed); + csrand(seed); c_forrange (s.iterations()) - cmap_x_insert(&map, crandom(), 0); - csrandom(seed); + cmap_x_insert(&map, crand(), 0); + csrand(seed); c_forrange (s.iterations()) - cmap_x_erase(&map, crandom()); + cmap_x_erase(&map, crand()); s.set_result(cmap_x_size(&map)); cmap_x_drop(&map); } @@ -124,11 +124,11 @@ static void ins_and_access_i(picobench::state& s) size_t result = 0; MapInt map; map.max_load_factor((int)MaxLoadFactor100 / 100.0); - csrandom(seed); + csrand(seed); picobench::scope scope(s); c_forrange (N1) - result += ++map[crandom() & mask]; + result += ++map[crand() & mask]; s.set_result(result); } @@ -137,11 +137,11 @@ static void ins_and_access_cmap_i(picobench::state& s) uint64_t mask = (1ull << s.arg()) - 1; size_t result = 0; cmap_i map = cmap_i_init(); - csrandom(seed); + csrand(seed); picobench::scope scope(s); c_forrange (N1) - result += ++cmap_i_insert(&map, crandom() & mask, 0).ref->second; + result += ++cmap_i_insert(&map, crand() & mask, 0).ref->second; s.set_result(result); cmap_i_drop(&map); } @@ -158,7 +158,7 @@ PICOBENCH_SUITE("Map3"); static void randomize(char* str, size_t len) { for (size_t k=0; k < len; ++k) { - union {uint64_t i; char c[8];} r = {.i = crandom()}; + union {uint64_t i; char c[8];} r = {.i = crand()}; for (unsigned i=0; i<8 && ksecond; } // reset rng back to inital state - csrandom(seed); + csrand(seed); // measure erase then iterate whole map c_forrange (n, s.iterations()) { - cmap_x_erase(&map, crandom()); + cmap_x_erase(&map, crand()); if (!(n & K)) c_foreach (i, cmap_x, map) result += i.ref->second; } diff --git a/misc/benchmarks/picobench/picobench_csmap.cpp b/misc/benchmarks/picobench/picobench_csmap.cpp index 5caab6cc..a6a97b14 100644 --- a/misc/benchmarks/picobench/picobench_csmap.cpp +++ b/misc/benchmarks/picobench/picobench_csmap.cpp @@ -1,6 +1,6 @@ #include #define i_static -#include +#include #define i_static #include #include @@ -71,20 +71,20 @@ template static void insert_i(picobench::state& s) { MapInt map; - csrandom(seed); + csrand(seed); picobench::scope scope(s); c_forrange (n, s.iterations()) - map.emplace(crandom() & 0xfffffff, n); + map.emplace(crand() & 0xfffffff, n); s.set_result(map.size()); } static void insert_csmap_i(picobench::state& s) { csmap_i map = csmap_i_init(); - csrandom(seed); + csrand(seed); picobench::scope scope(s); c_forrange (n, s.iterations()) - csmap_i_insert(&map, crandom() & 0xfffffff, n); + csmap_i_insert(&map, crand() & 0xfffffff, n); s.set_result(csmap_i_size(&map)); csmap_i_drop(&map); } @@ -103,21 +103,21 @@ static void ins_and_erase_i(picobench::state& s) size_t result = 0; uint64_t mask = (1ull << s.arg()) - 1; MapInt map; - csrandom(seed); + csrand(seed); picobench::scope scope(s); c_forrange (i, s.iterations()) - map.emplace(crandom() & mask, i); + map.emplace(crand() & mask, i); result = map.size(); map.clear(); - csrandom(seed); + csrand(seed); c_forrange (i, s.iterations()) - map[crandom() & mask] = i; + map[crand() & mask] = i; - csrandom(seed); + csrand(seed); c_forrange (s.iterations()) - map.erase(crandom() & mask); + map.erase(crand() & mask); s.set_result(result); } @@ -126,21 +126,21 @@ static void ins_and_erase_csmap_i(picobench::state& s) size_t result = 0; uint64_t mask = (1ull << s.arg()) - 1; csmap_i map = csmap_i_init(); - csrandom(seed); + csrand(seed); picobench::scope scope(s); c_forrange (i, s.iterations()) - csmap_i_insert(&map, crandom() & mask, i); + csmap_i_insert(&map, crand() & mask, i); result = csmap_i_size(&map); csmap_i_clear(&map); - csrandom(seed); + csrand(seed); c_forrange (i, s.iterations()) - csmap_i_insert_or_assign(&map, crandom() & mask, i); + csmap_i_insert_or_assign(&map, crand() & mask, i); - csrandom(seed); + csrand(seed); c_forrange (s.iterations()) - csmap_i_erase(&map, crandom() & mask); + csmap_i_erase(&map, crand() & mask); s.set_result(result); csmap_i_drop(&map); } @@ -158,12 +158,12 @@ static void ins_and_access_i(picobench::state& s) uint64_t mask = (1ull << s.arg()) - 1; size_t result = 0; MapInt map; - csrandom(seed); + csrand(seed); picobench::scope scope(s); c_forrange (s.iterations()) { - result += ++map[crandom() & mask]; - auto it = map.find(crandom() & mask); + result += ++map[crand() & mask]; + auto it = map.find(crand() & mask); if (it != map.end()) map.erase(it->first); } s.set_result(result + map.size()); @@ -174,12 +174,12 @@ static void ins_and_access_csmap_i(picobench::state& s) uint64_t mask = (1ull << s.arg()) - 1; size_t result = 0; csmap_i map = csmap_i_init(); - csrandom(seed); + csrand(seed); picobench::scope scope(s); c_forrange (s.iterations()) { - result += ++csmap_i_insert(&map, crandom() & mask, 0).ref->second; - const csmap_i_value* val = csmap_i_get(&map, crandom() & mask); + result += ++csmap_i_insert(&map, crand() & mask, 0).ref->second; + const csmap_i_value* val = csmap_i_get(&map, crand() & mask); if (val) csmap_i_erase(&map, val->first); } s.set_result(result + csmap_i_size(&map)); @@ -194,7 +194,7 @@ PICOBENCH(ins_and_access_csmap_i).P; PICOBENCH_SUITE("Map4"); static void randomize(char* str, int len) { - union {uint64_t i; char c[8];} r = {.i = crandom()}; + union {uint64_t i; char c[8];} r = {.i = crand()}; for (int i = len - 7, j = 0; i < len; ++j, ++i) str[i] = (r.c[j] & 63) + 48; } @@ -207,12 +207,12 @@ static void ins_and_access_s(picobench::state& s) MapStr map; picobench::scope scope(s); - csrandom(seed); + csrand(seed); c_forrange (s.iterations()) { randomize(&str[0], str.size()); map.emplace(str, str); } - csrandom(seed); + csrand(seed); c_forrange (s.iterations()) { randomize(&str[0], str.size()); result += map.erase(str); @@ -228,12 +228,12 @@ static void ins_and_access_csmap_s(picobench::state& s) csmap_str map = csmap_str_init(); picobench::scope scope(s); - csrandom(seed); + csrand(seed); c_forrange (s.iterations()) { randomize(buf, s.arg()); csmap_str_emplace(&map, buf, buf); } - csrandom(seed); + csrand(seed); c_forrange (s.iterations()) { randomize(buf, s.arg()); result += csmap_str_erase(&map, buf); @@ -262,22 +262,22 @@ static void iterate_x(picobench::state& s) uint64_t K = (1ull << s.arg()) - 1; picobench::scope scope(s); - csrandom(seed); + csrand(seed); size_t result = 0; // measure insert then iterate whole map c_forrange (n, s.iterations()) { - map[crandom()] = n; + map[crand()] = n; if (!(n & K)) for (auto const& keyVal : map) result += keyVal.second; } // reset rng back to inital state - csrandom(seed); + csrand(seed); // measure erase then iterate whole map c_forrange (n, s.iterations()) { - map.erase(crandom()); + map.erase(crand()); if (!(n & K)) for (auto const& keyVal : map) result += keyVal.second; } @@ -290,22 +290,22 @@ static void iterate_csmap_x(picobench::state& s) uint64_t K = (1ull << s.arg()) - 1; picobench::scope scope(s); - csrandom(seed); + csrand(seed); size_t result = 0; // measure insert then iterate whole map c_forrange (n, s.iterations()) { - csmap_x_insert_or_assign(&map, crandom(), n); + csmap_x_insert_or_assign(&map, crand(), n); if (!(n & K)) c_foreach (i, csmap_x, map) result += i.ref->second; } // reset rng back to inital state - csrandom(seed); + csrand(seed); // measure erase then iterate whole map c_forrange (n, s.iterations()) { - csmap_x_erase(&map, crandom()); + csmap_x_erase(&map, crand()); if (!(n & K)) c_foreach (i, csmap_x, map) result += i.ref->second; } diff --git a/misc/benchmarks/plotbench/cdeq_benchmark.cpp b/misc/benchmarks/plotbench/cdeq_benchmark.cpp index 1259cc07..a8399ea8 100644 --- a/misc/benchmarks/plotbench/cdeq_benchmark.cpp +++ b/misc/benchmarks/plotbench/cdeq_benchmark.cpp @@ -1,7 +1,7 @@ #include #include #define i_static -#include +#include #ifdef __cplusplus #include @@ -28,10 +28,10 @@ Sample test_std_deque() { { s.test[INSERT].t1 = clock(); container con; - csrandom(seed); - c_forrange (N/3) con.push_front(crandom() & mask1); - c_forrange (N/3) {con.push_back(crandom() & mask1); con.pop_front();} - c_forrange (N/3) con.push_back(crandom() & mask1); + csrand(seed); + c_forrange (N/3) con.push_front(crand() & mask1); + c_forrange (N/3) {con.push_back(crand() & mask1); con.pop_front();} + c_forrange (N/3) con.push_back(crand() & mask1); s.test[INSERT].t2 = clock(); s.test[INSERT].sum = con.size(); s.test[ERASE].t1 = clock(); @@ -40,13 +40,13 @@ Sample test_std_deque() { s.test[ERASE].sum = con.size(); }{ container con; - csrandom(seed); - c_forrange (N) con.push_back(crandom() & mask2); + csrand(seed); + c_forrange (N) con.push_back(crand() & mask2); s.test[FIND].t1 = clock(); size_t sum = 0; // Iteration - not inherent find - skipping //container::iterator it; - //c_forrange (S) if ((it = std::find(con.begin(), con.end(), crandom() & mask2)) != con.end()) sum += *it; + //c_forrange (S) if ((it = std::find(con.begin(), con.end(), crand() & mask2)) != con.end()) sum += *it; s.test[FIND].t2 = clock(); s.test[FIND].sum = sum; s.test[ITER].t1 = clock(); @@ -72,10 +72,10 @@ Sample test_stc_deque() { s.test[INSERT].t1 = clock(); container con = cdeq_x_init(); //cdeq_x_reserve(&con, N); - csrandom(seed); - c_forrange (N/3) cdeq_x_push_front(&con, crandom() & mask1); - c_forrange (N/3) {cdeq_x_push_back(&con, crandom() & mask1); cdeq_x_pop_front(&con);} - c_forrange (N/3) cdeq_x_push_back(&con, crandom() & mask1); + csrand(seed); + c_forrange (N/3) cdeq_x_push_front(&con, crand() & mask1); + c_forrange (N/3) {cdeq_x_push_back(&con, crand() & mask1); cdeq_x_pop_front(&con);} + c_forrange (N/3) cdeq_x_push_back(&con, crand() & mask1); s.test[INSERT].t2 = clock(); s.test[INSERT].sum = cdeq_x_size(&con); s.test[ERASE].t1 = clock(); @@ -84,13 +84,13 @@ Sample test_stc_deque() { s.test[ERASE].sum = cdeq_x_size(&con); cdeq_x_drop(&con); }{ - csrandom(seed); + csrand(seed); container con = cdeq_x_init(); - c_forrange (N) cdeq_x_push_back(&con, crandom() & mask2); + c_forrange (N) cdeq_x_push_back(&con, crand() & mask2); s.test[FIND].t1 = clock(); size_t sum = 0; //cdeq_x_iter it, end = cdeq_x_end(&con); - //c_forrange (S) if ((it = cdeq_x_find(&con, crandom() & mask2)).ref != end.ref) sum += *it.ref; + //c_forrange (S) if ((it = cdeq_x_find(&con, crand() & mask2)).ref != end.ref) sum += *it.ref; s.test[FIND].t2 = clock(); s.test[FIND].sum = sum; s.test[ITER].t1 = clock(); diff --git a/misc/benchmarks/plotbench/clist_benchmark.cpp b/misc/benchmarks/plotbench/clist_benchmark.cpp index 04c8e8cd..46bd2793 100644 --- a/misc/benchmarks/plotbench/clist_benchmark.cpp +++ b/misc/benchmarks/plotbench/clist_benchmark.cpp @@ -1,7 +1,7 @@ #include #include #define i_static -#include +#include #ifdef __cplusplus #include @@ -28,9 +28,9 @@ Sample test_std_forward_list() { { s.test[INSERT].t1 = clock(); container con; - csrandom(seed); - c_forrange (N/2) con.push_front(crandom() & mask1); - c_forrange (N/2) con.push_front(crandom() & mask1); + csrand(seed); + c_forrange (N/2) con.push_front(crand() & mask1); + c_forrange (N/2) con.push_front(crand() & mask1); s.test[INSERT].t2 = clock(); s.test[INSERT].sum = 0; s.test[ERASE].t1 = clock(); @@ -39,13 +39,13 @@ Sample test_std_forward_list() { s.test[ERASE].sum = 0; }{ container con; - csrandom(seed); - c_forrange (N) con.push_front(crandom() & mask2); + csrand(seed); + c_forrange (N) con.push_front(crand() & mask2); s.test[FIND].t1 = clock(); size_t sum = 0; container::iterator it; // Iteration - not inherent find - skipping - //c_forrange (S) if ((it = std::find(con.begin(), con.end(), crandom() & mask2)) != con.end()) sum += *it; + //c_forrange (S) if ((it = std::find(con.begin(), con.end(), crand() & mask2)) != con.end()) sum += *it; s.test[FIND].t2 = clock(); s.test[FIND].sum = sum; s.test[ITER].t1 = clock(); @@ -70,9 +70,9 @@ Sample test_stc_forward_list() { { s.test[INSERT].t1 = clock(); container con = clist_x_init(); - csrandom(seed); - c_forrange (N/2) clist_x_push_front(&con, crandom() & mask1); - c_forrange (N/2) clist_x_push_back(&con, crandom() & mask1); + csrand(seed); + c_forrange (N/2) clist_x_push_front(&con, crand() & mask1); + c_forrange (N/2) clist_x_push_back(&con, crand() & mask1); s.test[INSERT].t2 = clock(); s.test[INSERT].sum = 0; s.test[ERASE].t1 = clock(); @@ -81,13 +81,13 @@ Sample test_stc_forward_list() { s.test[ERASE].sum = 0; clist_x_drop(&con); }{ - csrandom(seed); + csrand(seed); container con = clist_x_init(); - c_forrange (N) clist_x_push_front(&con, crandom() & mask2); + c_forrange (N) clist_x_push_front(&con, crand() & mask2); s.test[FIND].t1 = clock(); size_t sum = 0; //clist_x_iter it, end = clist_x_end(&con); - //c_forrange (S) if ((it = clist_x_find(&con, crandom() & mask2)).ref != end.ref) sum += *it.ref; + //c_forrange (S) if ((it = clist_x_find(&con, crand() & mask2)).ref != end.ref) sum += *it.ref; s.test[FIND].t2 = clock(); s.test[FIND].sum = sum; s.test[ITER].t1 = clock(); diff --git a/misc/benchmarks/plotbench/cmap_benchmark.cpp b/misc/benchmarks/plotbench/cmap_benchmark.cpp index 7a8f29d2..0582d162 100644 --- a/misc/benchmarks/plotbench/cmap_benchmark.cpp +++ b/misc/benchmarks/plotbench/cmap_benchmark.cpp @@ -1,7 +1,7 @@ #include #include #define i_static -#include +#include #ifdef __cplusplus #include @@ -26,28 +26,28 @@ Sample test_std_unordered_map() { typedef std::unordered_map container; Sample s = {"std,unordered_map"}; { - csrandom(seed); + csrand(seed); s.test[INSERT].t1 = clock(); container con; - c_forrange (i, N/2) con.emplace(crandom() & mask1, i); + c_forrange (i, N/2) con.emplace(crand() & mask1, i); c_forrange (i, N/2) con.emplace(i, i); s.test[INSERT].t2 = clock(); s.test[INSERT].sum = con.size(); - csrandom(seed); + csrand(seed); s.test[ERASE].t1 = clock(); - c_forrange (N) con.erase(crandom() & mask1); + c_forrange (N) con.erase(crand() & mask1); s.test[ERASE].t2 = clock(); s.test[ERASE].sum = con.size(); }{ container con; - csrandom(seed); - c_forrange (i, N/2) con.emplace(crandom() & mask1, i); + csrand(seed); + c_forrange (i, N/2) con.emplace(crand() & mask1, i); c_forrange (i, N/2) con.emplace(i, i); - csrandom(seed); + csrand(seed); s.test[FIND].t1 = clock(); size_t sum = 0; container::iterator it; - c_forrange (N) if ((it = con.find(crandom() & mask1)) != con.end()) sum += it->second; + c_forrange (N) if ((it = con.find(crand() & mask1)) != con.end()) sum += it->second; s.test[FIND].t2 = clock(); s.test[FIND].sum = sum; s.test[ITER].t1 = clock(); @@ -70,30 +70,30 @@ Sample test_stc_unordered_map() { typedef cmap_x container; Sample s = {"STC,unordered_map"}; { - csrandom(seed); + csrand(seed); s.test[INSERT].t1 = clock(); container con = cmap_x_init(); - c_forrange (i, N/2) cmap_x_insert(&con, crandom() & mask1, i); + c_forrange (i, N/2) cmap_x_insert(&con, crand() & mask1, i); c_forrange (i, N/2) cmap_x_insert(&con, i, i); s.test[INSERT].t2 = clock(); s.test[INSERT].sum = cmap_x_size(&con); - csrandom(seed); + csrand(seed); s.test[ERASE].t1 = clock(); - c_forrange (N) cmap_x_erase(&con, crandom() & mask1); + c_forrange (N) cmap_x_erase(&con, crand() & mask1); s.test[ERASE].t2 = clock(); s.test[ERASE].sum = cmap_x_size(&con); cmap_x_drop(&con); }{ container con = cmap_x_init(); - csrandom(seed); - c_forrange (i, N/2) cmap_x_insert(&con, crandom() & mask1, i); + csrand(seed); + c_forrange (i, N/2) cmap_x_insert(&con, crand() & mask1, i); c_forrange (i, N/2) cmap_x_insert(&con, i, i); - csrandom(seed); + csrand(seed); s.test[FIND].t1 = clock(); size_t sum = 0; const cmap_x_value* val; c_forrange (N) - if ((val = cmap_x_get(&con, crandom() & mask1))) + if ((val = cmap_x_get(&con, crand() & mask1))) sum += val->second; s.test[FIND].t2 = clock(); s.test[FIND].sum = sum; diff --git a/misc/benchmarks/plotbench/cpque_benchmark.cpp b/misc/benchmarks/plotbench/cpque_benchmark.cpp index a729c09f..da092b7f 100644 --- a/misc/benchmarks/plotbench/cpque_benchmark.cpp +++ b/misc/benchmarks/plotbench/cpque_benchmark.cpp @@ -1,7 +1,7 @@ #include #include #define i_static -#include +#include #define i_val float #define i_cmp -c_default_cmp @@ -11,19 +11,17 @@ #include static const uint32_t seed = 1234; +static const int N = 10000000; void std_test() { - stc64_t rng; - int N = 10000000; - std::priority_queue, std::greater> pq; - rng = stc64_new(seed); + csrand(seed); clock_t start = clock(); c_forrange (i, N) - pq.push((float) stc64_randf(&rng)*100000); + pq.push((float) crandf()*100000.0); - printf("Built priority queue: %f secs\n", (clock() - start) / (float) CLOCKS_PER_SEC); + printf("Built priority queue: %f secs\n", (float)(clock() - start)/(float)CLOCKS_PER_SEC); printf("%g ", pq.top()); start = clock(); @@ -31,32 +29,30 @@ void std_test() pq.pop(); } - printf("\npopped PQ: %f secs\n\n", (clock() - start) / (float) CLOCKS_PER_SEC); + printf("\npopped PQ: %f secs\n\n", (float)(clock() - start)/(float)CLOCKS_PER_SEC); } void stc_test() { - stc64_t rng; - int N = 10000000, M = 10; + int N = 10000000; c_auto (cpque_f, pq) { - rng = stc64_new(seed); + csrand(seed); clock_t start = clock(); - c_forrange (i, N) - cpque_f_push(&pq, (float) stc64_randf(&rng)*100000); + c_forrange (i, N) { + cpque_f_push(&pq, (float) crandf()*100000); + } - printf("Built priority queue: %f secs\n", (clock() - start) / (float) CLOCKS_PER_SEC); + printf("Built priority queue: %f secs\n", (float)(clock() - start)/(float)CLOCKS_PER_SEC); printf("%g ", *cpque_f_top(&pq)); - c_forrange (i, M) { + start = clock(); + c_forrange (i, N) { cpque_f_pop(&pq); } - start = clock(); - c_forrange (i, M, N) - cpque_f_pop(&pq); printf("\npopped PQ: %f secs\n", (clock() - start) / (float) CLOCKS_PER_SEC); } } diff --git a/misc/benchmarks/plotbench/csmap_benchmark.cpp b/misc/benchmarks/plotbench/csmap_benchmark.cpp index 46bd695c..da3fc9cc 100644 --- a/misc/benchmarks/plotbench/csmap_benchmark.cpp +++ b/misc/benchmarks/plotbench/csmap_benchmark.cpp @@ -1,7 +1,7 @@ #include #include #define i_static -#include +#include #ifdef __cplusplus #include @@ -26,28 +26,28 @@ Sample test_std_map() { typedef std::map container; Sample s = {"std,map"}; { - csrandom(seed); + csrand(seed); s.test[INSERT].t1 = clock(); container con; - c_forrange (i, N/2) con.emplace(crandom() & mask1, i); + c_forrange (i, N/2) con.emplace(crand() & mask1, i); c_forrange (i, N/2) con.emplace(i, i); s.test[INSERT].t2 = clock(); s.test[INSERT].sum = con.size(); - csrandom(seed); + csrand(seed); s.test[ERASE].t1 = clock(); - c_forrange (N) con.erase(crandom() & mask1); + c_forrange (N) con.erase(crand() & mask1); s.test[ERASE].t2 = clock(); s.test[ERASE].sum = con.size(); }{ container con; - csrandom(seed); - c_forrange (i, N/2) con.emplace(crandom() & mask1, i); + csrand(seed); + c_forrange (i, N/2) con.emplace(crand() & mask1, i); c_forrange (i, N/2) con.emplace(i, i); - csrandom(seed); + csrand(seed); s.test[FIND].t1 = clock(); size_t sum = 0; container::iterator it; - c_forrange (N) if ((it = con.find(crandom() & mask1)) != con.end()) sum += it->second; + c_forrange (N) if ((it = con.find(crand() & mask1)) != con.end()) sum += it->second; s.test[FIND].t2 = clock(); s.test[FIND].sum = sum; s.test[ITER].t1 = clock(); @@ -71,30 +71,30 @@ Sample test_stc_map() { typedef csmap_x container; Sample s = {"STC,map"}; { - csrandom(seed); + csrand(seed); s.test[INSERT].t1 = clock(); container con = csmap_x_init(); - c_forrange (i, N/2) csmap_x_insert(&con, crandom() & mask1, i); + c_forrange (i, N/2) csmap_x_insert(&con, crand() & mask1, i); c_forrange (i, N/2) csmap_x_insert(&con, i, i); s.test[INSERT].t2 = clock(); s.test[INSERT].sum = csmap_x_size(&con); - csrandom(seed); + csrand(seed); s.test[ERASE].t1 = clock(); - c_forrange (N) csmap_x_erase(&con, crandom() & mask1); + c_forrange (N) csmap_x_erase(&con, crand() & mask1); s.test[ERASE].t2 = clock(); s.test[ERASE].sum = csmap_x_size(&con); csmap_x_drop(&con); }{ container con = csmap_x_init(); - csrandom(seed); - c_forrange (i, N/2) csmap_x_insert(&con, crandom() & mask1, i); + csrand(seed); + c_forrange (i, N/2) csmap_x_insert(&con, crand() & mask1, i); c_forrange (i, N/2) csmap_x_insert(&con, i, i); - csrandom(seed); + csrand(seed); s.test[FIND].t1 = clock(); size_t sum = 0; const csmap_x_value* val; c_forrange (N) - if ((val = csmap_x_get(&con, crandom() & mask1))) + if ((val = csmap_x_get(&con, crand() & mask1))) sum += val->second; s.test[FIND].t2 = clock(); s.test[FIND].sum = sum; diff --git a/misc/benchmarks/plotbench/cvec_benchmark.cpp b/misc/benchmarks/plotbench/cvec_benchmark.cpp index fe7e09fb..b605f4e6 100644 --- a/misc/benchmarks/plotbench/cvec_benchmark.cpp +++ b/misc/benchmarks/plotbench/cvec_benchmark.cpp @@ -1,7 +1,7 @@ #include #include #define i_static -#include +#include #ifdef __cplusplus #include @@ -28,8 +28,8 @@ Sample test_std_vector() { { s.test[INSERT].t1 = clock(); container con; - csrandom(seed); - c_forrange (N) con.push_back(crandom() & mask1); + csrand(seed); + c_forrange (N) con.push_back(crand() & mask1); s.test[INSERT].t2 = clock(); s.test[INSERT].sum = con.size(); s.test[ERASE].t1 = clock(); @@ -38,13 +38,13 @@ Sample test_std_vector() { s.test[ERASE].sum = con.size(); }{ container con; - csrandom(seed); - c_forrange (N) con.push_back(crandom() & mask2); + csrand(seed); + c_forrange (N) con.push_back(crand() & mask2); s.test[FIND].t1 = clock(); size_t sum = 0; //container::iterator it; // Iteration - not inherent find - skipping - //c_forrange (S) if ((it = std::find(con.begin(), con.end(), crandom() & mask2)) != con.end()) sum += *it; + //c_forrange (S) if ((it = std::find(con.begin(), con.end(), crand() & mask2)) != con.end()) sum += *it; s.test[FIND].t2 = clock(); s.test[FIND].sum = sum; s.test[ITER].t1 = clock(); @@ -70,8 +70,8 @@ Sample test_stc_vector() { { s.test[INSERT].t1 = clock(); container con = cvec_x_init(); - csrandom(seed); - c_forrange (N) cvec_x_push_back(&con, crandom() & mask1); + csrand(seed); + c_forrange (N) cvec_x_push_back(&con, crand() & mask1); s.test[INSERT].t2 = clock(); s.test[INSERT].sum = cvec_x_size(&con); s.test[ERASE].t1 = clock(); @@ -80,13 +80,13 @@ Sample test_stc_vector() { s.test[ERASE].sum = cvec_x_size(&con); cvec_x_drop(&con); }{ - csrandom(seed); + csrand(seed); container con = cvec_x_init(); - c_forrange (N) cvec_x_push_back(&con, crandom() & mask2); + c_forrange (N) cvec_x_push_back(&con, crand() & mask2); s.test[FIND].t1 = clock(); size_t sum = 0; //cvec_x_iter it, end = cvec_x_end(&con); - //c_forrange (S) if ((it = cvec_x_find(&con, crandom() & mask2)).ref != end.ref) sum += *it.ref; + //c_forrange (S) if ((it = cvec_x_find(&con, crand() & mask2)).ref != end.ref) sum += *it.ref; s.test[FIND].t2 = clock(); s.test[FIND].sum = sum; s.test[ITER].t1 = clock(); diff --git a/misc/benchmarks/shootout_hashmaps.cpp b/misc/benchmarks/shootout_hashmaps.cpp index 947a35b4..bae9a42b 100644 --- a/misc/benchmarks/shootout_hashmaps.cpp +++ b/misc/benchmarks/shootout_hashmaps.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #define MAX_LOAD_FACTOR 85 @@ -40,8 +40,8 @@ KHASH_MAP_INIT_INT64(ii, IValue) #define i_max_load_factor MAX_LOAD_FACTOR / 100.0f #include -#define SEED(s) rng = stc64_new(s) -#define RAND(N) (stc64_rand(&rng) & (((uint64_t)1 << N) - 1)) +#define SEED(s) rng = crand_init(s) +#define RAND(N) (crand_u64(&rng) & (((uint64_t)1 << N) - 1)) #define CMAP_SETUP(X, Key, Value) cmap_##X map = cmap_##X##_init() #define CMAP_PUT(X, key, val) cmap_##X##_insert_or_assign(&map, key, val).ref->second @@ -314,7 +314,7 @@ int main(int argc, char* argv[]) unsigned keybits = argc >= 3 ? atoi(argv[2]) : DEFAULT_KEYBITS; unsigned n = n_mill * 1000000; unsigned N1 = n, N2 = n, N3 = n, N4 = n, N5 = n; - stc64_t rng; + crand_t rng; size_t seed = 123456; // time(NULL); printf("\nUnordered hash map shootout\n"); diff --git a/misc/benchmarks/various/cbits_benchmark.cpp b/misc/benchmarks/various/cbits_benchmark.cpp index dd709db1..1764f556 100644 --- a/misc/benchmarks/various/cbits_benchmark.cpp +++ b/misc/benchmarks/various/cbits_benchmark.cpp @@ -5,7 +5,7 @@ enum{ N=1<<22 }; // 4.2 mill. #define i_static -#include +#include #define i_type cbits #define i_len N #include @@ -39,12 +39,12 @@ int main(int argc, char **argv) one_sec_delay(); total = 0; - csrandom(seed); + csrand(seed); current_time = get_time_in_ms(); c_forrange (40 * N) { - uint64_t r = crandom(); + uint64_t r = crand(); bools[r & (N-1)] = r & 1<<29; } @@ -66,13 +66,13 @@ int main(int argc, char **argv) one_sec_delay(); total = 0; - csrandom(seed); + csrand(seed); current_time = get_time_in_ms(); bitset bits; c_forrange (40 * N) { - uint64_t r = crandom(); + uint64_t r = crand(); bits[r & (N-1)] = r & 1<<29; } @@ -92,13 +92,13 @@ int main(int argc, char **argv) one_sec_delay(); total = 0; - csrandom(seed); + csrand(seed); current_time = get_time_in_ms(); cbits bits2 = cbits_with_size(N, false); c_forrange (40 * N) { - uint64_t r = crandom(); + uint64_t r = crand(); cbits_set_value(&bits2, r & (N-1), r & 1<<29); } diff --git a/misc/benchmarks/various/prng_bench.cpp b/misc/benchmarks/various/prng_bench.cpp index be07f799..234e3805 100644 --- a/misc/benchmarks/various/prng_bench.cpp +++ b/misc/benchmarks/various/prng_bench.cpp @@ -2,7 +2,7 @@ #include #include #include -#include +#include static inline uint64_t rotl64(const uint64_t x, const int k) { return (x << k) | (x >> (64 - k)); } @@ -124,7 +124,7 @@ int main(void) { enum {N = 500000000}; uint16_t* recipient = new uint16_t[N]; - static stc64_t rng; + static crand_t rng; init_state(rng.state, 12345123); std::mt19937 mt(12345123); @@ -187,7 +187,7 @@ int main(void) beg = clock(); for (size_t i = 0; i < N; i++) - recipient[i] = stc64_rand(&rng); + recipient[i] = crand_u64(&rng); end = clock(); cout << "stc64:\t\t" << (float(end - beg) / CLOCKS_PER_SEC) diff --git a/misc/benchmarks/various/sso_bench.cpp b/misc/benchmarks/various/sso_bench.cpp index 0fffef7a..993ff1bb 100644 --- a/misc/benchmarks/various/sso_bench.cpp +++ b/misc/benchmarks/various/sso_bench.cpp @@ -2,7 +2,7 @@ #include #include -#include +#include #include #define i_type StcVec @@ -30,7 +30,7 @@ static inline std::string randomString_STD(int strsize) { char* p = &s[0]; union { uint64_t u8; uint8_t b[8]; } r; for (int i = 0; i < strsize; ++i) { - if ((i & 7) == 0) r.u8 = crandom() & 0x3f3f3f3f3f3f3f3f; + if ((i & 7) == 0) r.u8 = crand() & 0x3f3f3f3f3f3f3f3f; p[i] = CHARS[r.b[i & 7]]; } return s; @@ -41,7 +41,7 @@ static inline cstr randomString_STC(int strsize) { char* p = cstr_data(&s); union { uint64_t u8; uint8_t b[8]; } r; for (int i = 0; i < strsize; ++i) { - if ((i & 7) == 0) r.u8 = crandom() & 0x3f3f3f3f3f3f3f3f; + if ((i & 7) == 0) r.u8 = crand() & 0x3f3f3f3f3f3f3f3f; p[i] = CHARS[r.b[i & 7]]; } return s; @@ -85,7 +85,7 @@ int main() { // VECTOR WITH STRINGS - csrandom(seed); + csrand(seed); sum = 0, n = 0; std::cerr << "\nstrsize\tmsecs\tstd::vector, size=" << BENCHMARK_SIZE << "\n"; for (int strsize = 1; strsize <= MAX_STRING_SIZE; strsize += 2) { @@ -95,7 +95,7 @@ int main() { } std::cout << "Avg:\t" << sum/n << '\n'; - csrandom(seed); + csrand(seed); sum = 0, n = 0; std::cerr << "\nstrsize\tmsecs\tcvec, size=" << BENCHMARK_SIZE << "\n"; for (int strsize = 1; strsize <= MAX_STRING_SIZE; strsize += 2) { @@ -108,7 +108,7 @@ int main() { // SORTED SET WITH STRINGS - csrandom(seed); + csrand(seed); sum = 0, n = 0; std::cerr << "\nstrsize\tmsecs\tstd::set, size=" << BENCHMARK_SIZE/16 << "\n"; for (int strsize = 1; strsize <= MAX_STRING_SIZE; strsize += 2) { @@ -118,7 +118,7 @@ int main() { } std::cout << "Avg:\t" << sum/n << '\n'; - csrandom(seed); + csrand(seed); sum = 0, n = 0; std::cerr << "\nstrsize\tmsecs\tcsset, size=" << BENCHMARK_SIZE/16 << "\n"; for (int strsize = 1; strsize <= MAX_STRING_SIZE; strsize += 2) { diff --git a/misc/examples/birthday.c b/misc/examples/birthday.c index 2b52cc48..c301128a 100644 --- a/misc/examples/birthday.c +++ b/misc/examples/birthday.c @@ -1,7 +1,7 @@ #include #include #include -#include +#include #define i_tag ic #define i_key uint64_t @@ -17,11 +17,11 @@ static void test_repeats(void) const static uint64_t mask = (1ull << BITS) - 1; printf("birthday paradox: value range: 2^%d, testing repeats of 2^%d values\n", BITS, BITS_TEST); - stc64_t rng = stc64_new(seed); + crand_t rng = crand_init(seed); cmap_ic m = cmap_ic_with_capacity(N); c_forrange (i, N) { - uint64_t k = stc64_rand(&rng) & mask; + uint64_t k = crand_u64(&rng) & mask; int v = cmap_ic_insert(&m, k, 0).ref->second += 1; if (v > 1) printf("repeated value %" PRIu64 " (%d) at 2^%d\n", k, v, (int)log2((double)i)); @@ -38,12 +38,12 @@ void test_distribution(void) { enum {BITS = 26}; printf("distribution test: 2^%d values\n", BITS); - stc64_t rng = stc64_new(seed); + crand_t rng = crand_init(seed); const size_t N = 1ull << BITS ; cmap_x map = {0}; c_forrange (N) { - uint64_t k = stc64_rand(&rng); + uint64_t k = crand_u64(&rng); cmap_x_insert(&map, k & 0xf, 0).ref->second += 1; } diff --git a/misc/examples/gauss2.c b/misc/examples/gauss2.c index 7fede5aa..df709d03 100644 --- a/misc/examples/gauss2.c +++ b/misc/examples/gauss2.c @@ -1,7 +1,7 @@ #include #include -#include +#include #include // Declare int -> int sorted map. @@ -13,21 +13,21 @@ int main() { enum {N = 5000000}; uint64_t seed = (uint64_t)time(NULL); - stc64_t rng = stc64_new(seed); - const double Mean = round(stc64_randf(&rng)*98.f - 49.f), StdDev = stc64_randf(&rng)*10.f + 1.f, Scale = 74.f; + crand_t rng = crand_init(seed); + const double Mean = round(crand_f64(&rng)*98.f - 49.f), StdDev = crand_f64(&rng)*10.f + 1.f, Scale = 74.f; printf("Demo of gaussian / normal distribution of %d random samples\n", N); printf("Mean %f, StdDev %f\n", Mean, StdDev); // Setup random engine with normal distribution. - stc64_normalf_t dist = stc64_normalf_new(Mean, StdDev); + crand_norm_t dist = crand_norm_init(Mean, StdDev); // Create and init histogram map with defered destruct csmap_int hist = {0}; cstr bar = {0}; c_forrange (N) { - int index = (int)round( stc64_normalf(&rng, &dist) ); + int index = (int)round(crand_norm(&rng, &dist)); csmap_int_insert(&hist, index, 0).ref->second += 1; } diff --git a/misc/examples/list.c b/misc/examples/list.c index 9f0b2504..eb81067d 100644 --- a/misc/examples/list.c +++ b/misc/examples/list.c @@ -1,7 +1,7 @@ #include #include #include -#include +#include #define i_type DList #define i_val double @@ -11,11 +11,10 @@ int main() { const int n = 3000000; DList list = {0}; - stc64_t rng = stc64_new(1234567); - stc64_uniformf_t dist = stc64_uniformf_new(100.0f, n); + crand_t rng = crand_init(1234567); int m = 0; c_forrange (n) - DList_push_back(&list, stc64_uniformf(&rng, &dist)), ++m; + DList_push_back(&list, crand_f64(&rng)*n + 100), ++m; double sum = 0.0; printf("sumarize %d:\n", m); diff --git a/misc/examples/new_queue.c b/misc/examples/new_queue.c index f7887ffb..044e44cb 100644 --- a/misc/examples/new_queue.c +++ b/misc/examples/new_queue.c @@ -1,4 +1,4 @@ -#include +#include #include #include #include @@ -22,20 +22,20 @@ int point_cmp(const Point* a, const Point* b) { int main() { int n = 50000000; - stc64_t rng = stc64_new((uint64_t)time(NULL)); - stc64_uniform_t dist = stc64_uniform_new(0, n); + crand_t rng = crand_init((uint64_t)time(NULL)); + crand_unif_t dist = crand_unif_init(0, n); IQ Q = {0}; // Push 50'000'000 random numbers onto the queue. c_forrange (n) - IQ_push(&Q, (int)stc64_uniform(&rng, &dist)); + IQ_push(&Q, (int)crand_unif(&rng, &dist)); // Push or pop on the queue 50 million times printf("befor: size %" c_ZI ", capacity %" c_ZI "\n", IQ_size(&Q), IQ_capacity(&Q)); c_forrange (n) { - int r = (int)stc64_uniform(&rng, &dist); + int r = (int)crand_unif(&rng, &dist); if (r & 3) IQ_push(&Q, r); else diff --git a/misc/examples/priority.c b/misc/examples/priority.c index d3d0283e..95dd3183 100644 --- a/misc/examples/priority.c +++ b/misc/examples/priority.c @@ -1,7 +1,7 @@ #include #include -#include +#include #define i_val int64_t #define i_cmp -c_default_cmp // min-heap (increasing values) @@ -10,22 +10,22 @@ int main() { intptr_t N = 10000000; - stc64_t rng = stc64_new((uint64_t)time(NULL)); - stc64_uniform_t dist = stc64_uniform_new(0, N * 10); + crand_t rng = crand_init((uint64_t)time(NULL)); + crand_unif_t dist = crand_unif_init(0, N * 10); cpque_i heap = {0}; // Push ten million random numbers to priority queue printf("Push %" c_ZI " numbers\n", N); c_forrange (N) - cpque_i_push(&heap, stc64_uniform(&rng, &dist)); + cpque_i_push(&heap, crand_unif(&rng, &dist)); // push some negative numbers too. c_forlist (i, int, {-231, -32, -873, -4, -343}) cpque_i_push(&heap, *i.ref); c_forrange (N) - cpque_i_push(&heap, stc64_uniform(&rng, &dist)); + cpque_i_push(&heap, crand_unif(&rng, &dist)); puts("Extract the hundred smallest."); c_forrange (100) { diff --git a/misc/examples/queue.c b/misc/examples/queue.c index 0f387d52..83c18d09 100644 --- a/misc/examples/queue.c +++ b/misc/examples/queue.c @@ -1,4 +1,4 @@ -#include +#include #include #define i_val int @@ -7,20 +7,20 @@ int main() { int n = 100000000; - stc64_uniform_t dist; - stc64_t rng = stc64_new(1234); - dist = stc64_uniform_new(0, n); + crand_unif_t dist; + crand_t rng = crand_init(1234); + dist = crand_unif_init(0, n); cqueue_i queue = {0}; // Push ten million random numbers onto the queue. c_forrange (n) - cqueue_i_push(&queue, (int)stc64_uniform(&rng, &dist)); + cqueue_i_push(&queue, (int)crand_unif(&rng, &dist)); // Push or pop on the queue ten million times printf("%d\n", n); c_forrange (n) { // forrange uses initial n only. - int r = (int)stc64_uniform(&rng, &dist); + int r = (int)crand_unif(&rng, &dist); if (r & 1) ++n, cqueue_i_push(&queue, r); else diff --git a/misc/examples/random.c b/misc/examples/random.c index e27279a0..ea9c483e 100644 --- a/misc/examples/random.c +++ b/misc/examples/random.c @@ -1,12 +1,12 @@ #include #include -#include +#include int main() { const size_t N = 1000000000; const uint64_t seed = (uint64_t)time(NULL), range = 1000000; - stc64_t rng = stc64_new(seed); + crand_t rng = crand_init(seed); int64_t sum; clock_t diff, before; @@ -15,28 +15,28 @@ int main() sum = 0; before = clock(); c_forrange (N) { - sum += (uint32_t)stc64_rand(&rng); + sum += (uint32_t)crand_u64(&rng); } diff = clock() - before; printf("full range\t\t: %f secs, %" c_ZI ", avg: %f\n", (float)diff / CLOCKS_PER_SEC, N, (float)sum / (float)N); - stc64_uniform_t dist1 = stc64_uniform_new(0, range); - rng = stc64_new(seed); + crand_unif_t dist1 = crand_unif_init(0, range); + rng = crand_init(seed); sum = 0; before = clock(); c_forrange (N) { - sum += stc64_uniform(&rng, &dist1); // unbiased + sum += crand_unif(&rng, &dist1); // unbiased } diff = clock() - before; printf("unbiased 0-%" PRIu64 "\t: %f secs, %" c_ZI ", avg: %f\n", range, (float)diff/CLOCKS_PER_SEC, N, (float)sum / (float)N); sum = 0; - rng = stc64_new(seed); + rng = crand_init(seed); before = clock(); c_forrange (N) { - sum += (int64_t)(stc64_rand(&rng) % (range + 1)); // biased + sum += (int64_t)(crand_u64(&rng) % (range + 1)); // biased } diff = clock() - before; printf("biased 0-%" PRIu64 " \t: %f secs, %" c_ZI ", avg: %f\n", -- cgit v1.2.3 From 4abc7fe5bae28b7765d448ca8210548a9f0fa287 Mon Sep 17 00:00:00 2001 From: Tyge Løvset Date: Sun, 2 Apr 2023 22:13:48 +0200 Subject: Renamed c_flt_last(i) => c_flt_n(i) in algo/filter.h --- docs/ccommon_api.md | 16 ++++++++-------- include/stc/algo/filter.h | 2 +- misc/examples/list.c | 4 ++-- misc/examples/prime.c | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) (limited to 'docs') diff --git a/docs/ccommon_api.md b/docs/ccommon_api.md index 46966fd9..164abafe 100644 --- a/docs/ccommon_api.md +++ b/docs/ccommon_api.md @@ -85,14 +85,14 @@ Iterate containers with stop-criteria and chained range filtering. | `c_forfilter (it, ctype, container, filter)` | Filter out items in chain with && | | `c_forfilter_it (it, ctype, startit, filter)` | Filter from startit position | -| Built-in filter | Description | -|:----------------------------------|:-------------------------------------| -| `c_flt_skip(it, numItems)` | Skip numItems (inc count) | -| `c_flt_take(it, numItems)` | Take numItems (inc count) | -| `c_flt_skipwhile(it, predicate)` | Skip items until predicate is false | -| `c_flt_takewhile(it, predicate)` | Take items until predicate is false | -| `c_flt_count(it)` | Increment current and return value | -| `c_flt_last(it)` | Get value of last count/skip*/take* | +| Built-in filter | Description | +|:----------------------------------|:----------------------------------------| +| `c_flt_skip(it, numItems)` | Skip numItems (inc count) | +| `c_flt_take(it, numItems)` | Take numItems (inc count) | +| `c_flt_skipwhile(it, predicate)` | Skip items until predicate is false | +| `c_flt_takewhile(it, predicate)` | Take items until predicate is false | +| `c_flt_count(it)` | Increment current and return count | +| `c_flt_n(it)` | Return n items passed count/skip*/take* | ```c // Example: #include diff --git a/include/stc/algo/filter.h b/include/stc/algo/filter.h index 111d3273..d0687561 100644 --- a/include/stc/algo/filter.h +++ b/include/stc/algo/filter.h @@ -56,7 +56,7 @@ int main() #define c_flt_take(i, n) _flt_take(&(i).b, n) #define c_flt_takewhile(i, pred) _flt_takewhile(&(i).b, pred) #define c_flt_count(i) ++(i).b.s1[(i).b.s1top++] -#define c_flt_last(i) (i).b.s1[(i).b.s1top - 1] +#define c_flt_n(i) (i).b.s1[(i).b.s1top - 1] #define c_forfilter(i, C, cnt, filter) \ c_forfilter_it(i, C, C##_begin(&cnt), filter) diff --git a/misc/examples/list.c b/misc/examples/list.c index eb81067d..620c2037 100644 --- a/misc/examples/list.c +++ b/misc/examples/list.c @@ -23,14 +23,14 @@ int main() { printf("sum %f\n\n", sum); c_forfilter (i, DList, list, c_flt_take(i, 10)) - printf("%8d: %10f\n", c_flt_last(i), *i.ref); + printf("%8d: %10f\n", c_flt_n(i), *i.ref); puts("sort"); DList_sort(&list); // qsort O(n*log n) puts("sorted"); c_forfilter (i, DList, list, c_flt_take(i, 10)) - printf("%8d: %10f\n", c_flt_last(i), *i.ref); + printf("%8d: %10f\n", c_flt_n(i), *i.ref); puts(""); DList_drop(&list); diff --git a/misc/examples/prime.c b/misc/examples/prime.c index 34d64f10..d78f3ad3 100644 --- a/misc/examples/prime.c +++ b/misc/examples/prime.c @@ -47,7 +47,7 @@ int main(void) c_flt_take(i, 50) ){ printf("%lld ", *i.ref); - if (c_flt_last(i) % 10 == 0) puts(""); + if (c_flt_n(i) % 10 == 0) puts(""); } cbits_drop(&primes); -- cgit v1.2.3 From e88b655ca8cf28d357f5088c205857954ad269e2 Mon Sep 17 00:00:00 2001 From: Tyge Lovset Date: Mon, 3 Apr 2023 07:35:39 +0200 Subject: Renamed c_flt_n() => c_flt_getcount(), and c_flt_count() => c_flt_counter(). --- docs/ccommon_api.md | 24 ++++++++++++------------ include/stc/algo/filter.h | 6 +++--- misc/examples/list.c | 4 ++-- misc/examples/prime.c | 2 +- 4 files changed, 18 insertions(+), 18 deletions(-) (limited to 'docs') diff --git a/docs/ccommon_api.md b/docs/ccommon_api.md index 164abafe..6a0b4ee7 100644 --- a/docs/ccommon_api.md +++ b/docs/ccommon_api.md @@ -85,14 +85,14 @@ Iterate containers with stop-criteria and chained range filtering. | `c_forfilter (it, ctype, container, filter)` | Filter out items in chain with && | | `c_forfilter_it (it, ctype, startit, filter)` | Filter from startit position | -| Built-in filter | Description | -|:----------------------------------|:----------------------------------------| -| `c_flt_skip(it, numItems)` | Skip numItems (inc count) | -| `c_flt_take(it, numItems)` | Take numItems (inc count) | -| `c_flt_skipwhile(it, predicate)` | Skip items until predicate is false | -| `c_flt_takewhile(it, predicate)` | Take items until predicate is false | -| `c_flt_count(it)` | Increment current and return count | -| `c_flt_n(it)` | Return n items passed count/skip*/take* | +| Built-in filter | Description | +|:----------------------------------|:-------------------------------------------| +| `c_flt_skip(it, numItems)` | Skip numItems (inc count) | +| `c_flt_take(it, numItems)` | Take numItems (inc count) | +| `c_flt_skipwhile(it, predicate)` | Skip items until predicate is false | +| `c_flt_takewhile(it, predicate)` | Take items until predicate is false | +| `c_flt_counter(it)` | Increment current and return count | +| `c_flt_getcount(it)` | Number of items passed skip*/take*/counter | ```c // Example: #include @@ -110,9 +110,9 @@ int main() { crange R = crange_make(1001, INT32_MAX, 2); c_forfilter (i, crange, R, - isPrime(*i.ref) && - c_flt_skip(i, 24) && - c_flt_count(i) % 15 == 1 && + isPrime(*i.ref) && + c_flt_skip(i, 24) && + c_flt_counter(i) % 15 == 1 && c_flt_take(i, 10) ){ printf(" %lld", *i.ref); @@ -121,7 +121,7 @@ int main() { } // out: 1171 1283 1409 1493 1607 1721 1847 1973 2081 2203 ``` -Note that `c_flt_take()` and `c_flt_takewhile()`breaks the loop on false. +Note that `c_flt_take()` and `c_flt_takewhile()` breaks the loop on false. ## Generators diff --git a/include/stc/algo/filter.h b/include/stc/algo/filter.h index d0687561..e133577c 100644 --- a/include/stc/algo/filter.h +++ b/include/stc/algo/filter.h @@ -51,12 +51,12 @@ int main() // c_forfilter: -#define c_flt_skip(i, n) (c_flt_count(i) > (n)) +#define c_flt_skip(i, n) (c_flt_counter(i) > (n)) #define c_flt_skipwhile(i, pred) ((i).b.s2[(i).b.s2top++] |= !(pred)) #define c_flt_take(i, n) _flt_take(&(i).b, n) #define c_flt_takewhile(i, pred) _flt_takewhile(&(i).b, pred) -#define c_flt_count(i) ++(i).b.s1[(i).b.s1top++] -#define c_flt_n(i) (i).b.s1[(i).b.s1top - 1] +#define c_flt_counter(i) ++(i).b.s1[(i).b.s1top++] +#define c_flt_getcount(i) (i).b.s1[(i).b.s1top - 1] #define c_forfilter(i, C, cnt, filter) \ c_forfilter_it(i, C, C##_begin(&cnt), filter) diff --git a/misc/examples/list.c b/misc/examples/list.c index 620c2037..363d7fec 100644 --- a/misc/examples/list.c +++ b/misc/examples/list.c @@ -23,14 +23,14 @@ int main() { printf("sum %f\n\n", sum); c_forfilter (i, DList, list, c_flt_take(i, 10)) - printf("%8d: %10f\n", c_flt_n(i), *i.ref); + printf("%8d: %10f\n", c_flt_getcount(i), *i.ref); puts("sort"); DList_sort(&list); // qsort O(n*log n) puts("sorted"); c_forfilter (i, DList, list, c_flt_take(i, 10)) - printf("%8d: %10f\n", c_flt_n(i), *i.ref); + printf("%8d: %10f\n", c_flt_getcount(i), *i.ref); puts(""); DList_drop(&list); diff --git a/misc/examples/prime.c b/misc/examples/prime.c index d78f3ad3..a576a85c 100644 --- a/misc/examples/prime.c +++ b/misc/examples/prime.c @@ -47,7 +47,7 @@ int main(void) c_flt_take(i, 50) ){ printf("%lld ", *i.ref); - if (c_flt_n(i) % 10 == 0) puts(""); + if (c_flt_getcount(i) % 10 == 0) puts(""); } cbits_drop(&primes); -- cgit v1.2.3 From 13eb85e05a88633454df7b62b80737fcc9d12238 Mon Sep 17 00:00:00 2001 From: Tyge Lovset Date: Fri, 7 Apr 2023 13:33:06 +0200 Subject: Massive documentation update/improvements. Reduced benchmarks/plotbench repetition/sizes. --- README.md | 295 ++++++++++++++++---------- docs/ccommon_api.md | 191 ++++++++++++----- include/stc/algo/coroutine.h | 10 +- misc/benchmarks/plotbench/cdeq_benchmark.cpp | 4 +- misc/benchmarks/plotbench/clist_benchmark.cpp | 4 +- misc/benchmarks/plotbench/cmap_benchmark.cpp | 6 +- misc/benchmarks/plotbench/cpque_benchmark.cpp | 4 +- misc/benchmarks/plotbench/csmap_benchmark.cpp | 4 +- misc/benchmarks/plotbench/cvec_benchmark.cpp | 2 +- misc/benchmarks/plotbench/plot.py | 2 +- misc/benchmarks/plotbench/run_all.bat | 4 +- misc/benchmarks/plotbench/run_clang.sh | 2 +- misc/benchmarks/plotbench/run_gcc.sh | 4 +- misc/benchmarks/plotbench/run_vc.bat | 3 +- 14 files changed, 347 insertions(+), 188 deletions(-) (limited to 'docs') diff --git a/README.md b/README.md index 3f985b7a..224e7cb3 100644 --- a/README.md +++ b/README.md @@ -3,39 +3,16 @@ STC - Smart Template Containers for C ===================================== -News: Version 4.2 Released (2023-03-26) ------------------------------------------------- -I am happy to finally announce a new release! Major changes: -- A new exciting [**cspan**](docs/cspan_api.md) single/multi-dimensional array view (with numpy-like slicing). -- Signed sizes and indices for all containers. See C++ Core Guidelines by Stroustrup/Sutter: [ES.100](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#es100-dont-mix-signed-and-unsigned-arithmetic), [ES.102](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#es102-use-signed-types-for-arithmetic), [ES.106](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#es106-dont-try-to-avoid-negative-values-by-using-unsigned), and [ES.107](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#es107-dont-use-unsigned-for-subscripts-prefer-gslindex). -- Customizable allocator [per templated container type](https://github.com/tylov/STC/discussions/44#discussioncomment-4891925). -- Updates on **cregex** with several [new unicode character classes](docs/cregex_api.md#regex-cheatsheet). -- Algorithms: - - [crange](docs/ccommon_api.md#crange) - similar to [boost::irange](https://www.boost.org/doc/libs/release/libs/range/doc/html/range/reference/ranges/irange.html) integer range generator. - - [c_forfilter](docs/ccommon_api.md#c_forfilter) - ranges-like view filtering. - - [csort](include/stc/algo/csort.h) - [fast quicksort](misc/benchmarks/various/csort_bench.c) with custom inline comparison. -- Renamed `c_ARGSV()` => `c_SV()`: **csview** print arg. Note `c_sv()` is shorthand for *csview_from()*. -- Support for [uppercase flow-control](include/stc/priv/altnames.h) macro names in ccommon.h. -- Some API changes in **cregex** and **cstr**. -- Create single header container versions with python script. -- [Previous changes for version 4](#version-4). - -Introduction ------------- -STC is a *modern*, *ergonomic*, *type-safe*, *very fast* and *compact* container library for C99. -The API has similarities with c++ STL, but is more uniform across the containers and takes inspiration from Rust -and Python as well. It is an advantage to know how these containers work in other languages, like Java, C# or C++, -but it's not required. +### [Version 4.2](#version-history) -This library allows you to manage both trivial to very complex data in a wide variety of containers -without the need for boilerplate code. You may specify element-cloning, -comparison, -destruction and -more on complex container hierarchies without resorting to cumbersome function pointers with type casting. -Usage with trivial data types is simple to use compared to most generic container libraries for C because -of its type safety with an intuitive and consistent API. - -The library is mature and well tested, so you may use it in projects. However, minor breaking API changes may -still happen. The main development of this project is finished, but I will handle PRs with bugs and improvements -in the future, and do minor modifications. +--- +Description +----------- +STC is a *modern*, *fully typesafe*, *fast* and *compact* container and algorithm library for C99. +The API naming is similar to C++ STL, but it takes inspiration from Rust and Python as well. +The library let you store and manage trivial and highly complex data in a variety of +containers with ease. It is optimized for speed, usage ergonomics, consistency, +and it creates small binaries. Containers ---------- @@ -56,31 +33,75 @@ Containers - [***cdeq*** - **std::deque** alike type](docs/cdeq_api.md) - [***cvec*** - **std::vector** alike type](docs/cvec_api.md) -Others ------- -- [***ccommon*** - Generic safe macros and algorithms](docs/ccommon_api.md) -- [***cregex*** - Regular expressions (extended from Rob Pike's regexp9)](docs/cregex_api.md) -- [***crand*** - A novel very fast *PRNG* named **stc64**](docs/crandom_api.md) -- [***coption*** - getopt() alike command line args parser](docs/coption_api.md) - -Highlights ----------- -- **No boilerplate** - With STC, no boilerplate code is needed to setup containers either with simple built-in types or containers with complex element types, which requires cleanup. Only specify what is needed, e.g. compare-, clone-, drop- functions, and leave the rest as defaults. +Algorithms +---------- +- [***common*** Generic container algorithms](docs/ccommon_api.md#algorithms) +- [***filter*** - Ranges-like filtering](docs/ccommon_api.md#c_forfilter) +- [***coroutine*** - Tiny, fast and portable coroutine implementation](docs/ccommon_api.md#coroutines) +- [***cregex*** - Regular expressions based on Rob Pike's regexp9](docs/cregex_api.md) +- [***crange*** - Generalized iota integer generator](docs/ccommon_api.md#crange) +- [***crand*** - A novel very fast *PRNG* based on ***sfc64***](docs/crandom_api.md) +- [***coption*** - A ***getopt***-alike command line argument parser](docs/coption_api.md) + +--- +List of contents +----------------- +- [Highlights](#highlights) +- [STC is unique!](#stc-is-unique) +- [Performance](#performance) +- [STC naming conventions](#stc-naming-conventions) +- [Usage](#usage) +- [Installation](#installation) +- [Specifying template parameters](#specifying-template-parameters) +- [The *emplace* methods](#the-emplace-methods) +- [The *erase* methods](#the-erase-methods) +- [User-defined container type name](#user-defined-container-type-name) +- [Forward declarations](#forward-declarations) +- [Per-container-instance customization](#per-container-instance-customization) +- [Memory efficiency](#memory-efficiency) + +--- +## Highlights + +- **No boilerplate code** - Minimal code is needed to setup containers with any element type. Specify only what is needed, e.g. ***cmp***- and/or ***clone***-, ***drop***- functions, and leave the rest as defaults. - **Fully type safe** - Because of templating, it avoids error-prone casting of container types and elements back and forth from the containers. - **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. -- **Templates** - Use `#define i_{arg}` to specify container template arguments. There are templates for element-*type*, -*comparison*, -*destruction*, -*cloning*, -*conversion types*, and more. -- **Unparalleled performance** - Some containers are much faster than the c++ STL containers, the rest are about equal in speed. -- **Fully memory managed** - All containers will destruct keys/values via destructor defined as macro parameters before including the container header. Also, smart pointers are supported and can be stored in containers, see ***carc*** and ***cbox***. -- **Uniform, easy-to-learn API** - Methods to ***construct***, ***initialize***, ***iterate*** and ***destruct*** have uniform and intuitive usage across the various containers. -- **No signed/unsigned mixing** - Unsigned sizes and indices mixed with signed in comparisons and calculations is asking for trouble. STC uses only signed numbers in the API. -- **Small footprint** - Small source code and generated executables. The executable from the example below using ***six different*** container types is only ***19 Kb in size*** compiled with gcc -O3 -s on Linux. +- **Unparalleled performance** - Maps and sets are much faster than the C++ STL containers, the remaining are similar in speed. +- **Fully memory managed** - All containers will destruct keys/values via destructor (defined as macro parameters before including the container header). Also, smart pointers are supported and can be stored in containers, see ***carc*** and ***cbox***. +- **Uniform, easy-to-learn API** - Uniform and intuitive method/type names and usage across the various containers. +- **No signed/unsigned mixing** - Unsigned sizes and indices mixed with signed in comparisons and calculations is asking for trouble. STC uses only signed numbers in the API for this reason. +- **Small footprint** - Small source code and generated executables. The executable from the example below using *four different* container types is only ***19 Kb in size*** compiled with gcc -O3 -s on Linux. - **Dual mode compilation** - By default it is a simple header-only library with inline and static methods only, but you can easily switch to create a traditional library with shared symbols, without changing existing source files. See the Installation section. - **No callback functions** - All passed template argument functions/macros are directly called from the implementation, no slow callbacks which requires storage. - **Compiles with C++ and C99** - C code can be compiled with C++ (container element types must be POD). - **Forward declaration** - Templated containers may be forward declared without including the full API/implementation. See documentation below. -Performance ------------ +--- +## STC is unique! + +1. ***Centralized analysis of template parameters***. The analyser assigns values to all +non-specified template parameters (based on the specified ones) using meta-programming, +so that you don't have to! You may specify a set of "standard" template parameters for each container, +but as a minimum *only one is required*: `i_val` (+ `i_key` for maps). In this case STC assumes +that the elements are of basic types. For more complex types, additional template parameters must be given. +2. ***Alternative insert/lookup type***. You may specify an alternative type to use for lookup in +containers. E.g., containers with STC string elements (**cstr**) uses `const char*` as lookup type, +so construction of a `cstr` (which may allocate memory) for the lookup *is not needed*. Hence, the alt. lookup +key does not need to be destroyed after use, as it is normally a POD type. Finally, the alternative +lookup type may be passed to an ***emplace***-function. E.g. instead of calling +`cvec_str_push(&vec, cstr_from("Hello"))`, you may call `cvec_str_emplace(&vec, "Hello")`, +which is functionally identical, but more convenient. +3. ***Standardized container iterators***. All containers can be iterated in the same manner, and all use the +same element access syntax. E.g.: + - `c_foreach (it, MyInts, myints) *it.ref += 42;` works for any container defined as + `MyInts` with `int` elements. + - `c_foreach (it, MyInts, it1, it2) *it.ref += 42;` iterates from `it1` up to `it2`. + +--- +## Performance + +STC is a fast and memory efficient library, and code compiles very fast: + ![Benchmark](misc/benchmarks/pics/benchmark.gif) Benchmark notes: @@ -92,8 +113,9 @@ Benchmark notes: - **deque**: *insert*: n/3 push_front(), n/3 push_back()+pop_front(), n/3 push_back(). - **map and unordered map**: *insert*: n/2 random numbers, n/2 sequential numbers. *erase*: n/2 keys in the map, n/2 random keys. -STC conventions ---------------- +--- +## STC naming conventions + - Container names are prefixed by `c`, e.g. `cvec`, `cstr`. - Public STC macros are prefixed by `c_`, e.g. `c_foreach`, `c_make`. - Template parameter macros are prefixed by `i_`, e.g. `i_val`, `i_type`. @@ -103,8 +125,7 @@ STC conventions - Con_value - Con_raw - Con_iter - - Con_ssize -- Common function names for a container type Con: +- Some common function names: - Con_init() - Con_reserve(&con, capacity) - Con_drop(&con) @@ -113,7 +134,6 @@ STC conventions - Con_clone(con) - Con_push(&con, value) - Con_emplace(&con, rawval) - - Con_put_n(&con, rawval[], n) - Con_erase_at(&con, iter) - Con_front(&con) - Con_back(&con) @@ -122,40 +142,21 @@ STC conventions - Con_next(&iter) - Con_advance(iter, n) -Some standout features of STC ------------------------------ -1. ***Centralized analysis of template arguments***. Assigns good defaults to non-specified templates. -You may specify a number of "standard" template arguments for each container, but as minimum only one is -required (two for maps). In the latter case, STC assumes the elements are basic types. For more complex types, -additional template arguments should be defined. -2. ***General "heterogeneous lookup"-like feature***. Allows specification of an alternative type to use -for lookup in containers. E.g. for containers with string type (**cstr**) elements, `const char*` is used -as lookup type. It will then use the input `const char*` directly when comparing with the string data in the -container. This avoids the construction of a new `cstr` (which possible allocates memory) for the lookup. -Finally, destruction of the lookup key (i.e. string literal) after usage is not needed (or allowed), which -is convenient in C. A great ergonomic feature is that the alternative lookup type can also be used when adding -entries into containers through using the *emplace*-functions. E.g. `cvec_str_emplace_back(&vec, "Hello")`. -3. ***Standardized container iterators***. All container can be iterated in similar manner, and uses the -same element access syntax. E.g.: - - `c_foreach (it, IntContainer, container) printf(" %d", *it.ref);` will work for -every type of container defined as `IntContainer` with `int` elements. Also the form: - - `c_foreach (it, IntContainer, it1, it2)` -may be used to iterate from `it1` up to `it2`. - -Usage ------ -The usage of the containers is similar to the c++ standard containers in STL, so it should be easy if you -are familiar with them. All containers are generic/templated, except for **cstr** and **cbits**. -No casting is used, so containers are type-safe like templates in c++. A basic usage example: +--- +## Usage +The functionality of STC containers is similar to C++ STL standard containers. All containers except for a few, +like **cstr** and **cbits** are generic/templated. No type casting is done, so containers are type-safe like +templated types in C++. However, the specification of template parameters are naturally different. Here is +a basic usage example: ```c -#define i_type Floats // Container type name; if not defined, it would be cvec_float +#define i_type Floats // Container type name; unless defined name would be cvec_float #define i_val float // Container element type #include // "instantiate" the desired container type #include int main(void) { - Floats nums = Floats_init(); + Floats nums = {0}; Floats_push(&nums, 30.f); Floats_push(&nums, 10.f); Floats_push(&nums, 20.f); @@ -165,13 +166,13 @@ int main(void) Floats_sort(&nums); - c_foreach (i, Floats, nums) // Alternative and recommended way to iterate nums. - printf(" %g", *i.ref); // i.ref is a pointer to the current element. + c_foreach (i, Floats, nums) // Alternative and recommended way to iterate nums. + printf(" %g", *i.ref); // i.ref is a pointer to the current element. Floats_drop(&nums); // cleanup memory } ``` -Switching to a different container type is easy: +You may switch to a different container type, e.g. a sorted set (csset): ```c #define i_type Floats #define i_val float @@ -180,12 +181,12 @@ Switching to a different container type is easy: int main() { - Floats nums = c_make(Floats, {30.f, 10.f, 20.f}); // Initialize with a list of floats. + Floats nums = c_make(Floats, {30.f, 10.f, 20.f}); // Initialize nums with a list of floats. Floats_push(&nums, 50.f); Floats_push(&nums, 40.f); - // print the sorted numbers - c_foreach (i, Floats, nums) // c_foreach works with any container + // already sorted, print the numbers + c_foreach (i, Floats, nums) printf(" %g", *i.ref); Floats_drop(&nums); @@ -204,7 +205,7 @@ Let's make a vector of vectors that can be cloned. All of its element vectors wi #include #define i_type Vec2D -#define i_valclass Vec // Use i_valclass when the element type has "member" functions _clone(), _drop() and _cmp(). +#define i_valclass Vec // Use i_valclass when element type has "members" _clone(), _drop() and _cmp(). #define i_opt c_no_cmp // However, disable search/sort for Vec2D because Vec_cmp() is not defined. #include @@ -229,7 +230,7 @@ int main(void) c_drop(Vec2D, &vec, &clone); // Cleanup all (6) vectors. } ``` -Here is an example of using four different container types: +This example uses four different container types: ```c #include @@ -322,9 +323,9 @@ After erasing the elements found: lst: 10 30 40 50 map: [10: 1] [30: 3] [40: 4] [50: 5] ``` +--- +## Installation -Installation ------------- Because it is headers-only, headers can simply be included in your program. By default, functions are static (some inlined). You may add the *include* folder to the **CPATH** environment variable to let GCC, Clang, and TinyC locate the headers. @@ -368,9 +369,9 @@ or define your own as descibed above. #define i_tag pnt #include // clist_pnt ``` +--- +## Specifying template parameters -Specifying template parameters ------------------------------- Each templated type requires one `#include`, even if it's the same container base type, as described earlier. The template parameters are given by a `#define i_xxxx` statement, where *xxxx* is the parameter name. The list of template parameters: @@ -420,8 +421,9 @@ NB: Do not use when defining carc/cbox types themselves. - Instead of defining `i_*clone`, you may define *i_opt c_no_clone* to disable *clone* functionality. - For `i_keyclass`, if *i_keyraw* is defined along with it, *i_keyfrom* may also be defined to enable the *emplace*-functions. NB: the signature for ***cmp***, ***eq***, and ***hash*** uses *i_keyraw* as input. -The *emplace* versus non-emplace container methods --------------------------------------------------- +--- +## The *emplace* methods + STC, like c++ STL, has two sets of methods for adding elements to containers. One set begins with **emplace**, e.g. *cvec_X_emplace_back()*. This is an ergonimic alternative to *cvec_X_push_back()* when dealing non-trivial container elements, e.g. strings, shared pointers or @@ -492,8 +494,9 @@ it = cmap_str_find(&map, "Hello"); Apart from strings, maps and sets are normally used with trivial value types. However, the last example on the **cmap** page demonstrates how to specify a map with non-trivial keys. -Erase methods -------------- +--- +## The *erase* methods + | Name | Description | Container | |:--------------------------|:-----------------------------|:--------------------------------------------| | erase() | key based | csmap, csset, cmap, cset, cstr | @@ -502,8 +505,23 @@ Erase methods | erase_n() | index based | cvec, cdeq, cstr | | remove() | remove all matching values | clist | -Forward declaring containers ----------------------------- +--- +## User-defined container type name + +Define `i_type` instead of `i_tag`: +```c +#define i_type MyVec +#define i_val int +#include + +myvec vec = MyVec_init(); +MyVec_push_back(&vec, 1); +... +``` + +--- +## Forward declarations + It is possible to forward declare containers. This is useful when a container is part of a struct, but still not expose or include the full implementation / API of the container. ```c @@ -516,7 +534,6 @@ typedef struct Dataset { cstack_pnt colors; } Dataset; -... // Implementation #define i_is_forward // flag that the container was forward declared. #define i_val struct Point @@ -524,22 +541,57 @@ typedef struct Dataset { #include ``` -User-defined container type name --------------------------------- -Define `i_type` instead of `i_tag`: +--- +## Per-container-instance customization +Sometimes it is useful to extend a container type to hold extra data, e.g. a comparison +or allocator function pointer or some context that the function pointers can use. Most +libraries solve this by adding an opaque pointer (void*) or some function pointer(s) into +the data structure for the user to manage. This solution has a few disadvantages, e.g. the +pointers are not typesafe, and they take up space when not needed. STC solves this by letting +the user create a container wrapper struct where both the container and extra data fields can +be stored. The template parameters may then access the extra data using the "container_of" +technique. + +The example below shows how to customize containers to work with PostgreSQL memory management. +It adds a MemoryContext to each container by defining the `i_extend` template parameter followed +the by inclusion of ``. ```c -#define i_type MyVec +// stcpgs.h +#define pgs_malloc(sz) MemoryContextAlloc(c_getcon(self)->memctx, sz) +#define pgs_calloc(n, sz) MemoryContextAllocZero(c_getcon(self)->memctx, (n)*(sz)) +#define pgs_realloc(p, sz) (p ? repalloc(p, sz) : pgs_malloc(sz)) +#define pgs_free(p) (p ? pfree(p) : (void)0) // pfree/repalloc does not accept NULL. + +#define i_allocator pgs +#define i_no_clone +#define i_extend MemoryContext memctx +#include +``` +Define both `i_type` and `i_con` (the container type) before including the custom header: +```c +#define i_type IMap +#define i_key int #define i_val int -#include +#define i_con csmap +#include "stcpgs.h" -myvec vec = MyVec_init(); -MyVec_push_back(&vec, 1); -... +// Note the wrapper struct type is IMapExt. IMap is accessed by .get +void maptest() +{ + IMapExt map = {.memctx=CurrentMemoryContext}; + c_forrange (i, 1, 16) + IMap_insert(&map.get, i*i, i); + + c_foreach (i, IMap, map.get) + printf("%d:%d ", i.ref->first, i.ref->second); + + IMap_drop(&map.get); +} ``` +--- +## Memory efficiency -Memory efficiency ------------------ -STC is generally very memory efficient. Type sizes: +STC is generally very memory efficient. Memory usage for the different containers: - **cstr**, **cvec**, **cstack**, **cpque**: 1 pointer, 2 intptr_t + memory for elements. - **csview**, 1 pointer, 1 intptr_t. Does not own data! - **cspan**, 1 pointer and 2 \* dimension \* int32_t. Does not own data! @@ -550,7 +602,24 @@ STC is generally very memory efficient. Type sizes: - **carc**: Type size: 1 pointer, 1 long for the reference counter + memory for the shared element. - **cbox**: Type size: 1 pointer + memory for the pointed-to element. -# Version 4 +--- +# Version History + +## Version 4.1.1 +I am happy to finally announce a new release! Major changes: +- A new exciting [**cspan**](docs/cspan_api.md) single/multi-dimensional array view (with numpy-like slicing). +- Signed sizes and indices for all containers. See C++ Core Guidelines by Stroustrup/Sutter: [ES.100](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#es100-dont-mix-signed-and-unsigned-arithmetic), [ES.102](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#es102-use-signed-types-for-arithmetic), [ES.106](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#es106-dont-try-to-avoid-negative-values-by-using-unsigned), and [ES.107](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#es107-dont-use-unsigned-for-subscripts-prefer-gslindex). +- Customizable allocator [per templated container type](https://github.com/tylov/STC/discussions/44#discussioncomment-4891925). +- Updates on **cregex** with several [new unicode character classes](docs/cregex_api.md#regex-cheatsheet). +- Algorithms: + - [crange](docs/ccommon_api.md#crange) - similar to [boost::irange](https://www.boost.org/doc/libs/release/libs/range/doc/html/range/reference/ranges/irange.html) integer range generator. + - [c_forfilter](docs/ccommon_api.md#c_forfilter) - ranges-like view filtering. + - [csort](include/stc/algo/csort.h) - [fast quicksort](misc/benchmarks/various/csort_bench.c) with custom inline comparison. +- Renamed `c_ARGSV()` => `c_SV()`: **csview** print arg. Note `c_sv()` is shorthand for *csview_from()*. +- Support for [uppercase flow-control](include/stc/priv/altnames.h) macro names in ccommon.h. +- Some API changes in **cregex** and **cstr**. +- Create single header container versions with python script. + ## API changes summary V4.0 - Added **cregex** with documentation - powerful regular expressions. diff --git a/docs/ccommon_api.md b/docs/ccommon_api.md index 6a0b4ee7..af27bd1d 100644 --- a/docs/ccommon_api.md +++ b/docs/ccommon_api.md @@ -95,8 +95,7 @@ Iterate containers with stop-criteria and chained range filtering. | `c_flt_getcount(it)` | Number of items passed skip*/take*/counter | ```c // Example: -#include -#include +#include #include bool isPrime(long long i) { @@ -105,21 +104,21 @@ bool isPrime(long long i) { return true; } int main() { - // Get 10 prime numbers starting from 1000. - // Skip the first 24 primes, then select every 15th prime. - crange R = crange_make(1001, INT32_MAX, 2); + // 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, ... c_forfilter (i, crange, R, isPrime(*i.ref) && - c_flt_skip(i, 24) && - c_flt_counter(i) % 15 == 1 && + c_flt_skip(i, 15) && + c_flt_counter(i) % 25 == 1 && c_flt_take(i, 10) ){ printf(" %lld", *i.ref); } puts(""); } -// out: 1171 1283 1409 1493 1607 1721 1847 1973 2081 2203 +// out: 1097 1289 1481 1637 1861 2039 2243 2417 2657 2803 ``` Note that `c_flt_take()` and `c_flt_takewhile()` breaks the loop on false. @@ -144,7 +143,7 @@ c_forfilter (i, crange, r1, isPrime(*i.ref)) printf(" %lld", *i.ref); // 2 3 5 7 11 13 17 19 23 29 31 -// 2. The 11 first primes: +// 2. The first 11 primes: printf("2"); c_forfilter (i, crange, crange_object(3, INT64_MAX, 2), isPrime(*i.ref) && @@ -160,7 +159,9 @@ c_forfilter (i, crange, crange_object(3, INT64_MAX, 2), - *c_make(C, {...})*: Make any container from an initializer list. Example: ```c -#define i_val_str // cstr value type +#define i_val_str // owned cstr string value type +//#define i_valclass crawstr // non-owning const char* values with strcmp/cstrhash +//#define i_val const char* // non-owning const char* values with pointer cmp/hash. #include #define i_key int @@ -216,12 +217,13 @@ c_swap(cmap_int, &map1, &map2); c_drop(cvec_i, &vec1, &vec2, &vec3); // Type-safe casting a from const (pointer): -const char* cs = "Hello"; +const char cs[] = "Hello"; char* s = c_const_cast(char*, cs); // OK int* ip = c_const_cast(int*, cs); // issues a warning! ``` ### General predefined template parameter functions + ```c int c_default_cmp(const Type*, const Type*); Type c_default_clone(Type val); // simple copy @@ -244,8 +246,97 @@ int array[] = {1, 2, 3, 4}; intptr_t n = c_arraylen(array); ``` -## Scope macros (RAII) -### c_auto, c_with, c_scope, c_defer +--- +## Coroutines +This is an improved implementation of Simon Tatham's classic C code, which utilizes +the *Duff's device* trick. However, Tatham's implementation is not typesafe, +and it always allocates the coroutine's internal state dynamically. Also, +it does not let the coroutine do self-cleanup on early finish, i.e. it +just frees the dynamically allocated memory. + +In this implementation a coroutine may have any signature, but it should +take some 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 main user-loop +skips the triples which are upscaled version of smaller ones, by checking +the gcd() function, and breaks when diagonal length >= 100: +```c +#include + +struct triples { + int n; // input: max number of triples to be generated. + int a, b, c; + int cco_state; // required member +}; + +bool triples_next(struct triples* I) { // coroutine + cco_begin(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(true); + if (--I->n == 0) cco_return; + } + } + } + } + cco_final: // required label + puts("done"); + cco_end(false); +} + +int gcd(int a, int b) { // greatest common denominator + while (b) { + int t = a % b; + a = b; + b = t; + } + return a; +} + +int main() +{ + puts("\nCoroutine triples:"); + struct triples t = {INT32_MAX}; + int n = 0; + + while (triples_next(&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); // make sure coroutine cleanup is done + } +} +``` +### Coroutine API +**Note**: `cco_yield()` may not be called inside a `switch` statement. Use `if-else-if` constructs instead. + +| | Function / operator | Description | +|:----------|:-------------------------------------|:----------------------------------------| +| | `cco_final:` | Obligatory label in coroutine | +| | `cco_return;` | Early return from the coroutine | +| `bool` | `cco_suspended(ctx)` | Is coroutine in suspended state? | +| `bool` | `cco_alive(ctx)` | Is coroutine still alive? | +| `void` | `cco_begin(ctx)` | Begin coroutine block | +| `rettype` | `cco_end(retval)` | End coroutine block with return value | +| `rettype` | `cco_end()` | End coroutine block with (void) | +| `rettype` | `cco_yield(retval)` | Yield a value | +| `rettype` | `cco_yield(corocall2, ctx2, retval)` | Yield from another coroutine and return val | +| `rettype` | `cco_yield(corocall2, ctx2)` | Yield from another coroutine (void) | +| | From the caller side: | | +| `void` | `cco_stop(ctx)` | Next call of coroutine returns `cco_end()` | +| `void` | `cco_reset(ctx)` | Reset state to initial (for reuse) | + +--- +## RAII scope macros General ***defer*** mechanics for resource acquisition. These macros allows you to specify the freeing of the resources at the point where the acquisition takes place. The **checkauto** utility described below, ensures that the `c_auto*` macros are used correctly. @@ -258,57 +349,54 @@ The **checkauto** utility described below, ensures that the `c_auto*` macros are | `c_with (Type var=init, drop)` | Declare `var`. Defer `drop...` to end of scope | | `c_with (Type var=init, pred, drop)` | Adds a predicate in order to exit early if init failed | | `c_auto (Type, var1,...,var4)` | `c_with (Type var1=Type_init(), Type_drop(&var1))` ... | -| `continue` | Exit a block above without memory leaks | +| `continue` | Exit a defer-block without resource leak | -For multiple variables, use either multiple **c_with** in sequence, or declare variable outside -scope and use **c_scope**. For convenience, **c_auto** support up to 4 variables. ```c -// `c_with` is similar to python `with`: it declares and can drop a variable after going out of scope. -bool ok = false; -c_with (uint8_t* buf = malloc(BUF_SIZE), buf != NULL, free(buf)) -c_with (FILE* fp = fopen(fname, "rb"), fp != NULL, fclose(fp)) +// `c_defer` executes the expression(s) when leaving scope. +cstr s1 = cstr_lit("Hello"), s2 = cstr_lit("world"); +c_defer (cstr_drop(&s1), cstr_drop(&s2)) { - int n = fread(buf, 1, BUF_SIZE, fp); - if (n <= 0) continue; // auto cleanup! NB do not break or return here. - ... - ok = true; + printf("%s %s\n", cstr_str(&s1), cstr_str(&s2)); } -return ok; -// `c_auto` automatically initialize and destruct up to 4 variables: -c_auto (cstr, s1, s2) +// `c_scope` syntactically "binds" initialization and defer. +static pthread_mutex_t mut; +c_scope (pthread_mutex_lock(&mut), pthread_mutex_unlock(&mut)) { - cstr_append(&s1, "Hello"); - cstr_append(&s1, " world"); - - cstr_append(&s2, "Cool"); - cstr_append(&s2, " stuff"); - - printf("%s %s\n", cstr_str(&s1), cstr_str(&s2)); + /* Do syncronized work. */ } -// `c_with` is a general variant of `c_auto`: +// `c_with` is similar to python `with`: declare a variable and defer the drop call. c_with (cstr str = cstr_lit("Hello"), cstr_drop(&str)) { cstr_append(&str, " world"); printf("%s\n", cstr_str(&str)); } -// `c_scope` is like `c_with` but works with an already declared variable. -static pthread_mutex_t mut; -c_scope (pthread_mutex_lock(&mut), pthread_mutex_unlock(&mut)) +// `c_auto` automatically initialize and drops up to 4 variables: +c_auto (cstr, s1, s2) { - /* Do syncronized work. */ + cstr_append(&s1, "Hello"); + cstr_append(&s1, " world"); + cstr_append(&s2, "Cool"); + cstr_append(&s2, " stuff"); + printf("%s %s\n", cstr_str(&s1), cstr_str(&s2)); } - -// `c_defer` executes the expressions when leaving scope. Prefer c_with or c_scope. -cstr s1 = cstr_lit("Hello"), s2 = cstr_lit("world"); -c_defer (cstr_drop(&s1), cstr_drop(&s2)) +``` +**Example 1**: Use multiple **c_with** in sequence: +```c +bool ok = false; +c_with (uint8_t* buf = malloc(BUF_SIZE), buf != NULL, free(buf)) +c_with (FILE* fp = fopen(fname, "rb"), fp != NULL, fclose(fp)) { - printf("%s %s\n", cstr_str(&s1), cstr_str(&s2)); + int n = fread(buf, 1, BUF_SIZE, fp); + if (n <= 0) continue; // auto cleanup! NB do not break or return here. + ... + ok = true; } +return ok; ``` -**Example**: Load each line of a text file into a vector of strings: +**Example 2**: Load each line of a text file into a vector of strings: ```c #include #include @@ -319,20 +407,19 @@ c_defer (cstr_drop(&s1), cstr_drop(&s2)) // receiver should check errno variable cvec_str readFile(const char* name) { - cvec_str vec = cvec_str_init(); // returned - + cvec_str vec = {0}; // returned c_with (FILE* fp = fopen(name, "r"), fp != NULL, fclose(fp)) - c_with (cstr line = cstr_NULL, cstr_drop(&line)) + c_with (cstr line = {0}, cstr_drop(&line)) while (cstr_getline(&line, fp)) - cvec_str_emplace_back(&vec, cstr_str(&line)); + cvec_str_emplace(&vec, cstr_str(&line)); return vec; } int main() { - c_with (cvec_str x = readFile(__FILE__), cvec_str_drop(&x)) - c_foreach (i, cvec_str, x) - printf("%s\n", cstr_str(i.ref)); + c_with (cvec_str vec = readFile(__FILE__), cvec_str_drop(&vec)) + c_foreach (i, cvec_str, vec) + printf("| %s\n", cstr_str(i.ref)); } ``` diff --git a/include/stc/algo/coroutine.h b/include/stc/algo/coroutine.h index d29b2cef..b0ecd6b7 100644 --- a/include/stc/algo/coroutine.h +++ b/include/stc/algo/coroutine.h @@ -83,16 +83,16 @@ enum { case __LINE__:; \ } while (0) -#define cco_yield_2(corocall, ctx) \ - cco_yield_3(corocall, ctx,) +#define cco_yield_2(corocall2, ctx2) \ + cco_yield_3(corocall2, ctx2, ) -#define cco_yield_3(corocall, ctx, retval) \ +#define cco_yield_3(corocall2, ctx2, retval) \ do { \ *_state = __LINE__; \ do { \ - corocall; if (cco_suspended(ctx)) return retval; \ + corocall2; if (cco_suspended(ctx2)) return retval; \ case __LINE__:; \ - } while (cco_alive(ctx)); \ + } while (cco_alive(ctx2)); \ } while (0) #define cco_final \ diff --git a/misc/benchmarks/plotbench/cdeq_benchmark.cpp b/misc/benchmarks/plotbench/cdeq_benchmark.cpp index a8399ea8..bb0e28c8 100644 --- a/misc/benchmarks/plotbench/cdeq_benchmark.cpp +++ b/misc/benchmarks/plotbench/cdeq_benchmark.cpp @@ -12,7 +12,7 @@ enum {INSERT, ERASE, FIND, ITER, DESTRUCT, N_TESTS}; const char* operations[] = {"insert", "erase", "find", "iter", "destruct"}; typedef struct { time_t t1, t2; uint64_t sum; float fac; } Range; typedef struct { const char* name; Range test[N_TESTS]; } Sample; -enum {SAMPLES = 2, N = 100000000, S = 0x3ffc, R = 4}; +enum {SAMPLES = 2, N = 50000000, S = 0x3ffc, R = 4}; uint64_t seed = 1, mask1 = 0xfffffff, mask2 = 0xffff; static float secs(Range s) { return (float)(s.t2 - s.t1) / CLOCKS_PER_SEC; } @@ -122,7 +122,7 @@ int main(int argc, char* argv[]) bool header = (argc > 2 && argv[2][0] == '1'); float std_sum = 0, stc_sum = 0; - c_forrange (j, N_TESTS) { + c_forrange (j, N_TESTS) { std_sum += secs(std_s[0].test[j]); stc_sum += secs(stc_s[0].test[j]); } diff --git a/misc/benchmarks/plotbench/clist_benchmark.cpp b/misc/benchmarks/plotbench/clist_benchmark.cpp index 46bd2793..01bfbf83 100644 --- a/misc/benchmarks/plotbench/clist_benchmark.cpp +++ b/misc/benchmarks/plotbench/clist_benchmark.cpp @@ -12,7 +12,7 @@ enum {INSERT, ERASE, FIND, ITER, DESTRUCT, N_TESTS}; const char* operations[] = {"insert", "erase", "find", "iter", "destruct"}; typedef struct { time_t t1, t2; uint64_t sum; float fac; } Range; typedef struct { const char* name; Range test[N_TESTS]; } Sample; -enum {SAMPLES = 2, N = 50000000, S = 0x3ffc, R = 4}; +enum {SAMPLES = 2, N = 10000000, S = 0x3ffc, R = 4}; uint64_t seed = 1, mask1 = 0xfffffff, mask2 = 0xffff; static float secs(Range s) { return (float)(s.t2 - s.t1) / CLOCKS_PER_SEC; } @@ -132,4 +132,4 @@ int main(int argc, char* argv[]) c_forrange (j, N_TESTS) printf("%s,%s n:%d,%s,%.3f,%.3f\n", comp, stc_s[0].name, N, operations[j], secs(stc_s[0].test[j]), secs(std_s[0].test[j]) ? secs(stc_s[0].test[j])/secs(std_s[0].test[j]) : 1.0f); printf("%s,%s n:%d,%s,%.3f,%.3f\n", comp, stc_s[0].name, N, "total", stc_sum, stc_sum/std_sum); -} \ No newline at end of file +} diff --git a/misc/benchmarks/plotbench/cmap_benchmark.cpp b/misc/benchmarks/plotbench/cmap_benchmark.cpp index 0582d162..6b2edbd7 100644 --- a/misc/benchmarks/plotbench/cmap_benchmark.cpp +++ b/misc/benchmarks/plotbench/cmap_benchmark.cpp @@ -11,7 +11,7 @@ enum {INSERT, ERASE, FIND, ITER, DESTRUCT, N_TESTS}; const char* operations[] = {"insert", "erase", "find", "iter", "destruct"}; typedef struct { time_t t1, t2; uint64_t sum; float fac; } Range; typedef struct { const char* name; Range test[N_TESTS]; } Sample; -enum {SAMPLES = 2, N = 8000000, R = 4}; +enum {SAMPLES = 2, N = 2000000, R = 4}; uint64_t seed = 1, mask1 = 0xffffffff; static float secs(Range s) { return (float)(s.t2 - s.t1) / CLOCKS_PER_SEC; } @@ -92,7 +92,7 @@ Sample test_stc_unordered_map() { s.test[FIND].t1 = clock(); size_t sum = 0; const cmap_x_value* val; - c_forrange (N) + c_forrange (N) if ((val = cmap_x_get(&con, crand() & mask1))) sum += val->second; s.test[FIND].t2 = clock(); @@ -139,4 +139,4 @@ int main(int argc, char* argv[]) c_forrange (j, N_TESTS) printf("%s,%s n:%d,%s,%.3f,%.3f\n", comp, stc_s[0].name, N, operations[j], secs(stc_s[0].test[j]), secs(std_s[0].test[j]) ? secs(stc_s[0].test[j])/secs(std_s[0].test[j]) : 1.0f); printf("%s,%s n:%d,%s,%.3f,%.3f\n", comp, stc_s[0].name, N, "total", stc_sum, stc_sum/std_sum); -} \ No newline at end of file +} diff --git a/misc/benchmarks/plotbench/cpque_benchmark.cpp b/misc/benchmarks/plotbench/cpque_benchmark.cpp index da092b7f..2d4c7a28 100644 --- a/misc/benchmarks/plotbench/cpque_benchmark.cpp +++ b/misc/benchmarks/plotbench/cpque_benchmark.cpp @@ -11,7 +11,7 @@ #include static const uint32_t seed = 1234; -static const int N = 10000000; +static const int N = 2500000; void std_test() { @@ -47,7 +47,7 @@ void stc_test() printf("Built priority queue: %f secs\n", (float)(clock() - start)/(float)CLOCKS_PER_SEC); printf("%g ", *cpque_f_top(&pq)); - + start = clock(); c_forrange (i, N) { cpque_f_pop(&pq); diff --git a/misc/benchmarks/plotbench/csmap_benchmark.cpp b/misc/benchmarks/plotbench/csmap_benchmark.cpp index da3fc9cc..60f2db49 100644 --- a/misc/benchmarks/plotbench/csmap_benchmark.cpp +++ b/misc/benchmarks/plotbench/csmap_benchmark.cpp @@ -11,7 +11,7 @@ enum {INSERT, ERASE, FIND, ITER, DESTRUCT, N_TESTS}; const char* operations[] = {"insert", "erase", "find", "iter", "destruct"}; typedef struct { time_t t1, t2; uint64_t sum; float fac; } Range; typedef struct { const char* name; Range test[N_TESTS]; } Sample; -enum {SAMPLES = 2, N = 4000000, R = 4}; +enum {SAMPLES = 2, N = 1000000, R = 4}; uint64_t seed = 1, mask1 = 0xfffffff; static float secs(Range s) { return (float)(s.t2 - s.t1) / CLOCKS_PER_SEC; } @@ -93,7 +93,7 @@ Sample test_stc_map() { s.test[FIND].t1 = clock(); size_t sum = 0; const csmap_x_value* val; - c_forrange (N) + c_forrange (N) if ((val = csmap_x_get(&con, crand() & mask1))) sum += val->second; s.test[FIND].t2 = clock(); diff --git a/misc/benchmarks/plotbench/cvec_benchmark.cpp b/misc/benchmarks/plotbench/cvec_benchmark.cpp index b605f4e6..c488a01c 100644 --- a/misc/benchmarks/plotbench/cvec_benchmark.cpp +++ b/misc/benchmarks/plotbench/cvec_benchmark.cpp @@ -12,7 +12,7 @@ enum {INSERT, ERASE, FIND, ITER, DESTRUCT, N_TESTS}; const char* operations[] = {"insert", "erase", "find", "iter", "destruct"}; typedef struct { time_t t1, t2; uint64_t sum; float fac; } Range; typedef struct { const char* name; Range test[N_TESTS]; } Sample; -enum {SAMPLES = 2, N = 150000000, S = 0x3ffc, R = 4}; +enum {SAMPLES = 2, N = 80000000, S = 0x3ffc, R = 4}; uint64_t seed = 1, mask1 = 0xfffffff, mask2 = 0xffff; static float secs(Range s) { return (float)(s.t2 - s.t1) / CLOCKS_PER_SEC; } diff --git a/misc/benchmarks/plotbench/plot.py b/misc/benchmarks/plotbench/plot.py index fa538285..0ba92264 100644 --- a/misc/benchmarks/plotbench/plot.py +++ b/misc/benchmarks/plotbench/plot.py @@ -4,7 +4,7 @@ import pandas as pd import matplotlib.pyplot as plt #sns.set_theme(style="whitegrid") -comp = ['All compilers', 'Mingw-g++-10.30', 'Win-Clang-12', 'VC-19.28'] +comp = ['All compilers', 'Mingw-g++-11.3.0', 'Win-Clang-14.0.1', 'VC-19.28'] n = int(sys.argv[1]) if len(sys.argv) > 1 else 0 file = sys.argv[2] if len(sys.argv) > 2 else 'plot_win.csv' df = pd.read_csv(file) diff --git a/misc/benchmarks/plotbench/run_all.bat b/misc/benchmarks/plotbench/run_all.bat index 2edd0a1e..98913a50 100644 --- a/misc/benchmarks/plotbench/run_all.bat +++ b/misc/benchmarks/plotbench/run_all.bat @@ -1,5 +1,7 @@ set out=plot_win.csv echo Compiler,Library,C,Method,Seconds,Ratio> %out% +echo gcc sh run_gcc.sh >> %out% +echo clang sh run_clang.sh >> %out% -call run_vc.bat >> %out% +REM call run_vc.bat >> %out% diff --git a/misc/benchmarks/plotbench/run_clang.sh b/misc/benchmarks/plotbench/run_clang.sh index ae19486e..096e71be 100644 --- a/misc/benchmarks/plotbench/run_clang.sh +++ b/misc/benchmarks/plotbench/run_clang.sh @@ -6,7 +6,7 @@ clang++ -I../include -O3 -o cmap_benchmark$exe cmap_benchmark.cpp clang++ -I../include -O3 -o csmap_benchmark$exe csmap_benchmark.cpp clang++ -I../include -O3 -o cvec_benchmark$exe cvec_benchmark.cpp -c='Win-Clang-12' +c='Win-Clang-14.0.1' ./cdeq_benchmark$exe $c ./clist_benchmark$exe $c ./cmap_benchmark$exe $c diff --git a/misc/benchmarks/plotbench/run_gcc.sh b/misc/benchmarks/plotbench/run_gcc.sh index 6a6472c0..5249ed1e 100644 --- a/misc/benchmarks/plotbench/run_gcc.sh +++ b/misc/benchmarks/plotbench/run_gcc.sh @@ -4,9 +4,9 @@ g++ -I../include -O3 -o cmap_benchmark cmap_benchmark.cpp g++ -I../include -O3 -o csmap_benchmark csmap_benchmark.cpp g++ -I../include -O3 -o cvec_benchmark cvec_benchmark.cpp -c='Mingw-g++-10.30' +c='Mingw-g++-11.3.0' ./cdeq_benchmark $c ./clist_benchmark $c ./cmap_benchmark $c ./csmap_benchmark $c -./cvec_benchmark $c \ No newline at end of file +./cvec_benchmark $c diff --git a/misc/benchmarks/plotbench/run_vc.bat b/misc/benchmarks/plotbench/run_vc.bat index 3dca925b..dc4938f8 100644 --- a/misc/benchmarks/plotbench/run_vc.bat +++ b/misc/benchmarks/plotbench/run_vc.bat @@ -1,3 +1,4 @@ + @echo off if "%VSINSTALLDIR%"=="" call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat" >nul cl.exe -nologo -EHsc -std:c++latest -I../include -O2 cdeq_benchmark.cpp >nul @@ -12,4 +13,4 @@ cdeq_benchmark.exe %c% clist_benchmark.exe %c% cmap_benchmark.exe %c% csmap_benchmark.exe %c% -cvec_benchmark.exe %c% \ No newline at end of file +cvec_benchmark.exe %c% -- cgit v1.2.3 From 701b7af4aaf9fe3965c656c500d9200dd7c4831d Mon Sep 17 00:00:00 2001 From: Tyge Lovset Date: Fri, 7 Apr 2023 22:51:45 +0200 Subject: More docs updating. --- README.md | 67 +++++++-------- docs/ccommon_api.md | 208 +++++++++++++++++++++++++---------------------- include/stc/ccommon.h | 2 +- include/stc/extend.h | 2 +- misc/examples/forloops.c | 2 +- misc/examples/functor.c | 2 +- 6 files changed, 148 insertions(+), 135 deletions(-) (limited to 'docs') diff --git a/README.md b/README.md index 224e7cb3..6f07f8ff 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ STC - Smart Template Containers for C ===================================== -### [Version 4.2](#version-history) +### [Version 4.2 RC](#version-history) --- Description @@ -35,13 +35,13 @@ Containers Algorithms ---------- -- [***common*** Generic container algorithms](docs/ccommon_api.md#algorithms) -- [***filter*** - Ranges-like filtering](docs/ccommon_api.md#c_forfilter) -- [***coroutine*** - Tiny, fast and portable coroutine implementation](docs/ccommon_api.md#coroutines) -- [***cregex*** - Regular expressions based on Rob Pike's regexp9](docs/cregex_api.md) -- [***crange*** - Generalized iota integer generator](docs/ccommon_api.md#crange) -- [***crand*** - A novel very fast *PRNG* based on ***sfc64***](docs/crandom_api.md) -- [***coption*** - A ***getopt***-alike command line argument parser](docs/coption_api.md) +- [***Ranged for-loops*** - c_foreach, c_forpair, c_forlist](docs/ccommon_api.md#ranged-for-loops) +- [***Range algorithms*** - c_forrange, crange, c_forfilter](docs/ccommon_api.md#range-algorithms) +- [***Generic algorithms*** - c_make, c_find_if, c_erase_if, etc.](docs/ccommon_api.md#generic-algorithms) +- [***Coroutines*** - Simon Tatham's coroutines done right](docs/ccommon_api.md#coroutines) +- [***Regular expressions*** - modernized Rob Pike's Plan-9 regexp](docs/cregex_api.md) +- [***Random numbers*** - a novel very fast *PRNG* based on *SFC64*](docs/crandom_api.md) +- [***Command line argument parser*** - similar to *getopt()*](docs/coption_api.md) --- List of contents @@ -49,7 +49,7 @@ List of contents - [Highlights](#highlights) - [STC is unique!](#stc-is-unique) - [Performance](#performance) -- [STC naming conventions](#stc-naming-conventions) +- [Naming conventions](#naming-conventions) - [Usage](#usage) - [Installation](#installation) - [Specifying template parameters](#specifying-template-parameters) @@ -57,19 +57,19 @@ List of contents - [The *erase* methods](#the-erase-methods) - [User-defined container type name](#user-defined-container-type-name) - [Forward declarations](#forward-declarations) -- [Per-container-instance customization](#per-container-instance-customization) +- [Per container-instance customization](#per-container-instance-customization) - [Memory efficiency](#memory-efficiency) --- ## Highlights -- **No boilerplate code** - Minimal code is needed to setup containers with any element type. Specify only what is needed, e.g. ***cmp***- and/or ***clone***-, ***drop***- functions, and leave the rest as defaults. +- **No boilerplate code** - Specify only the required template parameters, e.g. ***cmp***- and/or ***clone***-, ***drop***- functions, and leave the rest as defaults. - **Fully type safe** - Because of templating, it avoids error-prone casting of container types and elements back and forth from the containers. -- **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. +- **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. - **Unparalleled performance** - Maps and sets are much faster than the C++ STL containers, the remaining are similar in speed. -- **Fully memory managed** - All containers will destruct keys/values via destructor (defined as macro parameters before including the container header). Also, smart pointers are supported and can be stored in containers, see ***carc*** and ***cbox***. -- **Uniform, easy-to-learn API** - Uniform and intuitive method/type names and usage across the various containers. -- **No signed/unsigned mixing** - Unsigned sizes and indices mixed with signed in comparisons and calculations is asking for trouble. STC uses only signed numbers in the API for this reason. +- **Fully memory managed** - Containers destructs keys/values via default or user supplied drop function. They may be cloned if element types are clonable. Also, smart pointers are supported and can be stored in containers. See ***carc*** and ***cbox***. +- **Uniform, easy-to-learn API** - Intuitive method/type names and uniform usage across the various containers. +- **No signed/unsigned mixing** - Unsigned sizes and indices mixed with signed for comparison and calculation is asking for trouble. STC only uses signed numbers in the API for this reason. - **Small footprint** - Small source code and generated executables. The executable from the example below using *four different* container types is only ***19 Kb in size*** compiled with gcc -O3 -s on Linux. - **Dual mode compilation** - By default it is a simple header-only library with inline and static methods only, but you can easily switch to create a traditional library with shared symbols, without changing existing source files. See the Installation section. - **No callback functions** - All passed template argument functions/macros are directly called from the implementation, no slow callbacks which requires storage. @@ -87,7 +87,8 @@ that the elements are of basic types. For more complex types, additional templat 2. ***Alternative insert/lookup type***. You may specify an alternative type to use for lookup in containers. E.g., containers with STC string elements (**cstr**) uses `const char*` as lookup type, so construction of a `cstr` (which may allocate memory) for the lookup *is not needed*. Hence, the alt. lookup -key does not need to be destroyed after use, as it is normally a POD type. Finally, the alternative +key does not need to be destroyed after use as it is normally a POD type. Finally, the alternative +key does not need to be destroyed after use as it is normally a POD type. Finally, the alternative lookup type may be passed to an ***emplace***-function. E.g. instead of calling `cvec_str_push(&vec, cstr_from("Hello"))`, you may call `cvec_str_emplace(&vec, "Hello")`, which is functionally identical, but more convenient. @@ -100,7 +101,7 @@ same element access syntax. E.g.: --- ## Performance -STC is a fast and memory efficient library, and code compiles very fast: +STC is a fast and memory efficient library, and code compiles fast: ![Benchmark](misc/benchmarks/pics/benchmark.gif) @@ -114,7 +115,7 @@ Benchmark notes: - **map and unordered map**: *insert*: n/2 random numbers, n/2 sequential numbers. *erase*: n/2 keys in the map, n/2 random keys. --- -## STC naming conventions +## Naming conventions - Container names are prefixed by `c`, e.g. `cvec`, `cstr`. - Public STC macros are prefixed by `c_`, e.g. `c_foreach`, `c_make`. @@ -144,10 +145,10 @@ Benchmark notes: --- ## Usage -The functionality of STC containers is similar to C++ STL standard containers. All containers except for a few, +STC containers have similar functionality to C++ STL standard containers. All containers except for a few, like **cstr** and **cbits** are generic/templated. No type casting is done, so containers are type-safe like -templated types in C++. However, the specification of template parameters are naturally different. Here is -a basic usage example: +templated types in C++. However, the specification of template parameters are naturally different. In STC, +you specify template parameters by `#define` before including the container: ```c #define i_type Floats // Container type name; unless defined name would be cvec_float #define i_val float // Container element type @@ -166,7 +167,7 @@ int main(void) Floats_sort(&nums); - c_foreach (i, Floats, nums) // Alternative and recommended way to iterate nums. + c_foreach (i, Floats, nums) // Alternative and recommended way to iterate. printf(" %g", *i.ref); // i.ref is a pointer to the current element. Floats_drop(&nums); // cleanup memory @@ -181,7 +182,7 @@ You may switch to a different container type, e.g. a sorted set (csset): int main() { - Floats nums = c_make(Floats, {30.f, 10.f, 20.f}); // Initialize nums with a list of floats. + Floats nums = c_make(Floats, {30.f, 10.f, 20.f}); // Initialize with a list of floats. Floats_push(&nums, 50.f); Floats_push(&nums, 40.f); @@ -192,11 +193,11 @@ int main() Floats_drop(&nums); } ``` -For user-defined struct elements, `i_cmp` compare function should be defined, as the default `<` and `==` +For user-defined struct elements, `i_cmp` compare function should be defined as the default `<` and `==` only works for integral types. *Alternatively, `#define i_opt c_no_cmp` to disable sorting and searching*. Similarily, if an element destructor `i_valdrop` is defined, `i_valclone` function is required. *Alternatively `#define i_opt c_no_clone` to disable container cloning.* -Let's make a vector of vectors that can be cloned. All of its element vectors will be destroyed when destroying the Vec2D. +Let's make a vector of vectors, which can be cloned. All of its element vectors will be destroyed when destroying the Vec2D. ```c #include @@ -206,7 +207,7 @@ Let's make a vector of vectors that can be cloned. All of its element vectors wi #define i_type Vec2D #define i_valclass Vec // Use i_valclass when element type has "members" _clone(), _drop() and _cmp(). -#define i_opt c_no_cmp // However, disable search/sort for Vec2D because Vec_cmp() is not defined. +#define i_opt c_no_cmp // Disable cmp (search/sort) for Vec2D because Vec_cmp() is not defined. #include int main(void) @@ -542,11 +543,11 @@ typedef struct Dataset { ``` --- -## Per-container-instance customization -Sometimes it is useful to extend a container type to hold extra data, e.g. a comparison -or allocator function pointer or some context that the function pointers can use. Most -libraries solve this by adding an opaque pointer (void*) or some function pointer(s) into -the data structure for the user to manage. This solution has a few disadvantages, e.g. the +## Per container-instance customization +Sometimes it is useful to extend a container type to store extra data, e.g. a comparison +or allocator function pointer or a context which the function pointers can use. Most +libraries solve this by adding an opaque pointer (void*) or function pointer(s) into +the data structure for the user to manage. This solution has a few disadvantages: the pointers are not typesafe, and they take up space when not needed. STC solves this by letting the user create a container wrapper struct where both the container and extra data fields can be stored. The template parameters may then access the extra data using the "container_of" @@ -564,10 +565,10 @@ the by inclusion of ``. #define i_allocator pgs #define i_no_clone -#define i_extend MemoryContext memctx +#define i_extend MemoryContext memctx; #include ``` -Define both `i_type` and `i_con` (the container type) before including the custom header: +To use it, define both `i_type` and `i_con` (the container type) before including the custom header: ```c #define i_type IMap #define i_key int diff --git a/docs/ccommon_api.md b/docs/ccommon_api.md index af27bd1d..21eaf884 100644 --- a/docs/ccommon_api.md +++ b/docs/ccommon_api.md @@ -2,16 +2,17 @@ The following macros are recommended to use, and they safe/have no side-effects. -## Loop abstraction macros +--- +## Ranged for-loops -### c_foreach, c_foreach_r, c_forpair +### c_foreach, c_foreach_rv, c_forpair -| Usage | Description | -|:-----------------------------------------|:----------------------------------------| -| `c_foreach (it, ctype, container)` | Iteratate all elements | -| `c_foreach (it, ctype, it1, it2)` | Iterate the range [it1, it2) | -| `c_foreach_r (it, ctype, container)` | Iteratate in reverse (cstack,cvec,cdeq) | -| `c_forpair (key, val, ctype, container)` | Iterate with structured binding | +| 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 #define i_key int @@ -40,6 +41,25 @@ c_forpair (id, count, csmap_ii, map) // (3 2) (5 4) (7 3) (12 5) (23 1) ``` +### c_forlist +Iterate compound literal array elements. Additional to `i.ref`, you can access `i.data`, `i.size`, and `i.index` of the input list/element. +```c +// apply multiple push_backs +c_forlist (i, int, {1, 2, 3}) + cvec_i_push_back(&vec, *i.ref); + +// insert in existing map +c_forlist (i, cmap_ii_raw, { {4, 5}, {6, 7} }) + cmap_ii_insert(&map, i.ref->first, i.ref->second); + +// string literals pushed to a stack of cstr: +c_forlist (i, const char*, {"Hello", "crazy", "world"}) + cstack_str_emplace(&stk, *i.ref); +``` + +--- +## Range algorithms + ### c_forrange Abstraction for iterating sequence of integers. Like python's **for** *i* **in** *range()* loop. @@ -61,24 +81,38 @@ c_forrange (i, 30, 0, -5) printf(" %lld", i); // 30 25 20 15 10 5 ``` -### c_forlist -Iterate compound literal array elements. Additional to `i.ref`, you can access `i.data`, `i.size`, and `i.index` of the input list/element. +### crange +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 -// apply multiple push_backs -c_forlist (i, int, {1, 2, 3}) - cvec_i_push_back(&vec, *i.ref); +crange& crange_object(...) // create a compound literal crange object +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 + // note that step may be negative. +crange_iter crange_begin(crange* self); +crange_iter crange_end(crange* self); +void crange_next(crange_iter* it); -// insert in existing map -c_forlist (i, cmap_ii_raw, { {4, 5}, {6, 7} }) - cmap_ii_insert(&map, i.ref->first, i.ref->second); +// 1. All primes less than 32: +crange r1 = crange_make(3, 32, 2); +printf("2"); // first prime +c_forfilter (i, crange, r1, isPrime(*i.ref)) + printf(" %lld", *i.ref); +// 2 3 5 7 11 13 17 19 23 29 31 -// string literals pushed to a stack of cstr: -c_forlist (i, const char*, {"Hello", "crazy", "world"}) - cstack_str_emplace(&stk, *i.ref); +// 2. The first 11 primes: +printf("2"); +c_forfilter (i, crange, crange_object(3, INT64_MAX, 2), + isPrime(*i.ref) && + c_flt_take(10) +){ + printf(" %lld", *i.ref); +} +// 2 3 5 7 11 13 17 19 23 29 31 ``` ### c_forfilter -Iterate containers with stop-criteria and chained range filtering. +Iterate a container/range with chained range filtering. | Usage | Description | |:----------------------------------------------------|:---------------------------------------| @@ -122,46 +156,14 @@ int main() { ``` Note that `c_flt_take()` and `c_flt_takewhile()` breaks the loop on false. -## Generators - -### crange -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_object(...) // create a compound literal crange object -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 - // 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); -printf("2"); // first prime -c_forfilter (i, crange, r1, isPrime(*i.ref)) - printf(" %lld", *i.ref); -// 2 3 5 7 11 13 17 19 23 29 31 - -// 2. The first 11 primes: -printf("2"); -c_forfilter (i, crange, crange_object(3, INT64_MAX, 2), - isPrime(*i.ref) && - c_flt_take(10) -){ - printf(" %lld", *i.ref); -} -// 2 3 5 7 11 13 17 19 23 29 31 -``` -## Algorithms +--- +## Generic algorithms -### c_make, c_new, c_delete +### c_make, c_drop -- *c_make(C, {...})*: Make any container from an initializer list. Example: +Make any container from an initializer list: ```c -#define i_val_str // owned cstr string value type -//#define i_valclass crawstr // non-owning const char* values with strcmp/cstrhash -//#define i_val const char* // non-owning const char* values with pointer cmp/hash. +#define i_val_str // owned cstr string value type #include #define i_key int @@ -170,26 +172,21 @@ c_forfilter (i, crange, crange_object(3, INT64_MAX, 2), ... // Initializes with const char*, internally converted to cstr! cset_str myset = c_make(cset_str, {"This", "is", "the", "story"}); +cset_str myset2 = c_clone(myset); int x = 7, y = 8; cmap_int mymap = c_make(cmap_int, { {1, 2}, {3, 4}, {5, 6}, {x, y} }); ``` - -- ***c_new(Type)***: Allocate *and init* a new object on the heap -- ***c_delete(Type, ptr)***: Drop *and free* an object allocated on the heap +Drop multiple containers of the same type: ```c -#include - -cstr *stringptr = c_new(cstr, cstr_from("Hello")); -printf("%s\n", cstr_str(stringptr)); -c_delete(cstr, stringptr); +c_drop(cset_str, &myset, &myset2); ``` ### c_find_if, c_erase_if, c_eraseremove_if Find or erase linearily in containers using a predicate -- For *c_find_if (iter, C, c, pred)*, ***iter*** must be declared outside/prior to call. -- Use *c_erase_if (iter, C, c, pred)* with **clist**, **cmap**, **cset**, **csmap**, and **csset**. -- Use *c_eraseremove_if (iter, C, c, pred)* with **cstack**, **cvec**, **cdeq**, and **cqueue**. +- For `c_find_if(iter, C, c, pred)`, ***iter*** is in/out and must be declared prior to call. +- Use `c_erase_if(iter, C, c, pred)` with **clist**, **cmap**, **cset**, **csmap**, and **csset**. +- Use `c_eraseremove_if(iter, C, c, pred)` with **cstack**, **cvec**, **cdeq**, and **cqueue**. ```c // Search vec for first value > 2: cvec_i_iter i; @@ -208,51 +205,66 @@ if (it.ref) cmap_str_erase_at(&map, it); c_erase_if(i, csmap_str, map, cstr_contains(i.ref, "hello")); ``` -### c_swap, c_drop, c_const_cast +### c_new, c_delete + +- `c_new(Type, val)` - Allocate *and init* a new object on the heap +- `c_delete(Type, ptr)` - Drop *and free* an object allocated on the heap. NULL is OK. +```c +#include + +cstr *str_p = c_new(cstr, cstr_from("Hello")); +printf("%s\n", cstr_str(str_p)); +c_delete(cstr, str_p); +``` + +### c_malloc, c_calloc, c_realloc, c_free +Memory allocator wrappers that uses signed sizes. + +### c_arraylen +Return number of elements in an array. array must not be a pointer! +```c +int array[] = {1, 2, 3, 4}; +intptr_t n = c_arraylen(array); +``` + +### c_swap, c_const_cast ```c // Safe macro for swapping internals of two objects of same type: c_swap(cmap_int, &map1, &map2); -// Drop multiple containers of same type: -c_drop(cvec_i, &vec1, &vec2, &vec3); - // Type-safe casting a from const (pointer): const char cs[] = "Hello"; char* s = c_const_cast(char*, cs); // OK int* ip = c_const_cast(int*, cs); // issues a warning! ``` -### General predefined template parameter functions +### Predefined template parameter functions +**crawstr** - Non-owned `const char*` "class" element type: `#define i_valclass crawstr` ```c -int c_default_cmp(const Type*, const Type*); -Type c_default_clone(Type val); // simple copy -Type c_default_toraw(const Type* val); // dereference val -void c_default_drop(Type* val); // does nothing - typedef const char* crawstr; int crawstr_cmp(const crawstr* x, const crawstr* y); bool crawstr_eq(const crawstr* x, const crawstr* y); uint64_t crawstr_hash(const crawstr* x); ``` - -### c_malloc, c_calloc, c_realloc, c_free: customizable allocators -Memory allocator wrappers that uses signed sizes. - -### c_arraylen -Return number of elements in an array. array must not be a pointer! +Default implementations ```c -int array[] = {1, 2, 3, 4}; -intptr_t n = c_arraylen(array); +int c_default_cmp(const Type*, const Type*); // <=> +bool c_default_less(const Type*, const Type*); // < +bool c_default_eq(const Type*, const Type*); // == +uint64_t c_default_hash(const Type*); +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 an improved implementation of Simon Tatham's classic C code, which utilizes the *Duff's device* trick. However, Tatham's implementation is not typesafe, -and it always allocates the coroutine's internal state dynamically. Also, -it does not let the coroutine do self-cleanup on early finish, i.e. it -just frees the dynamically allocated memory. +and it always allocates the coroutine's internal state dynamically. But most 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 some struct pointer as parameter, which must contain the member `int cco_state;` @@ -260,8 +272,8 @@ 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 main user-loop -skips the triples which are upscaled version of smaller ones, by checking -the gcd() function, and breaks when diagonal length >= 100: +skips the triples which are upscaled version of smaller ones by checking +the gcd() function, and breaks when the diagonal size >= 100: ```c #include @@ -271,14 +283,14 @@ struct triples { int cco_state; // required member }; -bool triples_next(struct triples* I) { // coroutine - cco_begin(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) { +bool triples_next(struct triples* i) { // coroutine + cco_begin(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(true); - if (--I->n == 0) cco_return; + if (--i->n == 0) cco_return; } } } diff --git a/include/stc/ccommon.h b/include/stc/ccommon.h index 362b09ce..24ad59f9 100644 --- a/include/stc/ccommon.h +++ b/include/stc/ccommon.h @@ -182,7 +182,7 @@ STC_INLINE char* cstrnstrn(const char *str, const char *needle, for (C##_iter it = start, *_endref = (C##_iter*)(finish).ref \ ; it.ref != (C##_value*)_endref; C##_next(&it)) -#define c_foreach_r(it, C, cnt) \ +#define c_foreach_rv(it, C, cnt) \ for (C##_iter it = {.ref=C##_end(&cnt).end - 1, .end=(cnt).data - 1} \ ; it.ref != it.end; --it.ref) diff --git a/include/stc/extend.h b/include/stc/extend.h index a8cb5f5b..cbfc4a12 100644 --- a/include/stc/extend.h +++ b/include/stc/extend.h @@ -50,7 +50,7 @@ #endif typedef struct { - i_extend; + i_extend i_type get; } c_PASTE(i_type, Ext); diff --git a/misc/examples/forloops.c b/misc/examples/forloops.c index 82126456..1fc00614 100644 --- a/misc/examples/forloops.c +++ b/misc/examples/forloops.c @@ -42,7 +42,7 @@ int main() printf(" %d", *i.ref); puts("\n\nc_foreach_r: reverse"); - c_foreach_r (i, IVec, vec) + c_foreach_rv (i, IVec, vec) printf(" %d", *i.ref); puts("\n\nc_foreach in map:"); diff --git a/misc/examples/functor.c b/misc/examples/functor.c index 7cc770d0..7417080b 100644 --- a/misc/examples/functor.c +++ b/misc/examples/functor.c @@ -9,7 +9,7 @@ #define i_type IPQue #define i_val int -#define i_extend bool (*less)(const int*, const int*) +#define i_extend bool (*less)(const int*, const int*); #define i_less(x, y) c_getcon(self)->less(x, y) #define i_con cpque #include -- cgit v1.2.3 From d92ffe3da29b3352c90b2d356e395914ba5b3f52 Mon Sep 17 00:00:00 2001 From: Tyge Lovset Date: Sat, 8 Apr 2023 11:02:39 +0200 Subject: More docs updates, and a change in stc/extend.h. --- README.md | 51 +++++++++++++++++++++++++++-------------------- docs/ccommon_api.md | 33 +++++++++++++++--------------- include/stc/algo/crange.h | 4 ++-- include/stc/ccommon.h | 3 ++- include/stc/extend.h | 4 ++-- misc/examples/forfilter.c | 2 +- misc/examples/functor.c | 10 +++++----- misc/examples/prime.c | 2 +- 8 files changed, 59 insertions(+), 50 deletions(-) (limited to 'docs') diff --git a/README.md b/README.md index 6f07f8ff..220349b3 100644 --- a/README.md +++ b/README.md @@ -8,11 +8,10 @@ STC - Smart Template Containers for C --- Description ----------- -STC is a *modern*, *fully typesafe*, *fast* and *compact* container and algorithm library for C99. +STC is a *modern*, *typesafe*, *fast* and *compact* container and algorithms library for C99. The API naming is similar to C++ STL, but it takes inspiration from Rust and Python as well. -The library let you store and manage trivial and highly complex data in a variety of -containers with ease. It is optimized for speed, usage ergonomics, consistency, -and it creates small binaries. +The library handles everything from trivial to highly complex data using *templates*, and it +supports a variety of containers. Containers ---------- @@ -80,18 +79,18 @@ List of contents ## STC is unique! 1. ***Centralized analysis of template parameters***. The analyser assigns values to all -non-specified template parameters (based on the specified ones) using meta-programming, -so that you don't have to! You may specify a set of "standard" template parameters for each container, -but as a minimum *only one is required*: `i_val` (+ `i_key` for maps). In this case STC assumes -that the elements are of basic types. For more complex types, additional template parameters must be given. -2. ***Alternative insert/lookup type***. You may specify an alternative type to use for lookup in -containers. E.g., containers with STC string elements (**cstr**) uses `const char*` as lookup type, -so construction of a `cstr` (which may allocate memory) for the lookup *is not needed*. Hence, the alt. lookup -key does not need to be destroyed after use as it is normally a POD type. Finally, the alternative -key does not need to be destroyed after use as it is normally a POD type. Finally, the alternative -lookup type may be passed to an ***emplace***-function. E.g. instead of calling -`cvec_str_push(&vec, cstr_from("Hello"))`, you may call `cvec_str_emplace(&vec, "Hello")`, -which is functionally identical, but more convenient. +non-specified template parameters (based on the specified ones) using meta-programming, so +that you don't have to! You may specify a set of "standard" template parameters for each +container, but as a minimum *only one is required*: `i_val` (+ `i_key` for maps). In this +case, STC assumes that the elements are of basic types. For non-trivial types, additional +template parameters must be given. +2. ***Alternative insert/lookup type***. You may specify an alternative type to use for +lookup in containers. E.g., containers with STC string elements (**cstr**) uses `const char*` +as lookup type, so constructing a `cstr` (which may allocate memory) for the lookup +*is not needed*. Hence, the alternative lookup key does not need to be destroyed after use, +as it is normally a POD type. Finally, the key may be passed to an ***emplace***-function. +So instead of calling e.g. `cvec_str_push(&vec, cstr_from("Hello"))`, you may call +`cvec_str_emplace(&vec, "Hello")`, which is functionally identical, but more convenient. 3. ***Standardized container iterators***. All containers can be iterated in the same manner, and all use the same element access syntax. E.g.: - `c_foreach (it, MyInts, myints) *it.ref += 42;` works for any container defined as @@ -146,9 +145,9 @@ Benchmark notes: --- ## Usage STC containers have similar functionality to C++ STL standard containers. All containers except for a few, -like **cstr** and **cbits** are generic/templated. No type casting is done, so containers are type-safe like -templated types in C++. However, the specification of template parameters are naturally different. In STC, -you specify template parameters by `#define` before including the container: +like **cstr** and **cbits** are generic/templated. No type casting is used, so containers are type-safe like +templated types in C++. However, to specify template parameters with STC, you define them as macros prior to +including the container: ```c #define i_type Floats // Container type name; unless defined name would be cvec_float #define i_val float // Container element type @@ -576,10 +575,10 @@ To use it, define both `i_type` and `i_con` (the container type) before includin #define i_con csmap #include "stcpgs.h" -// Note the wrapper struct type is IMapExt. IMap is accessed by .get +// Note the wrapper struct type is IMap_ext. IMap is accessed by .get void maptest() { - IMapExt map = {.memctx=CurrentMemoryContext}; + IMap_ext map = {.memctx=CurrentMemoryContext}; c_forrange (i, 1, 16) IMap_insert(&map.get, i*i, i); @@ -605,9 +604,17 @@ STC is generally very memory efficient. Memory usage for the different container --- # Version History +## Version 4.2 +- Much improved documentation +- Added Coroutines + documentation +- Added `c_const_cast()` typesafe macro. +- Renamed c_foreach_r => `c_foreach_rv` +- Renamed c_flt_count(i) => `c_flt_counter(i)` +- Renamed c_flt_last(i) => `c_flt_getcount(i)` +- Removed c_PAIR ## Version 4.1.1 -I am happy to finally announce a new release! Major changes: +Major changes: - A new exciting [**cspan**](docs/cspan_api.md) single/multi-dimensional array view (with numpy-like slicing). - Signed sizes and indices for all containers. See C++ Core Guidelines by Stroustrup/Sutter: [ES.100](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#es100-dont-mix-signed-and-unsigned-arithmetic), [ES.102](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#es102-use-signed-types-for-arithmetic), [ES.106](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#es106-dont-try-to-avoid-negative-values-by-using-unsigned), and [ES.107](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#es107-dont-use-unsigned-for-subscripts-prefer-gslindex). - Customizable allocator [per templated container type](https://github.com/tylov/STC/discussions/44#discussioncomment-4891925). diff --git a/docs/ccommon_api.md b/docs/ccommon_api.md index 21eaf884..90191e40 100644 --- a/docs/ccommon_api.md +++ b/docs/ccommon_api.md @@ -84,7 +84,7 @@ c_forrange (i, 30, 0, -5) printf(" %lld", i); ### crange 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_object(...) // create a compound literal crange object +crange& crange_obj(...) // create a compound literal crange object 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 @@ -102,7 +102,7 @@ c_forfilter (i, crange, r1, isPrime(*i.ref)) // 2. The first 11 primes: printf("2"); -c_forfilter (i, crange, crange_object(3, INT64_MAX, 2), +c_forfilter (i, crange, crange_obj(3, INT64_MAX, 2), isPrime(*i.ref) && c_flt_take(10) ){ @@ -260,22 +260,23 @@ void c_default_drop(Type* p); // does nothing --- ## Coroutines -This is an improved implementation of Simon Tatham's classic C code, which utilizes -the *Duff's device* trick. However, Tatham's implementation is not typesafe, -and it always allocates the coroutine's internal state dynamically. But most crucially, +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 some struct pointer as parameter, which must contain the member `int cco_state;` +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 main user-loop -skips the triples which are upscaled version of smaller ones by checking -the gcd() function, and breaks when the diagonal size >= 100: +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: ```c -#include +#include struct triples { int n; // input: max number of triples to be generated. @@ -311,8 +312,7 @@ int gcd(int a, int b) { // greatest common denominator int main() { - puts("\nCoroutine triples:"); - struct triples t = {INT32_MAX}; + struct triples t = {.n=INT32_MAX}; int n = 0; while (triples_next(&t)) { @@ -324,12 +324,13 @@ int main() if (t.c < 100) printf("%d: {%d, %d, %d}\n", ++n, t.a, t.b, t.c); else - cco_stop(&t); // make sure coroutine cleanup is done + cco_stop(&t); // cleanup in next coroutine call/resume } } ``` ### Coroutine API -**Note**: `cco_yield()` may not be called inside a `switch` statement. Use `if-else-if` constructs instead. +**Note**: *cco_yield()* may not be called inside a `switch` statement. Use `if-else-if` constructs instead. +To resume the coroutine from where it was suspended with *cco_yield()*, simply call the coroutine again. | | Function / operator | Description | |:----------|:-------------------------------------|:----------------------------------------| @@ -340,7 +341,7 @@ int main() | `void` | `cco_begin(ctx)` | Begin coroutine block | | `rettype` | `cco_end(retval)` | End coroutine block with return value | | `rettype` | `cco_end()` | End coroutine block with (void) | -| `rettype` | `cco_yield(retval)` | Yield a value | +| `rettype` | `cco_yield(retval)` | Return a value and suspend execution | | `rettype` | `cco_yield(corocall2, ctx2, retval)` | Yield from another coroutine and return val | | `rettype` | `cco_yield(corocall2, ctx2)` | Yield from another coroutine (void) | | | From the caller side: | | diff --git a/include/stc/algo/crange.h b/include/stc/algo/crange.h index 91ffdd56..ca06c258 100644 --- a/include/stc/algo/crange.h +++ b/include/stc/algo/crange.h @@ -34,7 +34,7 @@ int main() // use a temporary crange object. int a = 100, b = INT32_MAX; - c_forfilter (i, crange, crange_object(a, b, 8), + c_forfilter (i, crange, crange_obj(a, b, 8), c_flt_skip(i, 10) && c_flt_take(i, 3)) printf(" %lld", *i.ref); @@ -46,7 +46,7 @@ int main() #include -#define crange_object(...) \ +#define crange_obj(...) \ (*(crange[]){crange_make(__VA_ARGS__)}) typedef long long crange_value; diff --git a/include/stc/ccommon.h b/include/stc/ccommon.h index 24ad59f9..1a2b3c3f 100644 --- a/include/stc/ccommon.h +++ b/include/stc/ccommon.h @@ -122,8 +122,9 @@ #define c_make(C, ...) \ C##_from_n((C##_raw[])__VA_ARGS__, c_sizeof((C##_raw[])__VA_ARGS__)/c_sizeof(C##_raw)) -#define c_arraylen(a) (intptr_t)(sizeof(a)/sizeof 0[a]) #define c_litstrlen(literal) (c_sizeof("" literal) - 1) +#define c_arraylen(a) (c_ARRAYLEN(a) + c_static_assert(sizeof(a) != sizeof(uintptr_t))) +#define c_ARRAYLEN(a) (intptr_t)(sizeof(a)/sizeof 0[a]) // Non-owning c-string typedef const char* crawstr; diff --git a/include/stc/extend.h b/include/stc/extend.h index cbfc4a12..66b3ebd1 100644 --- a/include/stc/extend.h +++ b/include/stc/extend.h @@ -52,9 +52,9 @@ typedef struct { i_extend i_type get; -} c_PASTE(i_type, Ext); +} c_PASTE(i_type, _ext); -#define c_getcon(cptr) c_container_of(cptr, _cx_memb(Ext), get) +#define c_getcon(cptr) c_container_of(cptr, _cx_memb(_ext), get) #define i_is_forward #define _i_inc diff --git a/misc/examples/forfilter.c b/misc/examples/forfilter.c index daea68b9..fbb7280f 100644 --- a/misc/examples/forfilter.c +++ b/misc/examples/forfilter.c @@ -54,7 +54,7 @@ fn main() { void demo2(void) { IVec vector = {0}; - c_forfilter (x, crange, crange_object(INT64_MAX), + c_forfilter (x, crange, crange_obj(INT64_MAX), c_flt_skipwhile(x, *x.ref != 11) && (*x.ref % 2) != 0 && c_flt_take(x, 5) diff --git a/misc/examples/functor.c b/misc/examples/functor.c index 7417080b..c0a4f8e8 100644 --- a/misc/examples/functor.c +++ b/misc/examples/functor.c @@ -14,10 +14,10 @@ #define i_con cpque #include -void print_queue(const char* name, IPQueExt q) { +void print_queue(const char* name, IPQue_ext q) { // NB: make a clone because there is no way to traverse // priority_queue's content without erasing the queue. - IPQueExt copy = {q.less, IPQue_clone(q.get)}; + IPQue_ext copy = {q.less, IPQue_clone(q.get)}; for (printf("%s: \t", name); !IPQue_empty(©.get); IPQue_pop(©.get)) printf("%d ", *IPQue_top(©.get)); @@ -38,9 +38,9 @@ int main() printf("%d ", data[i]); puts(""); - IPQueExt q1 = {int_less}; // Max priority queue - IPQueExt minq1 = {int_greater}; // Min priority queue - IPQueExt q5 = {int_lambda}; // Using lambda to compare elements. + IPQue_ext q1 = {int_less}; // Max priority queue + IPQue_ext minq1 = {int_greater}; // Min priority queue + IPQue_ext q5 = {int_lambda}; // Using lambda to compare elements. c_forrange (i, n) IPQue_push(&q1.get, data[i]); diff --git a/misc/examples/prime.c b/misc/examples/prime.c index a576a85c..d0887353 100644 --- a/misc/examples/prime.c +++ b/misc/examples/prime.c @@ -42,7 +42,7 @@ int main(void) puts("\n"); puts("Show the last 50 primes using a temporary crange generator:"); - c_forfilter (i, crange, crange_object(n - 1, 0, -2), + c_forfilter (i, crange, crange_obj(n - 1, 0, -2), cbits_test(&primes, *i.ref/2) && c_flt_take(i, 50) ){ -- cgit v1.2.3