From 5f57d597cd27aef55adbcb3b452973b0c6e33667 Mon Sep 17 00:00:00 2001 From: Tyge Lovset Date: Tue, 20 Dec 2022 23:31:51 +0100 Subject: Restructured folders: examples, benchmarks, tests into misc folder. --- include/stc/alt/csmap.h | 512 -------------------------------------------- include/stc/alt/cstr.h | 385 --------------------------------- include/stc/carc.h | 4 +- include/stc/carr2.h | 4 +- include/stc/carr3.h | 4 +- include/stc/cbox.h | 4 +- include/stc/cdeq.h | 4 +- include/stc/clist.h | 4 +- include/stc/cmap.h | 4 +- include/stc/cpque.h | 4 +- include/stc/csmap.h | 4 +- include/stc/cstack.h | 4 +- include/stc/cvec.h | 6 +- include/stc/priv/template.h | 331 ++++++++++++++++++++++++++++ include/stc/stctest.h | 203 ------------------ include/stc/template.h | 331 ---------------------------- 16 files changed, 354 insertions(+), 1454 deletions(-) delete mode 100644 include/stc/alt/csmap.h delete mode 100644 include/stc/alt/cstr.h create mode 100644 include/stc/priv/template.h delete mode 100644 include/stc/stctest.h delete mode 100644 include/stc/template.h (limited to 'include') diff --git a/include/stc/alt/csmap.h b/include/stc/alt/csmap.h deleted file mode 100644 index 65a9f80b..00000000 --- a/include/stc/alt/csmap.h +++ /dev/null @@ -1,512 +0,0 @@ -/* MIT License - * - * Copyright (c) 2022 Tyge Løvset, NORCE, www.norceresearch.no - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -// Sorted/Ordered set and map - implemented as an AA-tree. -/* -#include -#include - -#define i_tag sx // Sorted map -#define i_key_str -#define i_val double -#include - -int main(void) { - c_with (csmap_sx m = csmap_sx_init(), csmap_sx_drop(&m)) - { - csmap_sx_emplace(&m, "Testing one", 1.234); - csmap_sx_emplace(&m, "Testing two", 12.34); - csmap_sx_emplace(&m, "Testing three", 123.4); - - csmap_sx_value *v = csmap_sx_get(&m, "Testing five"); // NULL - double num = *csmap_sx_at(&m, "Testing one"); - csmap_sx_emplace_or_assign(&m, "Testing three", 1000.0); // update - csmap_sx_erase(&m, "Testing two"); - - c_foreach (i, csmap_sx, m) - printf("map %s: %g\n", cstr_str(&i.ref->first), i.ref->second); - } -} -*/ -#include - -#ifndef CSMAP_H_INCLUDED -#define STC_CSMAP_V1 1 -#include -#include -#include -#endif // CSMAP_H_INCLUDED - -#ifndef _i_prefix -#define _i_prefix csmap_ -#endif -#ifdef _i_isset - #define _i_MAP_ONLY c_false - #define _i_SET_ONLY c_true - #define _i_keyref(vp) (vp) -#else - #define _i_ismap - #define _i_MAP_ONLY c_true - #define _i_SET_ONLY c_false - #define _i_keyref(vp) (&(vp)->first) -#endif -#include - -#if !c_option(c_is_forward) -_cx_deftypes(_c_aatree_types, _cx_self, i_key, i_val, i_size, _i_MAP_ONLY, _i_SET_ONLY); -#endif - -_i_MAP_ONLY( struct _cx_value { - _cx_key first; - _cx_mapped second; -}; ) -struct _cx_node { - struct _cx_node *link[2]; - uint8_t level; - _cx_value value; -}; - -typedef i_keyraw _cx_rawkey; -typedef i_valraw _cx_memb(_rawmapped); -typedef _i_SET_ONLY( i_keyraw ) - _i_MAP_ONLY( struct { i_keyraw first; i_valraw second; } ) - _cx_raw; - -#if !defined i_no_clone -STC_API _cx_self _cx_memb(_clone)(_cx_self cx); -#if !defined i_no_emplace -STC_API _cx_result _cx_memb(_emplace)(_cx_self* self, i_keyraw rkey _i_MAP_ONLY(, i_valraw rmapped)); -#endif // !i_no_emplace -#endif // !i_no_clone -STC_API _cx_self _cx_memb(_init)(void); -STC_API _cx_result _cx_memb(_insert)(_cx_self* self, i_key key _i_MAP_ONLY(, i_val mapped)); -STC_API _cx_result _cx_memb(_push)(_cx_self* self, _cx_value _val); -STC_API void _cx_memb(_drop)(_cx_self* self); -STC_API _cx_value* _cx_memb(_find_it)(const _cx_self* self, i_keyraw rkey, _cx_iter* out); -STC_API _cx_iter _cx_memb(_lower_bound)(const _cx_self* self, i_keyraw rkey); -STC_API _cx_value* _cx_memb(_front)(const _cx_self* self); -STC_API _cx_value* _cx_memb(_back)(const _cx_self* self); -STC_API int _cx_memb(_erase)(_cx_self* self, i_keyraw rkey); -STC_API _cx_iter _cx_memb(_erase_at)(_cx_self* self, _cx_iter it); -STC_API _cx_iter _cx_memb(_erase_range)(_cx_self* self, _cx_iter it1, _cx_iter it2); -STC_API void _cx_memb(_next)(_cx_iter* it); - -STC_INLINE bool _cx_memb(_empty)(_cx_self cx) { return cx.size == 0; } -STC_INLINE size_t _cx_memb(_size)(_cx_self cx) { return cx.size; } -STC_INLINE void _cx_memb(_swap)(_cx_self* a, _cx_self* b) { c_swap(_cx_self, *a, *b); } -STC_INLINE _cx_iter _cx_memb(_find)(const _cx_self* self, i_keyraw rkey) - { _cx_iter it; _cx_memb(_find_it)(self, rkey, &it); return it; } -STC_INLINE bool _cx_memb(_contains)(const _cx_self* self, i_keyraw rkey) - { _cx_iter it; return _cx_memb(_find_it)(self, rkey, &it) != NULL; } -STC_INLINE const _cx_value* _cx_memb(_get)(const _cx_self* self, i_keyraw rkey) - { _cx_iter it; return _cx_memb(_find_it)(self, rkey, &it); } -STC_INLINE _cx_value* _cx_memb(_get_mut)(_cx_self* self, i_keyraw rkey) - { _cx_iter it; return _cx_memb(_find_it)(self, rkey, &it); } - -STC_INLINE void -_cx_memb(_clear)(_cx_self* self) - { _cx_memb(_drop)(self); *self = _cx_memb(_init)(); } - -STC_INLINE _cx_raw -_cx_memb(_value_toraw)(_cx_value* val) { - return _i_SET_ONLY( i_keyto(val) ) - _i_MAP_ONLY( c_INIT(_cx_raw){i_keyto((&val->first)), - i_valto((&val->second))} ); -} - -STC_INLINE int -_cx_memb(_value_cmp)(const _cx_value* x, const _cx_value* y) { - _cx_rawkey rx = i_keyto(_i_keyref(x)), ry = i_keyto(_i_keyref(y)); - return i_cmp((&rx), (&ry)); -} - -STC_INLINE void -_cx_memb(_value_drop)(_cx_value* val) { - i_keydrop(_i_keyref(val)); - _i_MAP_ONLY( i_valdrop((&val->second)); ) -} - -#if !defined i_no_clone -STC_INLINE _cx_value -_cx_memb(_value_clone)(_cx_value _val) { - *_i_keyref(&_val) = i_keyclone((*_i_keyref(&_val))); - _i_MAP_ONLY( _val.second = i_valclone(_val.second); ) - return _val; -} - -STC_INLINE void -_cx_memb(_copy)(_cx_self *self, const _cx_self* other) { - if (self->root == other->root) - return; - _cx_memb(_drop)(self); - *self = _cx_memb(_clone)(*other); -} -#endif // !i_no_clone - -#ifndef _i_isset - #if !defined i_no_clone && !defined i_no_emplace - STC_API _cx_result _cx_memb(_emplace_or_assign)(_cx_self* self, i_keyraw rkey, i_valraw rmapped); - #endif - STC_API _cx_result _cx_memb(_insert_or_assign)(_cx_self* self, i_key key, i_val mapped); - - STC_INLINE const _cx_mapped* - _cx_memb(_at)(const _cx_self* self, i_keyraw rkey) - { _cx_iter it; return &_cx_memb(_find_it)(self, rkey, &it)->second; } - STC_INLINE _cx_mapped* - _cx_memb(_at_mut)(_cx_self* self, i_keyraw rkey) - { _cx_iter it; return &_cx_memb(_find_it)(self, rkey, &it)->second; } -#endif // !_i_isset - -STC_INLINE _cx_iter -_cx_memb(_begin)(const _cx_self* self) { - _cx_iter it; - it.ref = NULL, it._top = 0, it._tn = self->root; - _cx_memb(_next)(&it); - return it; -} - -STC_INLINE _cx_iter -_cx_memb(_end)(const _cx_self* self) { - (void)self; - _cx_iter it; it.ref = NULL, it._top = 0, it._tn = NULL; - return it; -} - -STC_INLINE _cx_iter -_cx_memb(_advance)(_cx_iter it, size_t n) { - while (n-- && it.ref) - _cx_memb(_next)(&it); - return it; -} - -/* -------------------------- IMPLEMENTATION ------------------------- */ -#if defined(i_implement) - -#ifndef CSMAP_H_INCLUDED -static struct { void *link[2]; uint8_t level; } -_csmap_sentinel = {{&_csmap_sentinel, &_csmap_sentinel}, 0}; -#endif - -static _cx_result _cx_memb(_insert_entry_)(_cx_self* self, i_keyraw rkey); - -STC_DEF _cx_self -_cx_memb(_init)(void) { - _cx_self cx = {(_cx_node *)&_csmap_sentinel, 0}; - return cx; -} - -STC_DEF _cx_value* -_cx_memb(_front)(const _cx_self* self) { - _cx_node *tn = self->root; - while (tn->link[0]->level) - tn = tn->link[0]; - return &tn->value; -} - -STC_DEF _cx_value* -_cx_memb(_back)(const _cx_self* self) { - _cx_node *tn = self->root; - while (tn->link[1]->level) - tn = tn->link[1]; - return &tn->value; -} - -STC_DEF _cx_result -_cx_memb(_insert)(_cx_self* self, i_key key _i_MAP_ONLY(, i_val mapped)) { - _cx_result res = _cx_memb(_insert_entry_)(self, i_keyto((&key))); - if (res.inserted) - { *_i_keyref(res.ref) = key; _i_MAP_ONLY( res.ref->second = mapped; )} - else - { i_keydrop((&key)); _i_MAP_ONLY( i_valdrop((&mapped)); )} - return res; -} - -STC_DEF _cx_result -_cx_memb(_push)(_cx_self* self, _cx_value _val) { - _cx_result _res = _cx_memb(_insert_entry_)(self, i_keyto(_i_keyref(&_val))); - if (_res.inserted) - *_res.ref = _val; - else - _cx_memb(_value_drop)(&_val); - return _res; -} - -#ifndef _i_isset - STC_DEF _cx_result - _cx_memb(_insert_or_assign)(_cx_self* self, i_key key, i_val mapped) { - _cx_result res = _cx_memb(_insert_entry_)(self, i_keyto((&key))); - if (res.inserted) - res.ref->first = key; - else - { i_keydrop((&key)); i_valdrop((&res.ref->second)); } - res.ref->second = mapped; - return res; - } - #if !defined i_no_clone && !defined i_no_emplace - STC_DEF _cx_result - _cx_memb(_emplace_or_assign)(_cx_self* self, i_keyraw rkey, i_valraw rmapped) { - _cx_result res = _cx_memb(_insert_entry_)(self, rkey); - if (res.inserted) - res.ref->first = i_keyfrom(rkey); - else - { i_valdrop((&res.ref->second)); } - res.ref->second = i_valfrom(rmapped); - return res; - } - #endif // !i_no_clone && !i_no_emplace -#endif // !_i_isset - -STC_DEF _cx_value* -_cx_memb(_find_it)(const _cx_self* self, _cx_rawkey rkey, _cx_iter* out) { - _cx_node *tn = self->root; - out->_top = 0; - while (tn->level) { - int c; _cx_rawkey raw = i_keyto(_i_keyref(&tn->value)); - if ((c = i_cmp((&raw), (&rkey))) < 0) - tn = tn->link[1]; - else if (c > 0) - { out->_st[out->_top++] = tn; tn = tn->link[0]; } - else - { out->_tn = tn->link[1]; return (out->ref = &tn->value); } - } - return (out->ref = NULL); -} - -STC_DEF _cx_iter -_cx_memb(_lower_bound)(const _cx_self* self, i_keyraw rkey) { - _cx_iter it; - _cx_memb(_find_it)(self, rkey, &it); - if (!it.ref && it._top) { - _cx_node *tn = it._st[--it._top]; - it._tn = tn->link[1]; - it.ref = &tn->value; - } - return it; -} - -STC_DEF void -_cx_memb(_next)(_cx_iter *it) { - _cx_node *tn = it->_tn; - if (it->_top || tn->level) { - while (tn->level) { - it->_st[it->_top++] = tn; - tn = tn->link[0]; - } - tn = it->_st[--it->_top]; - it->_tn = tn->link[1]; - it->ref = &tn->value; - } else - it->ref = NULL; -} - -static _cx_node * -_cx_memb(_skew_)(_cx_node *tn) { - if (tn && tn->link[0]->level == tn->level && tn->level) { - _cx_node *tmp = tn->link[0]; - tn->link[0] = tmp->link[1]; - tmp->link[1] = tn; - tn = tmp; - } - return tn; -} - -static _cx_node * -_cx_memb(_split_)(_cx_node *tn) { - if (tn->link[1]->link[1]->level == tn->level && tn->level) { - _cx_node *tmp = tn->link[1]; - tn->link[1] = tmp->link[0]; - tmp->link[0] = tn; - tn = tmp; - ++tn->level; - } - return tn; -} - -static _cx_node* -_cx_memb(_insert_entry_i_)(_cx_node* tn, const _cx_rawkey* rkey, _cx_result* res) { - _cx_node *up[64], *tx = tn; - int c, top = 0, dir = 0; - while (tx->level) { - up[top++] = tx; - _cx_rawkey r = i_keyto(_i_keyref(&tx->value)); - if (!(c = (i_cmp((&r), rkey)))) - { res->ref = &tx->value; return tn; } - dir = (c < 0); - tx = tx->link[dir]; - } - tn = c_alloc(_cx_node); - tn->link[0] = tn->link[1] = (_cx_node*)&_csmap_sentinel; - tn->level = 1; - res->ref = &tn->value, res->inserted = true; - if (top == 0) - return tn; - up[top - 1]->link[dir] = tn; - while (top--) { - if (top) - dir = (up[top - 1]->link[1] == up[top]); - up[top] = _cx_memb(_skew_)(up[top]); - up[top] = _cx_memb(_split_)(up[top]); - if (top) - up[top - 1]->link[dir] = up[top]; - } - return up[0]; -} - -STC_DEF _cx_result -_cx_memb(_insert_entry_)(_cx_self* self, i_keyraw rkey) { - _cx_result res = {NULL}; - self->root = _cx_memb(_insert_entry_i_)(self->root, &rkey, &res); - self->size += res.inserted; - return res; -} - -static _cx_node* -_cx_memb(_erase_r_)(_cx_node *tn, const _cx_rawkey* rkey, int *erased) { - if (tn->level == 0) - return tn; - _cx_rawkey raw = i_keyto(_i_keyref(&tn->value)); - _cx_node *tx; int c = (i_cmp((&raw), rkey)); - if (c != 0) - tn->link[c < 0] = _cx_memb(_erase_r_)(tn->link[c < 0], rkey, erased); - else { - if (!*erased) - { _cx_memb(_value_drop)(&tn->value); *erased = 1; } - if (tn->link[0]->level && tn->link[1]->level) { - tx = tn->link[0]; - while (tx->link[1]->level) - tx = tx->link[1]; - tn->value = tx->value; - raw = i_keyto(_i_keyref(&tn->value)); - tn->link[0] = _cx_memb(_erase_r_)(tn->link[0], &raw, erased); - } else { /* unlink node */ - tx = tn; - tn = tn->link[tn->link[0]->level == 0]; - c_free(tx); - } - } - if (tn->link[0]->level < tn->level - 1 || tn->link[1]->level < tn->level - 1) { - if (tn->link[1]->level > --tn->level) - tn->link[1]->level = tn->level; - tn = _cx_memb(_skew_)(tn); - tx = tn->link[0] = _cx_memb(_skew_)(tn->link[0]); - tx->link[0] = _cx_memb(_skew_)(tx->link[0]); - tn = _cx_memb(_split_)(tn); - tn->link[0] = _cx_memb(_split_)(tn->link[0]); - } - return tn; -} - -STC_DEF int -_cx_memb(_erase)(_cx_self* self, i_keyraw rkey) { - int erased = 0; - self->root = _cx_memb(_erase_r_)(self->root, &rkey, &erased); - self->size -= erased; - return erased; -} - -STC_DEF _cx_iter -_cx_memb(_erase_at)(_cx_self* self, _cx_iter it) { - _cx_rawkey raw = i_keyto(_i_keyref(it.ref)), nxt; - _cx_memb(_next)(&it); - if (it.ref) - nxt = i_keyto(_i_keyref(it.ref)); - _cx_memb(_erase)(self, raw); - if (it.ref) - _cx_memb(_find_it)(self, nxt, &it); - return it; -} - -STC_DEF _cx_iter -_cx_memb(_erase_range)(_cx_self* self, _cx_iter it1, _cx_iter it2) { - if (!it2.ref) { - while (it1.ref) - it1 = _cx_memb(_erase_at)(self, it1); - return it1; - } - _cx_key k1 = *_i_keyref(it1.ref), k2 = *_i_keyref(it2.ref); - _cx_rawkey r1 = i_keyto((&k1)); - for (;;) { - if (memcmp(&k1, &k2, sizeof k1) == 0) - return it1; - _cx_memb(_next)(&it1); - k1 = *_i_keyref(it1.ref); - _cx_memb(_erase)(self, r1); - r1 = i_keyto((&k1)); - _cx_memb(_find_it)(self, r1, &it1); - } -} - -#if !defined i_no_clone -static _cx_node* -_cx_memb(_clone_r_)(_cx_node *tn) { - if (! tn->level) - return tn; - _cx_node *cn = c_alloc(_cx_node); - cn->level = tn->level; - cn->value = _cx_memb(_value_clone)(tn->value); - cn->link[0] = _cx_memb(_clone_r_)(tn->link[0]); - cn->link[1] = _cx_memb(_clone_r_)(tn->link[1]); - return cn; -} - -STC_DEF _cx_self -_cx_memb(_clone)(_cx_self cx) { - return c_INIT(_cx_self){_cx_memb(_clone_r_)(cx.root), cx.size}; -} -#endif // !i_no_clone - -#if !defined i_no_emplace -STC_DEF _cx_result -_cx_memb(_emplace)(_cx_self* self, i_keyraw rkey _i_MAP_ONLY(, i_valraw rmapped)) { - _cx_result res = _cx_memb(_insert_entry_)(self, rkey); - if (res.inserted) { - *_i_keyref(res.ref) = i_keyfrom(rkey); - _i_MAP_ONLY(res.ref->second = i_valfrom(rmapped);) - } - return res; -} -#endif // i_no_emplace - -static void -_cx_memb(_drop_r_)(_cx_node* tn) { - if (tn->level != 0) { - _cx_memb(_drop_r_)(tn->link[0]); - _cx_memb(_drop_r_)(tn->link[1]); - _cx_memb(_value_drop)(&tn->value); - c_free(tn); - } -} - -STC_DEF void -_cx_memb(_drop)(_cx_self* self) { - _cx_memb(_drop_r_)(self->root); -} - -#endif // i_implement -#undef _i_isset -#undef _i_ismap -#undef _i_keyref -#undef _i_MAP_ONLY -#undef _i_SET_ONLY -#define CSMAP_H_INCLUDED -#include diff --git a/include/stc/alt/cstr.h b/include/stc/alt/cstr.h deleted file mode 100644 index ecb9c9fb..00000000 --- a/include/stc/alt/cstr.h +++ /dev/null @@ -1,385 +0,0 @@ -/* MIT License - * - * Copyright (c) 2022 Tyge Løvset, NORCE, www.norceresearch.no - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef CSTR_H_INCLUDED -#define CSTR_H_INCLUDED - -#include -#include -#include /* malloc */ -#include -#include -#include /* vsnprintf */ -#include - -#define c_unchecked_container_of(ptr, type, member) \ - ((type*)((char*)(ptr) - offsetof(type, member))) - -typedef char cstr_value; -typedef struct { cstr_value* str; } cstr; -typedef struct { size_t size, cap; char chr[1]; } cstr_priv; -#define _cstr_p(self) c_unchecked_container_of((self)->str, cstr_priv, chr) -#ifdef i_static - static cstr_priv _cstr_nullrep = {0, 0, {0}}; - static const cstr cstr_NULL = {_cstr_nullrep.chr}; -#else - extern const cstr cstr_NULL; -#endif -/* optimal memory: based on malloc_usable_size() sequence: 24, 40, 56, ... */ -#define _cstr_opt_mem(cap) ((((offsetof(cstr_priv, chr) + (cap) + 8)>>4)<<4) + 8) -/* optimal string capacity: 7, 23, 39, ... */ -#define _cstr_opt_cap(cap) (_cstr_opt_mem(cap) - offsetof(cstr_priv, chr) - 1) - -STC_API cstr cstr_from_n(const char* str, size_t n); -STC_API cstr cstr_from_fmt(const char* fmt, ...); -STC_API char* cstr_reserve(cstr* self, size_t cap); -STC_API void cstr_resize(cstr* self, size_t len, char fill); -STC_API cstr* cstr_assign_n(cstr* self, const char* str, size_t n); -STC_API int cstr_printf(cstr* self, const char* fmt, ...); -STC_API cstr* cstr_append_n(cstr* self, const char* str, size_t n); -STC_API cstr cstr_replace_sv(csview str, csview find, csview repl, unsigned count); -STC_DEF void cstr_replace_at_sv(cstr* self, const size_t pos, size_t len, csview repl); -STC_API void cstr_erase(cstr* self, size_t pos, size_t n); -STC_API size_t cstr_find(const cstr* self, const char* needle); -STC_API size_t cstr_find_at(const cstr* self, size_t pos, const char* needle); -STC_API bool cstr_getdelim(cstr *self, int delim, FILE *stream); - -STC_INLINE cstr cstr_init() { return cstr_NULL; } -STC_INLINE const char* cstr_str(const cstr* self) { return self->str; } -#define cstr_toraw(self) (self)->str -STC_INLINE csview cstr_sv(const cstr* self) - { return c_INIT(csview){self->str, _cstr_p(self)->size}; } -#define cstr_lit(literal) \ - cstr_from_n(literal, c_strlen_lit(literal)) -STC_INLINE cstr cstr_from(const char* str) - { return cstr_from_n(str, strlen(str)); } -STC_INLINE char* cstr_data(cstr* self) { return self->str; } -STC_INLINE size_t cstr_size(const cstr* self) { return _cstr_p(self)->size; } -STC_INLINE size_t cstr_capacity(cstr s) { return _cstr_p(&s)->cap; } -STC_INLINE bool cstr_empty(cstr s) { return _cstr_p(&s)->size == 0; } -STC_INLINE void cstr_drop(cstr* self) - { if (_cstr_p(self)->cap) c_free(_cstr_p(self)); } -STC_INLINE cstr cstr_clone(cstr s) - { return cstr_from_n(s.str, _cstr_p(&s)->size); } -STC_INLINE void cstr_clear(cstr* self) - { self->str[_cstr_p(self)->size = 0] = '\0'; } -STC_INLINE cstr* cstr_assign(cstr* self, const char* str) - { return cstr_assign_n(self, str, strlen(str)); } -STC_INLINE cstr* cstr_copy(cstr* self, cstr s) - { return cstr_assign_n(self, s.str, _cstr_p(&s)->size); } -STC_INLINE cstr* cstr_append(cstr* self, const char* str) - { return cstr_append_n(self, str, strlen(str)); } -STC_INLINE cstr* cstr_append_s(cstr* self, cstr s) - { return cstr_append_n(self, s.str, _cstr_p(&s)->size); } -STC_INLINE void cstr_push_back(cstr* self, char value) - { cstr_append_n(self, &value, 1); } -STC_INLINE void cstr_pop_back(cstr* self) - { self->str[ --_cstr_p(self)->size ] = '\0'; } -STC_INLINE void cstr_insert_n(cstr* self, const size_t pos, const char* str, const size_t n) - { cstr_replace_at_sv(self, pos, 0, c_SV(str, n)); } -STC_INLINE void cstr_insert(cstr* self, const size_t pos, const char* str) - { cstr_replace_at_sv(self, pos, 0, c_SV(str, strlen(str))); } -STC_INLINE void cstr_insert_s(cstr* self, const size_t pos, cstr s) - { cstr_replace_at_sv(self, pos, 0, c_SV(s.str, _cstr_p(&s)->size)); } -STC_INLINE void cstr_replace_at(cstr* self, const size_t pos, const size_t len, const char* str) - { cstr_replace_at_sv(self, pos, len, c_SV(str, strlen(str))); } -STC_INLINE void cstr_replace_s(cstr* self, const size_t pos, const size_t len, cstr s) - { cstr_replace_at_sv(self, pos, len, c_SV(s.str, _cstr_p(&s)->size)); } -STC_INLINE char* cstr_front(cstr* self) { return self->str; } -STC_INLINE char* cstr_back(cstr* self) - { return self->str + _cstr_p(self)->size - 1; } -STC_INLINE bool cstr_equals(const cstr* self, const char* str) - { return strcmp(self->str, str) == 0; } -STC_INLINE bool cstr_equals_s(const cstr* self, cstr s) - { return strcmp(self->str, s.str) == 0; } -STC_INLINE bool cstr_contains(const cstr* self, const char* needle) - { return strstr(self->str, needle) != NULL; } -STC_INLINE bool cstr_getline(cstr *self, FILE *stream) - { return cstr_getdelim(self, '\n', stream); } - -STC_INLINE cstr_buf cstr_buffer(cstr* s) { - cstr_priv* p = _cstr_p(s); - return c_INIT(cstr_buf){s->str, p->size, p->cap}; -} - -STC_INLINE cstr cstr_with_capacity(const size_t cap) { - cstr s = cstr_NULL; - cstr_reserve(&s, cap); - return s; -} - -STC_INLINE cstr cstr_with_size(const size_t len, const char fill) { - cstr s = cstr_NULL; - cstr_resize(&s, len, fill); - return s; -} - -STC_INLINE char* cstr_append_uninit(cstr *self, size_t n) { - size_t len = cstr_size(self); char* d; - if (!(d = cstr_reserve(self, len + n))) return NULL; - _cstr_p(self)->size += n; - return d + len; -} - -STC_INLINE cstr* cstr_take(cstr* self, cstr s) { - if (self->str != s.str && _cstr_p(self)->cap) - c_free(_cstr_p(self)); - self->str = s.str; - return self; -} - -STC_INLINE cstr cstr_move(cstr* self) { - cstr tmp = *self; - *self = cstr_NULL; - return tmp; -} - -STC_INLINE bool cstr_starts_with(const cstr* self, const char* sub) { - const char* p = self->str; - while (*sub && *p == *sub) ++p, ++sub; - return *sub == 0; -} - -STC_INLINE bool cstr_ends_with(const cstr* self, const char* sub) { - const size_t n = strlen(sub), sz = _cstr_p(self)->size; - return n <= sz && !memcmp(self->str + sz - n, sub, n); -} - -STC_INLINE int c_strncasecmp(const char* s1, const char* s2, size_t nmax) { - int ret = 0; - while (nmax-- && (ret = tolower(*s1++) - tolower(*s2)) == 0 && *s2++) - ; - return ret; -} - -/* container adaptor functions: */ -#define cstr_cmp(xp, yp) strcmp((xp)->str, (yp)->str) - -STC_INLINE bool cstr_eq(const cstr* x, const cstr* y) { - size_t xs = _cstr_p(x)->size, ys = _cstr_p(y)->size; - return xs == ys && !memcmp(x->str, y->str, xs); -} -STC_INLINE uint64_t cstr_hash(const cstr *self) { - return cfasthash(self->str, _cstr_p(self)->size); -} - -STC_INLINE void -cstr_replace(cstr* self, const char* find, const char* repl, unsigned count) { - csview in = cstr_sv(self); - cstr_take(self, cstr_replace_sv(in, c_SV(find, strlen(find)), - c_SV(repl, strlen(repl)), count)); -} - -/* -------------------------- IMPLEMENTATION ------------------------- */ -#if defined(i_implement) - -#ifndef i_static -static cstr_priv _cstr_nullrep = {0, 0, {0}}; -const cstr cstr_NULL = {_cstr_nullrep.chr}; -#endif - -STC_DEF char* -cstr_reserve(cstr* self, const size_t cap) { - cstr_priv *p = _cstr_p(self); - const size_t oldcap = p->cap; - if (cap > oldcap) { - p = (cstr_priv*) c_realloc(((oldcap != 0) & (p != &_cstr_nullrep)) ? p : NULL, _cstr_opt_mem(cap)); - if (!p) return NULL; - self->str = p->chr; - if (oldcap == 0) self->str[p->size = 0] = '\0'; - p->cap = _cstr_opt_cap(cap); - } - return self->str; -} - -STC_DEF void -cstr_resize(cstr* self, const size_t len, const char fill) { - const size_t n = _cstr_p(self)->size; - cstr_reserve(self, len); - if (len > n) memset(self->str + n, fill, len - n); - if (len | n) self->str[_cstr_p(self)->size = len] = '\0'; -} - -STC_DEF cstr -cstr_from_n(const char* str, const size_t n) { - if (n == 0) return cstr_NULL; - cstr_priv* prv = (cstr_priv*) c_malloc(_cstr_opt_mem(n)); - cstr s = {(char *) memcpy(prv->chr, str, n)}; - s.str[prv->size = n] = '\0'; - prv->cap = _cstr_opt_cap(n); - return s; -} - -#if defined(__clang__) -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wdeprecated-declarations" -#elif defined(_MSC_VER) -# pragma warning(push) -# pragma warning(disable: 4996) -#endif - -STC_DEF int -cstr_vfmt(cstr* self, const char* fmt, va_list args) { - va_list args2; - va_copy(args2, args); - int len = vsnprintf(NULL, (size_t)0, fmt, args); - cstr_reserve(self, len); - vsprintf(self->str, fmt, args2); - va_end(args2); - return _cstr_p(self)->size = len; -} - -#if defined(__clang__) -# pragma clang diagnostic pop -#elif defined(_MSC_VER) -# pragma warning(pop) -#endif - -STC_DEF cstr -cstr_from_fmt(const char* fmt, ...) { - cstr ret = cstr_NULL; - va_list args; va_start(args, fmt); - cstr_vfmt(&ret, fmt, args); - va_end(args); - return ret; -} - -STC_DEF int -cstr_printf(cstr* self, const char* fmt, ...) { - cstr ret = cstr_NULL; - va_list args; - va_start(args, fmt); - int n = cstr_vfmt(&ret, fmt, args); - va_end(args); - cstr_drop(self); - *self = ret; - return n; -} - -STC_DEF cstr* -cstr_assign_n(cstr* self, const char* str, const size_t n) { - if (n || _cstr_p(self)->cap) { - cstr_reserve(self, n); - memmove(self->str, str, n); - self->str[_cstr_p(self)->size = n] = '\0'; - } - return self; -} - -STC_DEF cstr* -cstr_append_n(cstr* self, const char* str, const size_t n) { - if (n == 0) return self; - const size_t oldlen = _cstr_p(self)->size, newlen = oldlen + n; - if (newlen > _cstr_p(self)->cap) { - const size_t off = (size_t) (str - self->str); /* handle self append */ - cstr_reserve(self, (oldlen*3 >> 1) + n); - if (off <= oldlen) str = self->str + off; - } - memcpy(&self->str[oldlen], str, n); - self->str[_cstr_p(self)->size = newlen] = '\0'; - return self; -} - -STC_INLINE void _cstr_internal_move(cstr* self, const size_t pos1, const size_t pos2) { - if (pos1 == pos2) - return; - const size_t len = _cstr_p(self)->size, newlen = len + pos2 - pos1; - if (newlen > _cstr_p(self)->cap) - cstr_reserve(self, (len*3 >> 1) + pos2 - pos1); - memmove(&self->str[pos2], &self->str[pos1], len - pos1); - self->str[_cstr_p(self)->size = newlen] = '\0'; -} - -STC_DEF void -cstr_replace_at_sv(cstr* self, const size_t pos, size_t len, csview repl) { - const size_t sz = cstr_size(self); - if (len > sz - pos) len = sz - pos; - char buf[256], *xstr = repl.size > 256 ? c_malloc(repl.size) : buf; - memcpy(xstr, repl.str, repl.size); - _cstr_internal_move(self, pos + len, pos + repl.size); - memcpy(&self->str[pos], xstr, repl.size); - if (repl.size > 256) c_free(xstr); -} - -STC_DEF cstr -cstr_replace_sv(csview str, csview find, csview repl, unsigned count) { - cstr out = cstr_NULL; - size_t from = 0; char* res; - if (count == 0) count = ~0; - if (find.size) - while (count-- && (res = cstrnstrn(str.str + from, find.str, str.size - from, find.size))) { - const size_t pos = res - str.str; - cstr_append_n(&out, str.str + from, pos - from); - cstr_append_n(&out, repl.str, repl.size); - from = pos + find.size; - } - cstr_append_n(&out, str.str + from, str.size - from); - return out; -} - -STC_DEF void -cstr_erase(cstr* self, const size_t pos, size_t n) { - const size_t len = _cstr_p(self)->size; - if (n > len - pos) n = len - pos; - if (len) { - memmove(&self->str[pos], &self->str[pos + n], len - (pos + n)); - self->str[_cstr_p(self)->size -= n] = '\0'; - } -} - -STC_DEF bool -cstr_getdelim(cstr *self, const int delim, FILE *fp) { - size_t pos = 0, cap = _cstr_p(self)->cap; - char* d = self->str; - int c = fgetc(fp); - if (c == EOF) - return false; - for (;;) { - if (c == delim || c == EOF) { - if (cap) d[_cstr_p(self)->size = pos] = '\0'; - return true; - } - if (pos == cap) { - d = cstr_reserve(self, (cap*3 >> 1) + 16); - cap = cstr_capacity(*self); - } - d[pos++] = (char) c; - c = fgetc(fp); - } -} - -STC_DEF size_t -cstr_find(const cstr* self, const char* needle) { - char* res = strstr(self->str, needle); - return res ? res - self->str : c_NPOS; -} - -STC_DEF size_t -cstr_find_at(const cstr* self, const size_t pos, const char* needle) { - if (pos > _cstr_p(self)->size) return c_NPOS; - char* res = strstr(self->str + pos, needle); - return res ? res - self->str : c_NPOS; -} - -#endif -#endif // CSTR_H_INCLUDED -#undef i_opt diff --git a/include/stc/carc.h b/include/stc/carc.h index 509bc595..22453f1d 100644 --- a/include/stc/carc.h +++ b/include/stc/carc.h @@ -78,7 +78,7 @@ int main() { #ifndef _i_prefix #define _i_prefix carc_ #endif -#include "template.h" +#include "priv/template.h" typedef i_keyraw _cx_raw; #if !c_option(c_no_atomic) @@ -201,4 +201,4 @@ STC_INLINE uint64_t _cx_memb(_hash)(const _cx_self* x) #undef _i_atomic_inc #undef _i_atomic_dec_and_test -#include "template.h" +#include "priv/template.h" diff --git a/include/stc/carr2.h b/include/stc/carr2.h index 4a12c2e3..89ae9b77 100644 --- a/include/stc/carr2.h +++ b/include/stc/carr2.h @@ -56,7 +56,7 @@ int main() { #ifndef _i_prefix #define _i_prefix carr2_ #endif -#include "template.h" +#include "priv/template.h" #if !c_option(c_is_forward) _cx_deftypes(_c_carr2_types, _cx_self, i_key); #endif @@ -149,4 +149,4 @@ STC_DEF void _cx_memb(_drop)(_cx_self* self) { } #endif -#include "template.h" +#include "priv/template.h" diff --git a/include/stc/carr3.h b/include/stc/carr3.h index c22536cb..d167dd69 100644 --- a/include/stc/carr3.h +++ b/include/stc/carr3.h @@ -57,7 +57,7 @@ int main() { #ifndef _i_prefix #define _i_prefix carr3_ #endif -#include "template.h" +#include "priv/template.h" #if !c_option(c_is_forward) _cx_deftypes(_c_carr3_types, _cx_self, i_key); @@ -154,4 +154,4 @@ STC_DEF void _cx_memb(_drop)(_cx_self* self) { } #endif -#include "template.h" +#include "priv/template.h" diff --git a/include/stc/cbox.h b/include/stc/cbox.h index 4cbd5f84..bcb1b275 100644 --- a/include/stc/cbox.h +++ b/include/stc/cbox.h @@ -70,7 +70,7 @@ int main() { #ifndef _i_prefix #define _i_prefix cbox_ #endif -#include "template.h" +#include "priv/template.h" typedef i_keyraw _cx_raw; #if !c_option(c_is_forward) @@ -174,4 +174,4 @@ STC_INLINE uint64_t _cx_memb(_hash)(const _cx_self* x) { _cx_raw rx = i_keyto(x->get); return i_hash(&rx); } #endif -#include "template.h" +#include "priv/template.h" diff --git a/include/stc/cdeq.h b/include/stc/cdeq.h index a8a704b4..9416d5b2 100644 --- a/include/stc/cdeq.h +++ b/include/stc/cdeq.h @@ -34,7 +34,7 @@ #ifndef _i_prefix #define _i_prefix cdeq_ #endif -#include "template.h" +#include "priv/template.h" #if !c_option(c_is_forward) _cx_deftypes(_c_cdeq_types, _cx_self, i_key); @@ -425,5 +425,5 @@ _cx_memb(_value_cmp)(const _cx_value* x, const _cx_value* y) { #endif // !c_no_cmp #endif // !_i_queue #endif // IMPLEMENTATION -#include "template.h" +#include "priv/template.h" #define CDEQ_H_INCLUDED diff --git a/include/stc/clist.h b/include/stc/clist.h index 8b96eabf..1ace1dac 100644 --- a/include/stc/clist.h +++ b/include/stc/clist.h @@ -83,7 +83,7 @@ _c_clist_complete_types(clist_VOID, dummy); #ifndef _i_prefix #define _i_prefix clist_ #endif -#include "template.h" +#include "priv/template.h" #if !c_option(c_is_forward) _cx_deftypes(_c_clist_types, _cx_self, i_key); @@ -442,4 +442,4 @@ _cx_memb(_value_cmp)(const _cx_value* x, const _cx_value* y) { #endif // !c_no_cmp #endif // i_implement #define CLIST_H_INCLUDED -#include "template.h" +#include "priv/template.h" diff --git a/include/stc/cmap.h b/include/stc/cmap.h index b0793ce5..11edf127 100644 --- a/include/stc/cmap.h +++ b/include/stc/cmap.h @@ -73,7 +73,7 @@ typedef struct { size_t idx; uint8_t hx; } chash_bucket_t; #ifndef i_max_load_factor #define i_max_load_factor 0.85f #endif -#include "template.h" +#include "priv/template.h" #ifndef i_hash_functor #define i_hash_functor(self, x) i_hash(x) #endif @@ -450,4 +450,4 @@ _cx_memb(_erase_entry)(_cx_self* self, _cx_value* _val) { #undef _i_MAP_ONLY #undef _i_SET_ONLY #define CMAP_H_INCLUDED -#include "template.h" +#include "priv/template.h" diff --git a/include/stc/cpque.h b/include/stc/cpque.h index 9cf4b167..c1e7c13b 100644 --- a/include/stc/cpque.h +++ b/include/stc/cpque.h @@ -31,7 +31,7 @@ #define _i_prefix cpque_ #endif -#include "template.h" +#include "priv/template.h" #ifndef i_less_functor #define i_less_functor(self, x, y) i_less(x, y) #endif @@ -158,4 +158,4 @@ _cx_memb(_push)(_cx_self* self, _cx_value value) { #endif #define CPQUE_H_INCLUDED #undef i_less_functor -#include "template.h" +#include "priv/template.h" diff --git a/include/stc/csmap.h b/include/stc/csmap.h index 5c73294e..f651db00 100644 --- a/include/stc/csmap.h +++ b/include/stc/csmap.h @@ -73,7 +73,7 @@ int main(void) { #define _i_SET_ONLY c_false #define _i_keyref(vp) (&(vp)->first) #endif -#include "template.h" +#include "priv/template.h" #ifndef i_cmp_functor #define i_cmp_functor(self, x, y) i_cmp(x, y) #endif @@ -575,5 +575,5 @@ _cx_memb(_drop)(_cx_self* self) { #undef _i_MAP_ONLY #undef _i_SET_ONLY #define CSMAP_H_INCLUDED -#include "template.h" +#include "priv/template.h" #endif // !STC_CSMAP_V1 diff --git a/include/stc/cstack.h b/include/stc/cstack.h index 23778e3c..73d0e2e1 100644 --- a/include/stc/cstack.h +++ b/include/stc/cstack.h @@ -31,7 +31,7 @@ #ifndef _i_prefix #define _i_prefix cstack_ #endif -#include "template.h" +#include "priv/template.h" #if !c_option(c_is_forward) #ifdef i_capacity @@ -183,4 +183,4 @@ STC_INLINE void _cx_memb(_next)(_cx_iter* it) STC_INLINE _cx_iter _cx_memb(_advance)(_cx_iter it, intptr_t n) { if ((it.ref += n) >= it.end) it.ref = NULL ; return it; } -#include "template.h" +#include "priv/template.h" diff --git a/include/stc/cvec.h b/include/stc/cvec.h index ae6e452c..5eaefa04 100644 --- a/include/stc/cvec.h +++ b/include/stc/cvec.h @@ -71,7 +71,7 @@ int main() { #ifndef _i_prefix #define _i_prefix cvec_ #endif -#include "template.h" +#include "priv/template.h" #if !c_option(c_is_forward) _cx_deftypes(_c_cvec_types, _cx_self, i_key); @@ -256,7 +256,7 @@ _cx_memb(_sort)(_cx_self* self) { STC_DEF _cx_self _cx_memb(_init)(void) { - return (_cx_self){NULL}; + return c_INIT(_cx_self){NULL}; } STC_DEF void @@ -420,4 +420,4 @@ _cx_memb(_value_cmp)(const _cx_value* x, const _cx_value* y) { #endif // !c_no_cmp #endif // i_implement #define CVEC_H_INCLUDED -#include "template.h" +#include "priv/template.h" diff --git a/include/stc/priv/template.h b/include/stc/priv/template.h new file mode 100644 index 00000000..b7583e82 --- /dev/null +++ b/include/stc/priv/template.h @@ -0,0 +1,331 @@ +/* MIT License + * + * Copyright (c) 2022 Tyge Løvset, NORCE, www.norceresearch.no + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef _i_template +#define _i_template + +#ifndef STC_TEMPLATE_H_INCLUDED +#define STC_TEMPLATE_H_INCLUDED + #define _cx_self c_PASTE(_i_prefix, i_tag) + #define _cx_memb(name) c_PASTE(_cx_self, name) + #define _cx_deftypes(macro, SELF, ...) c_EXPAND(macro(SELF, __VA_ARGS__)) + #define _cx_value _cx_memb(_value) + #define _cx_key _cx_memb(_key) + #define _cx_mapped _cx_memb(_mapped) + #define _cx_raw _cx_memb(_raw) + #define _cx_rawkey _cx_memb(_rawkey) + #define _cx_iter _cx_memb(_iter) + #define _cx_result _cx_memb(_result) + #define _cx_node _cx_memb(_node) +#endif + +#ifdef i_type + #define i_tag i_type + #undef _i_prefix + #define _i_prefix +#endif + +#ifdef i_size + #define _i_expandby 2 +#else + #define i_size uint32_t + #define _i_expandby 1 +#endif + +#if !(defined i_key || defined i_key_str || defined i_key_ssv || \ + defined i_keyclass || defined i_keyboxed) + #if defined _i_ismap + #error "i_key* must be defined for maps." + #endif + + #if defined i_val_str + #define i_key_str i_val_str + #endif + #if defined i_val_ssv + #define i_key_ssv i_val_ssv + #endif + #if defined i_valboxed + #define i_keyboxed i_valboxed + #endif + #if defined i_valclass + #define i_keyclass i_valclass + #endif + #if defined i_val + #define i_key i_val + #endif + #if defined i_valraw + #define i_keyraw i_valraw + #endif + #if defined i_valclone + #define i_keyclone i_valclone + #endif + #if defined i_valfrom + #define i_keyfrom i_valfrom + #endif + #if defined i_valto + #define i_keyto i_valto + #endif + #if defined i_valdrop + #define i_keydrop i_valdrop + #endif +#endif + +#if c_option(c_no_cmp) + #define i_no_cmp +#endif +#if c_option(c_no_eq) + #define i_no_eq +#endif +#if c_option(c_no_hash) + #define i_no_hash +#endif +#if c_option(c_no_emplace) + #define i_no_emplace +#endif + +#if defined i_key_str + #define i_keyclass cstr + #define i_rawclass crawstr + #define i_keyfrom cstr_from + #ifndef i_tag + #define i_tag str + #endif +#elif defined i_key_ssv + #define i_keyclass cstr + #define i_rawclass csview + #define i_keyfrom cstr_from_sv + #define i_keyto cstr_sv + #define i_eq csview_eq + #ifndef i_tag + #define i_tag ssv + #endif +#elif defined i_keyboxed + #define i_keyclass i_keyboxed + #define i_rawclass c_PASTE(i_keyboxed, _raw) + #define i_keyfrom c_PASTE(i_keyboxed, _from) + #ifndef i_no_eq + #define i_eq c_PASTE(i_keyboxed, _raw_eq) + #endif +#endif + +#if defined i_rawclass + #define i_keyraw i_rawclass +#elif defined i_keyclass && !defined i_keyraw + #define i_rawclass i_key +#endif + +#ifdef i_keyclass + #define i_key i_keyclass + #ifndef i_keyclone + #define i_keyclone c_PASTE(i_key, _clone) + #endif + #if !defined i_keyto && defined i_keyraw + #define i_keyto c_PASTE(i_key, _toraw) + #endif + #ifndef i_keydrop + #define i_keydrop c_PASTE(i_key, _drop) + #endif +#endif + +#ifdef i_rawclass + #if !defined i_cmp && !defined i_no_cmp + #define i_cmp c_PASTE(i_keyraw, _cmp) + #endif + #if !defined i_hash && !defined i_no_hash + #define i_hash c_PASTE(i_keyraw, _hash) + #endif +#endif + +#if !defined i_key + #error "no i_key or i_val defined" +#elif defined i_keyraw ^ defined i_keyto + #error "both i_***raw and i_***to must be defined, if any" +#elif defined i_keyfrom && !defined i_keyraw + #error "i_***from defined without i_***raw" +#elif defined i_from || defined i_drop + #error "i_from / i_drop not supported. Define i_keyfrom/i_valfrom and/or i_keydrop/i_valdrop instead" +#endif + +#ifndef i_tag + #define i_tag i_key +#endif +#if c_option(c_no_clone) || (!defined i_keyclone && (defined i_keydrop || defined i_keyraw)) + #define i_no_clone +#endif +#ifndef i_keyraw + #define i_keyraw i_key +#endif +#ifndef i_keyfrom + #define i_keyfrom c_default_clone +#else + #define _i_has_from +#endif +#ifndef i_keyto + #define i_keyto c_default_toraw +#endif +#ifndef i_keyclone + #define i_keyclone c_default_clone +#endif +#ifndef i_keydrop + #define i_keydrop c_default_drop +#endif + +// i_eq, i_less, i_cmp +#if !defined i_eq && (defined i_cmp || defined i_less) + #define i_eq(x, y) !(i_cmp(x, y)) +#elif !defined i_eq + #define i_eq c_default_eq +#endif +#if defined i_less && defined i_cmp + #error "Only one of i_less and i_cmp may be defined" +#elif !defined i_less && !defined i_cmp + #define i_less c_default_less +#elif !defined i_less + #define i_less(x, y) (i_cmp(x, y)) < 0 +#endif +#ifndef i_cmp + #define i_cmp(x, y) (i_less(y, x)) - (i_less(x, y)) +#endif + +#ifndef i_hash + #define i_hash c_default_hash +#endif + +#if defined _i_ismap // ---- process cmap/csmap value i_val, ... ---- + +#ifdef i_val_str + #define i_valclass cstr + #define i_valraw crawstr + #define i_valfrom cstr_from +#elif defined i_val_ssv + #define i_valclass cstr + #define i_valraw csview + #define i_valfrom cstr_from_sv + #define i_valto cstr_sv +#elif defined i_valboxed + #define i_valclass i_valboxed + #define i_valraw c_PASTE(i_valboxed, _raw) + #define i_valfrom c_PASTE(i_valboxed, _from) +#endif + +#ifdef i_valclass + #define i_val i_valclass + #ifndef i_valclone + #define i_valclone c_PASTE(i_val, _clone) + #endif + #if !defined i_valto && defined i_valraw + #define i_valto c_PASTE(i_val, _toraw) + #endif + #ifndef i_valdrop + #define i_valdrop c_PASTE(i_val, _drop) + #endif +#endif + +#ifndef i_val + #error "i_val* must be defined for maps" +#endif + +#if !defined i_valclone && (defined i_valdrop || defined i_valraw) + #define i_no_clone +#endif +#ifndef i_valraw + #define i_valraw i_val +#endif +#ifndef i_valfrom + #define i_valfrom c_default_clone +#else + #define _i_has_from +#endif +#ifndef i_valto + #define i_valto c_default_toraw +#endif +#ifndef i_valclone + #define i_valclone c_default_clone +#endif +#ifndef i_valdrop + #define i_valdrop c_default_drop +#endif + +#endif // !_i_ismap + +#ifndef i_val + #define i_val i_key +#endif +#ifndef i_valraw + #define i_valraw i_keyraw +#endif +#ifndef _i_has_from + #define i_no_emplace +#endif + +#else // ============================================================ + +#undef i_type +#undef i_tag +#undef i_imp +#undef i_opt +#undef i_less +#undef i_cmp +#undef i_eq +#undef i_hash +#undef i_rawclass +#undef i_capacity +#undef i_size + +#undef i_val +#undef i_val_str +#undef i_val_ssv +#undef i_valboxed +#undef i_valclass +#undef i_valraw +#undef i_valclone +#undef i_valfrom +#undef i_valto +#undef i_valdrop + +#undef i_key +#undef i_key_str +#undef i_key_ssv +#undef i_keyboxed +#undef i_keyclass +#undef i_keyraw +#undef i_keyclone +#undef i_keyfrom +#undef i_keyto +#undef i_keydrop + +#undef i_header +#undef i_implement +#undef i_static +#undef i_extern + +#undef i_no_cmp +#undef i_no_eq +#undef i_no_hash +#undef i_no_clone +#undef i_no_emplace + +#undef _i_prefix +#undef _i_expandby +#undef _i_has_from +#undef _i_template +#endif diff --git a/include/stc/stctest.h b/include/stc/stctest.h deleted file mode 100644 index 9718af7f..00000000 --- a/include/stc/stctest.h +++ /dev/null @@ -1,203 +0,0 @@ -/* MIT License - * - * Copyright (c) 2022 Tyge Løvset, NORCE, www.norceresearch.no - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/* stctest: A small and simple C11 unit-testing framework. - - Features: - - Requires C11. Should work with any C compiler supporting _Generic. - - No library dependencies. Not even itself. Just a header file. - - Reports assertion failures, including expressions and line numbers. - - ANSI color output for maximum visibility. - - Easy to embed in apps for runtime tests (e.g. environment tests). - - Example: - - #include "stctest.h" - #include "mylib.h" - - void test_sheep() - { - EXPECT_EQ("Sheep are cool", are_sheep_cool()); - EXPECT_EQ(4, sheep.legs); - } - - void test_cheese() - { - EXPECT_GT(cheese.tanginess, 0); - EXPECT_EQ("Wensleydale", cheese.name); - } - - int main() - { - RUN_TEST(test_sheep); - RUN_TEST(test_cheese); - return REPORT_TESTS(); - } - */ - -#ifndef STCTEST_INCLUDED -#define STCTEST_INCLUDED - -#include -#include -#include -#include -#include -#include - -#define STC_FLOAT_EPSILON 1e-6 -#define STC_DOUBLE_EPSILON 1e-13 - - -#define EXPECT_TRUE(expr) \ - do { if (!_stctest_assert(__FILE__, __LINE__, #expr, (expr) != 0)) puts(""); } while (0) - -#define EXPECT_TRUE1(expr, v) \ - do { if (!_stctest_assert(__FILE__, __LINE__, #expr, (expr) != 0)) { \ - char _fmt[32]; sprintf(_fmt, " ; %%s --> %s\n", _stctest_FMT(v)); \ - printf(_fmt, #v, v); \ - }} while (0) - -#define EXPECT_FALSE(expr) EXPECT_TRUE(!(expr)) -#define EXPECT_FALSE1(expr, v) EXPECT_TRUE1(!(expr), v) - -/* NB! (char*) are compared as strings. Cast to (void*) to compare pointers only */ -#define EXPECT_EQ(a, b) _stctest_CHECK(a, ==, b, -STC_DOUBLE_EPSILON) -#define EXPECT_NE(a, b) _stctest_CHECK(a, !=, b, -STC_DOUBLE_EPSILON) -#define EXPECT_GT(a, b) _stctest_CHECK(a, >, b, -STC_DOUBLE_EPSILON) -#define EXPECT_LT(a, b) _stctest_CHECK(a, <, b, -STC_DOUBLE_EPSILON) -#define EXPECT_LE(a, b) _stctest_CHECK(a, <=, b, -STC_DOUBLE_EPSILON) -#define EXPECT_GE(a, b) _stctest_CHECK(a, >=, b, -STC_DOUBLE_EPSILON) -#define EXPECT_FLOAT_EQ(a, b) _stctest_CHECK((float)(a), ==, (float)(b), -STC_FLOAT_EPSILON) -#define EXPECT_DOUBLE_EQ(a, b) _stctest_CHECK((double)(a), ==, (double)(b), -STC_DOUBLE_EPSILON) -#define EXPECT_NEAR(a, b, abs_error) _stctest_CHECK((double)(a), ==, (double)(b), abs_error) - -/* Run a test() function */ -#define RUN_TEST(test, ...) do { \ - puts(#test "(" #__VA_ARGS__ "):"); \ - const int ps = _stctest_s.passes; \ - const int fs = _stctest_s.fails; \ - const clock_t _start = clock(); \ - test(__VA_ARGS__); \ - const int _sum = (clock() - _start)*1000 / CLOCKS_PER_SEC; \ - _stctest_s.total_ms += _sum; \ - printf(" passed: %d/%d. duration: %d ms\n", \ - _stctest_s.passes - ps, _stctest_s.passes + _stctest_s.fails - (ps + fs), _sum); \ -} while (0) - -#define REPORT_TESTS() stctest_report() - -/* ----------------------------------------------------------------------------- */ - -#define _stctest_CHECK(a, OP, b, e) \ - do { if (!_stctest_assert(__FILE__, __LINE__, #a " " #OP " " #b, _stctest_CMP(a, OP, b, e))) { \ - char _fmt[32]; sprintf(_fmt, " ; %s %s %s\n", _stctest_FMT(a), #OP, _stctest_FMT(b)); \ - printf(_fmt, a, b); \ - }} while (0) - -#define _stctest_CMP(a, OP, b, e) _Generic((a), \ - const char*: _stctest_strcmp, char*: _stctest_strcmp, \ - double: _Generic((b), double: _stctest_dblcmp, float: _stctest_dblcmp, default: _stctest_valcmp), \ - float: _Generic((b), double: _stctest_dblcmp, float: _stctest_dblcmp, default: _stctest_valcmp), \ - default: _stctest_valcmp)((a) OP (b), #OP, a, b, (double)(e)) - -#define _stctest_FMT(a) _Generic((a), \ - float: "%.8gf", double: "%.15g", \ - int64_t: "%" PRId64, int32_t: "%" PRId32, int16_t: "%" PRId16, int8_t: "%" PRId8, \ - uint64_t: "%" PRIu64 "u", uint32_t: "%" PRIu32 "u", uint16_t: "%" PRIu16 "u", uint8_t: "%" PRIu8 "u", \ - char*: "`%s`", const char*: "`%s`", \ - default: "%p") - -static int _stctest_strcmp(int res, const char* OP, ...) { - va_list ap; - va_start(ap, OP); - const char* a = va_arg(ap, const char *); - const char* b = va_arg(ap, const char *); - va_end(ap); - int c = strcmp(a, b); - if (OP[0] == '<') return OP[1] == '=' ? c <= 0 : c < 0; - if (OP[0] == '>') return OP[1] == '=' ? c >= 0 : c > 0; - return (OP[0] == '!') ^ (c == 0); -} - -// Knuth: -static int approximately_equal(double a, double b, double epsilon) { - double d = a - b; if (d < 0) d = -d; - if (a < 0) a = -a; if (b < 0) b = -b; - return d <= ((a < b ? b : a) * epsilon); // (a > b ? b : a) => essentially_equal: -} - -static int _stctest_dblcmp(int res, const char* OP, ...) { - va_list ap; - va_start(ap, OP); - double a = va_arg(ap, double); - double b = va_arg(ap, double); - double e = va_arg(ap, double); - double c = a - b; - va_end(ap); - if (OP[0] == '<') return OP[1] == '=' ? c <= 0 : c < 0; - if (OP[0] == '>') return OP[1] == '=' ? c >= 0 : c > 0; - return (OP[0] == '!') ^ (e < 0 ? approximately_equal(a, b, -e) : (c < 0 ? -c : c) <= e); -} - -static int _stctest_valcmp(int res, const char* OP, ...) - { return res; } - -#define _stctest_COLOR_CODE 0x1B -#define _stctest_COLOR_RED "[1;31m" -#define _stctest_COLOR_GREEN "[1;32m" -#define _stctest_COLOR_RESET "[0m" - -static struct stctest_s { - int passes; - int fails; - const char* current_file; - int total_ms; -} _stctest_s = {0}; - -static int _stctest_assert(const char* file, int line, const char* expression, int pass) { - if (pass) - _stctest_s.passes++; - else { - _stctest_s.fails++; - printf(" failed \"%s:%d\": (%s)", file, line, expression); - } - _stctest_s.current_file = file; - return pass; -} - -static int stctest_report(void) { - if (_stctest_s.fails) { - printf("%c%sFAILED%c%s: \"%s\": (failed %d, passed %d, total %d)\n", - _stctest_COLOR_CODE, _stctest_COLOR_RED, _stctest_COLOR_CODE, _stctest_COLOR_RESET, - _stctest_s.current_file, _stctest_s.fails, _stctest_s.passes, _stctest_s.passes + _stctest_s.fails); - } else { - printf("%c%sPASSED%c%s: \"%s\": (total %d)\n", - _stctest_COLOR_CODE, _stctest_COLOR_GREEN, _stctest_COLOR_CODE, _stctest_COLOR_RESET, - _stctest_s.current_file, _stctest_s.passes); - } - printf(" duration: %d ms\n", _stctest_s.total_ms); \ - return -_stctest_s.fails; -} - -#endif diff --git a/include/stc/template.h b/include/stc/template.h deleted file mode 100644 index b7583e82..00000000 --- a/include/stc/template.h +++ /dev/null @@ -1,331 +0,0 @@ -/* MIT License - * - * Copyright (c) 2022 Tyge Løvset, NORCE, www.norceresearch.no - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef _i_template -#define _i_template - -#ifndef STC_TEMPLATE_H_INCLUDED -#define STC_TEMPLATE_H_INCLUDED - #define _cx_self c_PASTE(_i_prefix, i_tag) - #define _cx_memb(name) c_PASTE(_cx_self, name) - #define _cx_deftypes(macro, SELF, ...) c_EXPAND(macro(SELF, __VA_ARGS__)) - #define _cx_value _cx_memb(_value) - #define _cx_key _cx_memb(_key) - #define _cx_mapped _cx_memb(_mapped) - #define _cx_raw _cx_memb(_raw) - #define _cx_rawkey _cx_memb(_rawkey) - #define _cx_iter _cx_memb(_iter) - #define _cx_result _cx_memb(_result) - #define _cx_node _cx_memb(_node) -#endif - -#ifdef i_type - #define i_tag i_type - #undef _i_prefix - #define _i_prefix -#endif - -#ifdef i_size - #define _i_expandby 2 -#else - #define i_size uint32_t - #define _i_expandby 1 -#endif - -#if !(defined i_key || defined i_key_str || defined i_key_ssv || \ - defined i_keyclass || defined i_keyboxed) - #if defined _i_ismap - #error "i_key* must be defined for maps." - #endif - - #if defined i_val_str - #define i_key_str i_val_str - #endif - #if defined i_val_ssv - #define i_key_ssv i_val_ssv - #endif - #if defined i_valboxed - #define i_keyboxed i_valboxed - #endif - #if defined i_valclass - #define i_keyclass i_valclass - #endif - #if defined i_val - #define i_key i_val - #endif - #if defined i_valraw - #define i_keyraw i_valraw - #endif - #if defined i_valclone - #define i_keyclone i_valclone - #endif - #if defined i_valfrom - #define i_keyfrom i_valfrom - #endif - #if defined i_valto - #define i_keyto i_valto - #endif - #if defined i_valdrop - #define i_keydrop i_valdrop - #endif -#endif - -#if c_option(c_no_cmp) - #define i_no_cmp -#endif -#if c_option(c_no_eq) - #define i_no_eq -#endif -#if c_option(c_no_hash) - #define i_no_hash -#endif -#if c_option(c_no_emplace) - #define i_no_emplace -#endif - -#if defined i_key_str - #define i_keyclass cstr - #define i_rawclass crawstr - #define i_keyfrom cstr_from - #ifndef i_tag - #define i_tag str - #endif -#elif defined i_key_ssv - #define i_keyclass cstr - #define i_rawclass csview - #define i_keyfrom cstr_from_sv - #define i_keyto cstr_sv - #define i_eq csview_eq - #ifndef i_tag - #define i_tag ssv - #endif -#elif defined i_keyboxed - #define i_keyclass i_keyboxed - #define i_rawclass c_PASTE(i_keyboxed, _raw) - #define i_keyfrom c_PASTE(i_keyboxed, _from) - #ifndef i_no_eq - #define i_eq c_PASTE(i_keyboxed, _raw_eq) - #endif -#endif - -#if defined i_rawclass - #define i_keyraw i_rawclass -#elif defined i_keyclass && !defined i_keyraw - #define i_rawclass i_key -#endif - -#ifdef i_keyclass - #define i_key i_keyclass - #ifndef i_keyclone - #define i_keyclone c_PASTE(i_key, _clone) - #endif - #if !defined i_keyto && defined i_keyraw - #define i_keyto c_PASTE(i_key, _toraw) - #endif - #ifndef i_keydrop - #define i_keydrop c_PASTE(i_key, _drop) - #endif -#endif - -#ifdef i_rawclass - #if !defined i_cmp && !defined i_no_cmp - #define i_cmp c_PASTE(i_keyraw, _cmp) - #endif - #if !defined i_hash && !defined i_no_hash - #define i_hash c_PASTE(i_keyraw, _hash) - #endif -#endif - -#if !defined i_key - #error "no i_key or i_val defined" -#elif defined i_keyraw ^ defined i_keyto - #error "both i_***raw and i_***to must be defined, if any" -#elif defined i_keyfrom && !defined i_keyraw - #error "i_***from defined without i_***raw" -#elif defined i_from || defined i_drop - #error "i_from / i_drop not supported. Define i_keyfrom/i_valfrom and/or i_keydrop/i_valdrop instead" -#endif - -#ifndef i_tag - #define i_tag i_key -#endif -#if c_option(c_no_clone) || (!defined i_keyclone && (defined i_keydrop || defined i_keyraw)) - #define i_no_clone -#endif -#ifndef i_keyraw - #define i_keyraw i_key -#endif -#ifndef i_keyfrom - #define i_keyfrom c_default_clone -#else - #define _i_has_from -#endif -#ifndef i_keyto - #define i_keyto c_default_toraw -#endif -#ifndef i_keyclone - #define i_keyclone c_default_clone -#endif -#ifndef i_keydrop - #define i_keydrop c_default_drop -#endif - -// i_eq, i_less, i_cmp -#if !defined i_eq && (defined i_cmp || defined i_less) - #define i_eq(x, y) !(i_cmp(x, y)) -#elif !defined i_eq - #define i_eq c_default_eq -#endif -#if defined i_less && defined i_cmp - #error "Only one of i_less and i_cmp may be defined" -#elif !defined i_less && !defined i_cmp - #define i_less c_default_less -#elif !defined i_less - #define i_less(x, y) (i_cmp(x, y)) < 0 -#endif -#ifndef i_cmp - #define i_cmp(x, y) (i_less(y, x)) - (i_less(x, y)) -#endif - -#ifndef i_hash - #define i_hash c_default_hash -#endif - -#if defined _i_ismap // ---- process cmap/csmap value i_val, ... ---- - -#ifdef i_val_str - #define i_valclass cstr - #define i_valraw crawstr - #define i_valfrom cstr_from -#elif defined i_val_ssv - #define i_valclass cstr - #define i_valraw csview - #define i_valfrom cstr_from_sv - #define i_valto cstr_sv -#elif defined i_valboxed - #define i_valclass i_valboxed - #define i_valraw c_PASTE(i_valboxed, _raw) - #define i_valfrom c_PASTE(i_valboxed, _from) -#endif - -#ifdef i_valclass - #define i_val i_valclass - #ifndef i_valclone - #define i_valclone c_PASTE(i_val, _clone) - #endif - #if !defined i_valto && defined i_valraw - #define i_valto c_PASTE(i_val, _toraw) - #endif - #ifndef i_valdrop - #define i_valdrop c_PASTE(i_val, _drop) - #endif -#endif - -#ifndef i_val - #error "i_val* must be defined for maps" -#endif - -#if !defined i_valclone && (defined i_valdrop || defined i_valraw) - #define i_no_clone -#endif -#ifndef i_valraw - #define i_valraw i_val -#endif -#ifndef i_valfrom - #define i_valfrom c_default_clone -#else - #define _i_has_from -#endif -#ifndef i_valto - #define i_valto c_default_toraw -#endif -#ifndef i_valclone - #define i_valclone c_default_clone -#endif -#ifndef i_valdrop - #define i_valdrop c_default_drop -#endif - -#endif // !_i_ismap - -#ifndef i_val - #define i_val i_key -#endif -#ifndef i_valraw - #define i_valraw i_keyraw -#endif -#ifndef _i_has_from - #define i_no_emplace -#endif - -#else // ============================================================ - -#undef i_type -#undef i_tag -#undef i_imp -#undef i_opt -#undef i_less -#undef i_cmp -#undef i_eq -#undef i_hash -#undef i_rawclass -#undef i_capacity -#undef i_size - -#undef i_val -#undef i_val_str -#undef i_val_ssv -#undef i_valboxed -#undef i_valclass -#undef i_valraw -#undef i_valclone -#undef i_valfrom -#undef i_valto -#undef i_valdrop - -#undef i_key -#undef i_key_str -#undef i_key_ssv -#undef i_keyboxed -#undef i_keyclass -#undef i_keyraw -#undef i_keyclone -#undef i_keyfrom -#undef i_keyto -#undef i_keydrop - -#undef i_header -#undef i_implement -#undef i_static -#undef i_extern - -#undef i_no_cmp -#undef i_no_eq -#undef i_no_hash -#undef i_no_clone -#undef i_no_emplace - -#undef _i_prefix -#undef _i_expandby -#undef _i_has_from -#undef _i_template -#endif -- cgit v1.2.3