From ebc7316ac3859bc5e2f01c85bee86d6adea45898 Mon Sep 17 00:00:00 2001 From: Tyge Løvset Date: Tue, 24 May 2022 21:12:56 +0200 Subject: API change: cbits now uses container pointers args in all member functions, except clone() ... for now. All containers may get same treatment! which will be a rather big API change. This was needed after testing fixed size cbits performance with pass-by-value: was very slow for large bitsets: now faster than std::bitset<>. Also reverted previous cbits_set_value(): much faster because it is branchless. --- README.md | 14 ++-- benchmarks/misc/cbits_benchmark.cpp | 123 ++++++++++++++++++++++++++++++++++++ docs/cbits_api.md | 52 +++++++-------- examples/bits.c | 44 ++++++------- examples/bits2.c | 24 +++---- examples/prime.c | 6 +- include/stc/cbits.h | 89 +++++++++++++------------- include/stc/ccommon.h | 6 +- 8 files changed, 243 insertions(+), 115 deletions(-) create mode 100644 benchmarks/misc/cbits_benchmark.cpp diff --git a/README.md b/README.md index 0194f906..0d402d79 100644 --- a/README.md +++ b/README.md @@ -249,14 +249,16 @@ After erasing elements found: Installation ------------ -Because it is headers-only, headers can simply be included in your program. The methods are static -by default (some inlined). You may add the *include* folder to the **CPATH** environment variable to +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. -If containers are used across several translation units with common instantiated container types, -it is recommended to build as a "library" to minimize the executable size. To enable this mode, -specify **-DSTC_HEADER** as a compiler option in your build environment and place all the instantiations -of containers used in a single C-source file, e.g.: +If containers are used across several translation units with common instantiated container types, it is +recommended to build as a "library" with external linking to minimize executable size. To enable this, +specify `-DSTC_HEADER` as compiler option in your build environment. Next, place all the instantiations +of the containers used inside a single C-source file as in the example below, and `#define STC_IMPLEMENT` at top. +You may also cherry-pick external linking mode on individual containers by `#define i_opt c_header` and +`#define i_opt c_implement`, or force static symbols by `#define i_opt c_static` before container includes. ```c // stc_libs.c #define STC_IMPLEMENT diff --git a/benchmarks/misc/cbits_benchmark.cpp b/benchmarks/misc/cbits_benchmark.cpp new file mode 100644 index 00000000..50e19a7c --- /dev/null +++ b/benchmarks/misc/cbits_benchmark.cpp @@ -0,0 +1,123 @@ +#include +#include +#include // rand +#include // timer + +enum{ N=1<<22 }; // 4.2 mill. + +#include +#define i_type cbits +#define i_len N +#include + +inline unsigned long get_time_in_ms() +{ + return (unsigned long)(1000 * clock() / CLOCKS_PER_SEC); +} + + +void one_sec_delay() +{ + unsigned long end_time = get_time_in_ms() + 1000; + + while(get_time_in_ms() < end_time) + { + } +} + + +int main(int argc, char **argv) +{ + size_t seed = time(NULL); + + using namespace std; + bool *bools = new bool[N]; + + unsigned long current_time, difference1, difference2; + uint64_t total; + + one_sec_delay(); + + total = 0; + csrandom(seed); + current_time = get_time_in_ms(); + + c_forrange (40 * N) + { + uint64_t r = crandom(); + bools[r & (N-1)] = r & 1<<29; + } + + difference1 = get_time_in_ms() - current_time; + current_time = get_time_in_ms(); + + c_forrange (100) c_forrange (num, N) + { + total += bools[num]; + } + + delete [] bools; + + difference2 = get_time_in_ms() - current_time; + + cout << "Bool:" << endl << "sum total = " << total << ", random access time = " << difference1 + << ", sequential access time = " << difference2 << endl << endl; + + one_sec_delay(); + + total = 0; + csrandom(seed); + current_time = get_time_in_ms(); + bitset bits; + + c_forrange (40 * N) + { + uint64_t r = crandom(); + bits[r & (N-1)] = r & 1<<29; + } + + difference1 = get_time_in_ms() - current_time; + current_time = get_time_in_ms(); + + c_forrange (100) c_forrange (num, N) + { + total += bits[num]; + } + + difference2 = get_time_in_ms() - current_time; + + cout << "Bitset:" << endl << "sum total = " << total << ", random access time = " << difference1 + << ", sequential access time = " << difference2 << endl << endl; + + one_sec_delay(); + + total = 0; + csrandom(seed); + current_time = get_time_in_ms(); + cbits bits2 = cbits_with_size(N, false); + + c_forrange (40 * N) + { + uint64_t r = crandom(); + cbits_set_value(&bits2, r & (N-1), r & 1<<29); + } + + difference1 = get_time_in_ms() - current_time; + current_time = get_time_in_ms(); + + c_forrange (100) c_forrange (num, N) + { + total += cbits_at(&bits2, num); + } + + cbits_drop(&bits2); + + difference2 = get_time_in_ms() - current_time; + + cout << "cbits:" << endl << "sum total = " << total << ", random access time = " << difference1 + << ", sequential access time = " << difference2 << endl << endl; + + //cin.get(); + + return 0; +} diff --git a/docs/cbits_api.md b/docs/cbits_api.md index b9d54737..613c2d8c 100644 --- a/docs/cbits_api.md +++ b/docs/cbits_api.md @@ -21,38 +21,38 @@ All cbits definitions and prototypes are available by including a single header ```c cbits cbits_init(void); cbits cbits_from(const char* str); -cbits cbits_with_size(size_t size, bool value); // size must be <= N if N is defined +cbits cbits_with_size(size_t size, bool value); // size must be <= N if N is defined cbits cbits_with_pattern(size_t size, uint64_t pattern); cbits cbits_clone(cbits other); void cbits_clear(cbits* self); -cbits* cbits_copy(cbits* self, cbits other); -void cbits_resize(cbits* self, size_t size, bool value); // only if i_len is not defined +cbits* cbits_copy(cbits* self, const cbits* other); +void cbits_resize(cbits* self, size_t size, bool value); // only if i_len is not defined void cbits_drop(cbits* self); -cbits* cbits_take(cbits* self, cbits other); // give other to self -cbits cbits_move(cbits* self); // transfer self to caller - -size_t cbits_size(cbits set); -size_t cbits_count(cbits set); // count number of bits set - -bool cbits_test(cbits set, size_t i); -bool cbits_at(cbits set, size_t i); // same as cbits_test() -bool cbits_subset_of(cbits set, cbits other); // is set a subset of other? -bool cbits_disjoint(cbits set, cbits other); // no common bits -char* cbits_to_str(cbits set, char* str, size_t start, intptr_t stop); - -void cbits_set(cbits *self, size_t i); -void cbits_reset(cbits *self, size_t i); -void cbits_set_value(cbits *self, size_t i, bool value); -void cbits_set_all(cbits *self, bool value); -void cbits_set_pattern(cbits *self, uint64_t pattern); -void cbits_flip_all(cbits *self); -void cbits_flip(cbits *self, size_t i); - -void cbits_intersect(cbits *self, cbits other); -void cbits_union(cbits *self, cbits other); -void cbits_xor(cbits *self, cbits other); // set of disjoint bits +cbits* cbits_take(cbits* self, const cbits* other); // give other to self +cbits cbits_move(cbits* self); // transfer self to caller + +size_t cbits_size(const cbits* self); +size_t cbits_count(const cbits* self); // count number of bits set + +bool cbits_test(const cbits* self, size_t i); +bool cbits_at(const cbits* self, size_t i); // same as cbits_test() +bool cbits_subset_of(const cbits* self, const cbits* other); // is set a subset of other? +bool cbits_disjoint(const cbits* self, const cbits* other); // no common bits +char* cbits_to_str(const cbits* self, char* str, size_t start, intptr_t stop); + +void cbits_set(cbits* self, size_t i); +void cbits_reset(cbits* self, size_t i); +void cbits_set_value(cbits* self, size_t i, bool value); +void cbits_set_all(cbits* self, bool value); +void cbits_set_pattern(cbits* self, uint64_t pattern); +void cbits_flip_all(cbits* self); +void cbits_flip(cbits* self, size_t i); + +void cbits_intersect(cbits* self, const cbits* other); +void cbits_union(cbits* self, const cbits* other); +void cbits_xor(cbits* self, const cbits* other); // set of disjoint bits ``` ## Types diff --git a/examples/bits.c b/examples/bits.c index bd2a9f03..8888f73e 100644 --- a/examples/bits.c +++ b/examples/bits.c @@ -4,21 +4,21 @@ int main() { c_autovar (cbits set = cbits_with_size(23, true), cbits_drop(&set)) { - printf("count %" PRIuMAX ", %" PRIuMAX "\n", cbits_count(set), cbits_size(set)); + printf("count %" PRIuMAX ", %" PRIuMAX "\n", cbits_count(&set), cbits_size(&set)); cbits s1 = cbits_from("1110100110111"); char buf[256]; - cbits_to_str(s1, buf, 0, -1); - printf("buf: %s: %" PRIuMAX "\n", buf, cbits_count(s1)); + cbits_to_str(&s1, buf, 0, -1); + printf("buf: %s: %" PRIuMAX "\n", buf, cbits_count(&s1)); cbits_drop(&s1); cbits_reset(&set, 9); cbits_resize(&set, 43, false); - c_autobuf (str, char, cbits_size(set) + 1) - printf(" str: %s\n", cbits_to_str(set, str, 0, -1)); + c_autobuf (str, char, cbits_size(&set) + 1) + printf(" str: %s\n", cbits_to_str(&set, str, 0, -1)); - printf("%4" PRIuMAX ": ", cbits_size(set)); - c_forrange (i, cbits_size(set)) - printf("%d", cbits_test(set, i)); + printf("%4" PRIuMAX ": ", cbits_size(&set)); + c_forrange (i, cbits_size(&set)) + printf("%d", cbits_test(&set, i)); puts(""); cbits_set(&set, 28); @@ -26,14 +26,14 @@ int main() cbits_resize(&set, 93, false); cbits_resize(&set, 102, true); cbits_set_value(&set, 99, false); - printf("%4" PRIuMAX ": ", cbits_size(set)); - c_forrange (i, cbits_size(set)) - printf("%d", cbits_test(set, i)); + printf("%4" PRIuMAX ": ", cbits_size(&set)); + c_forrange (i, cbits_size(&set)) + printf("%d", cbits_test(&set, i)); puts("\nIterate:"); - printf("%4" PRIuMAX ": ", cbits_size(set)); - c_forrange (i, cbits_size(set)) - printf("%d", cbits_test(set, i)); + printf("%4" PRIuMAX ": ", cbits_size(&set)); + c_forrange (i, cbits_size(&set)) + printf("%d", cbits_test(&set, i)); puts(""); c_autovar (cbits s2 = cbits_clone(set), cbits_drop(&s2)) { @@ -42,20 +42,20 @@ int main() cbits_set(&s2, 17); cbits_set(&s2, 18); printf(" new: "); - c_forrange (i, cbits_size(s2)) - printf("%d", cbits_test(s2, i)); + c_forrange (i, cbits_size(&s2)) + printf("%d", cbits_test(&s2, i)); puts(""); printf(" xor: "); - cbits_xor(&set, s2); - c_forrange (i, cbits_size(set)) - printf("%d", cbits_test(set, i)); + cbits_xor(&set, &s2); + c_forrange (i, cbits_size(&set)) + printf("%d", cbits_test(&set, i)); puts(""); cbits_set_all(&set, false); - printf("%4" PRIuMAX ": ", cbits_size(set)); - c_forrange (i, cbits_size(set)) - printf("%d", cbits_test(set, i)); + printf("%4" PRIuMAX ": ", cbits_size(&set)); + c_forrange (i, cbits_size(&set)) + printf("%d", cbits_test(&set, i)); puts(""); } } diff --git a/examples/bits2.c b/examples/bits2.c index e9202efc..81e28d60 100644 --- a/examples/bits2.c +++ b/examples/bits2.c @@ -9,14 +9,14 @@ int main() { Bits s1 = Bits_from("1110100110111"); - printf("size %" PRIuMAX "\n", Bits_size(s1)); + printf("size %" PRIuMAX "\n", Bits_size(&s1)); char buf[256]; - Bits_to_str(s1, buf, 0, -1); - printf("buf: %s: count=%" PRIuMAX "\n", buf, Bits_count(s1)); + Bits_to_str(&s1, buf, 0, -1); + printf("buf: %s: count=%" PRIuMAX "\n", buf, Bits_count(&s1)); Bits_reset(&s1, 8); - c_autobuf (str, char, Bits_size(s1) + 1) - printf(" s1: %s\n", Bits_to_str(s1, str, 0, -1)); + c_autobuf (str, char, Bits_size(&s1) + 1) + printf(" s1: %s\n", Bits_to_str(&s1, str, 0, -1)); Bits s2 = Bits_clone(s1); @@ -24,19 +24,19 @@ int main() Bits_reset(&s2, 66); Bits_reset(&s2, 67); printf(" s2: "); - c_forrange (i, Bits_size(s2)) - printf("%d", Bits_test(s2, i)); + c_forrange (i, Bits_size(&s2)) + printf("%d", Bits_test(&s2, i)); puts(""); printf("xor: "); - Bits_xor(&s1, s2); - c_forrange (i, Bits_size(s1)) - printf("%d", Bits_test(s1, i)); + Bits_xor(&s1, &s2); + c_forrange (i, Bits_size(&s1)) + printf("%d", Bits_test(&s1, i)); puts(""); printf("all: "); Bits_set_pattern(&s1, 0x3333333333333333); - c_forrange (i, Bits_size(s1)) - printf("%d", Bits_test(s1, i)); + c_forrange (i, Bits_size(&s1)) + printf("%d", Bits_test(&s1, i)); puts(""); } diff --git a/examples/prime.c b/examples/prime.c index aee0a89e..cc16603b 100644 --- a/examples/prime.c +++ b/examples/prime.c @@ -11,7 +11,7 @@ cbits sieveOfEratosthenes(size_t n) for (size_t i = 3; i < q; i += 2) { size_t j = i; for (; j < n; j += 2) { - if (cbits_test(bits, j>>1)) { + if (cbits_test(&bits, j>>1)) { i = j; break; } @@ -30,13 +30,13 @@ int main(void) clock_t t1 = clock(); c_autovar (cbits primes = sieveOfEratosthenes(n + 1), cbits_drop(&primes)) { puts("done"); - size_t np = cbits_count(primes); + size_t np = cbits_count(&primes); clock_t t2 = clock(); printf("number of primes: %" PRIuMAX ", time: %f\n", np, (t2 - t1) / (float)CLOCKS_PER_SEC); printf("2"); for (size_t i = 3; i < 1000; i += 2) - if (cbits_test(primes, i>>1)) printf(" %" PRIuMAX "", i); + if (cbits_test(&primes, i>>1)) printf(" %" PRIuMAX "", i); puts(""); } } diff --git a/include/stc/cbits.h b/include/stc/cbits.h index 3dc78b7d..9eb52cf6 100644 --- a/include/stc/cbits.h +++ b/include/stc/cbits.h @@ -34,8 +34,8 @@ int main() { cbits_reset(&bset, 9); cbits_resize(&bset, 43, false); - printf("%4zu: ", cbits_size(bset)); - c_forrange (i, cbits_size(bset)) + printf("%4zu: ", cbits_size(&bset)); + c_forrange (i, cbits_size(&bset)) printf("%d", cbits_at(&bset, i)); puts(""); cbits_set(&bset, 28); @@ -44,8 +44,8 @@ int main() { cbits_resize(&bset, 102, true); cbits_set_value(&bset, 99, false); - printf("%4zu: ", cbits_size(bset)); - c_forrange (i, cbits_size(bset)) + printf("%4zu: ", cbits_size(&bset)); + c_forrange (i, cbits_size(&bset)) printf("%d", cbits_at(&bset, i)); puts(""); } @@ -73,7 +73,7 @@ int main() { #if !defined i_len STC_API void cbits_resize(i_type* self, size_t size, bool value); -STC_API i_type* cbits_copy(i_type* self, i_type other); +STC_API i_type* cbits_copy(i_type* self, const i_type* other); #endif STC_API bool _cbits_subset_of(const uint64_t* set, const uint64_t* other, size_t sz); STC_API bool _cbits_disjoint(const uint64_t* set, const uint64_t* other, size_t sz); @@ -81,13 +81,13 @@ STC_API size_t _cbits_count(const uint64_t* set, const size_t sz); STC_API char* _cbits_to_str(const uint64_t* set, const size_t sz, char* out, size_t start, intptr_t stop); -STC_INLINE size_t _i_memb(_size)(i_type set); +STC_INLINE size_t _i_memb(_size)(const i_type* self); STC_INLINE void _i_memb(_set_all)(i_type *self, const bool value) - { memset(self->data64, value? ~0 : 0, _cbits_bytes(_i_memb(_size)(*self))); } + { memset(self->data64, value? ~0 : 0, _cbits_bytes(_i_memb(_size)(self))); } STC_INLINE void _i_memb(_set_pattern)(i_type *self, const uint64_t pattern) { - size_t n = _cbits_words(_i_memb(_size)(*self)); + size_t n = _cbits_words(_i_memb(_size)(self)); while (n--) self->data64[n] = pattern; } @@ -96,7 +96,7 @@ STC_INLINE void _i_memb(_set_pattern)(i_type *self, const uint64_t pattern) { #define _i_assert(x) (void)0 STC_INLINE i_type _i_memb(_init)(void) { return c_make(i_type){0}; } STC_INLINE void _i_memb(_drop)(i_type* self) {} -STC_INLINE size_t _i_memb(_size)(i_type set) { return i_len; } +STC_INLINE size_t _i_memb(_size)(const i_type* self) { return i_len; } STC_INLINE i_type _i_memb(_move)(i_type* self) { return *self; } STC_INLINE i_type* _i_memb(_take)(i_type* self, i_type other) @@ -125,7 +125,7 @@ STC_INLINE i_type _i_memb(_with_pattern)(const size_t size, const uint64_t patte #define _i_assert(x) assert(x) STC_INLINE i_type cbits_init(void) { return c_make(i_type){NULL}; } STC_INLINE void cbits_drop(i_type* self) { c_free(self->data64); } -STC_INLINE size_t cbits_size(i_type set) { return set._size; } +STC_INLINE size_t cbits_size(const i_type* self) { return self->_size; } STC_INLINE i_type cbits_move(i_type* self) { i_type tmp = *self; @@ -160,11 +160,11 @@ STC_INLINE i_type cbits_with_pattern(const size_t size, const uint64_t pattern) } #endif -STC_INLINE bool _i_memb(_test)(i_type set, const size_t i) - { return (set.data64[i>>6] & _cbits_bit(i)) != 0; } +STC_INLINE bool _i_memb(_test)(const i_type* self, const size_t i) + { return (self->data64[i>>6] & _cbits_bit(i)) != 0; } -STC_INLINE bool _i_memb(_at)(i_type set, const size_t i) - { return (set.data64[i>>6] & _cbits_bit(i)) != 0; } +STC_INLINE bool _i_memb(_at)(const i_type* self, const size_t i) + { return (self->data64[i>>6] & _cbits_bit(i)) != 0; } STC_INLINE void _i_memb(_set)(i_type *self, const size_t i) { self->data64[i>>6] |= _cbits_bit(i); } @@ -172,14 +172,15 @@ STC_INLINE void _i_memb(_set)(i_type *self, const size_t i) STC_INLINE void _i_memb(_reset)(i_type *self, const size_t i) { self->data64[i>>6] &= ~_cbits_bit(i); } -STC_INLINE void _i_memb(_set_value)(i_type *self, const size_t i, const bool value) - { value ? _i_memb(_set)(self, i) : _i_memb(_reset)(self, i); } +STC_INLINE void _i_memb(_set_value)(i_type *self, const size_t i, const bool b) { + self->data64[i>>6] ^= ((uint64_t)-(int)b ^ self->data64[i>>6]) & _cbits_bit(i); +} STC_INLINE void _i_memb(_flip)(i_type *self, const size_t i) { self->data64[i>>6] ^= _cbits_bit(i); } STC_INLINE void _i_memb(_flip_all)(i_type *self) { - size_t n = _cbits_words(_i_memb(_size)(*self)); + size_t n = _cbits_words(_i_memb(_size)(self)); while (n--) self->data64[n] ^= ~(uint64_t)0; } @@ -191,22 +192,22 @@ STC_INLINE i_type _i_memb(_from)(const char* str) { } /* Intersection */ -STC_INLINE void _i_memb(_intersect)(i_type *self, i_type other) { - _i_assert(self->_size == other._size); - size_t n = _cbits_words(_i_memb(_size)(*self)); - while (n--) self->data64[n] &= other.data64[n]; +STC_INLINE void _i_memb(_intersect)(i_type *self, const i_type* other) { + _i_assert(self->_size == other->_size); + size_t n = _cbits_words(_i_memb(_size)(self)); + while (n--) self->data64[n] &= other->data64[n]; } /* Union */ -STC_INLINE void _i_memb(_union)(i_type *self, i_type other) { - _i_assert(self->_size == other._size); - size_t n = _cbits_words(_i_memb(_size)(*self)); - while (n--) self->data64[n] |= other.data64[n]; +STC_INLINE void _i_memb(_union)(i_type *self, const i_type* other) { + _i_assert(self->_size == other->_size); + size_t n = _cbits_words(_i_memb(_size)(self)); + while (n--) self->data64[n] |= other->data64[n]; } /* Exclusive disjunction */ -STC_INLINE void _i_memb(_xor)(i_type *self, i_type other) { - _i_assert(self->_size == other._size); - size_t n = _cbits_words(_i_memb(_size)(*self)); - while (n--) self->data64[n] ^= other.data64[n]; +STC_INLINE void _i_memb(_xor)(i_type *self, const i_type* other) { + _i_assert(self->_size == other->_size); + size_t n = _cbits_words(_i_memb(_size)(self)); + while (n--) self->data64[n] ^= other->data64[n]; } #if defined(__GNUC__) || defined(__clang__) @@ -223,31 +224,31 @@ STC_INLINE void _i_memb(_xor)(i_type *self, i_type other) { } #endif -STC_INLINE size_t _i_memb(_count)(i_type set) - { return _cbits_count(set.data64, _i_memb(_size)(set)); } +STC_INLINE size_t _i_memb(_count)(const i_type* self) + { return _cbits_count(self->data64, _i_memb(_size)(self)); } -STC_INLINE char* _i_memb(_to_str)(i_type set, char* out, size_t start, intptr_t stop) - { return _cbits_to_str(set.data64, _i_memb(_size)(set), out, start, stop); } +STC_INLINE char* _i_memb(_to_str)(const i_type* self, char* out, size_t start, intptr_t stop) + { return _cbits_to_str(self->data64, _i_memb(_size)(self), out, start, stop); } -STC_INLINE bool _i_memb(_subset_of)(i_type set, i_type other) { - _i_assert(set._size == other._size); - return _cbits_subset_of(set.data64, other.data64, _i_memb(_size)(set)); +STC_INLINE bool _i_memb(_subset_of)(const i_type* self, const i_type* other) { + _i_assert(self->_size == other->_size); + return _cbits_subset_of(self->data64, other->data64, _i_memb(_size)(self)); } -STC_INLINE bool _i_memb(_disjoint)(i_type set, i_type other) { - _i_assert(set._size == other._size); - return _cbits_disjoint(set.data64, other.data64, _i_memb(_size)(set)); +STC_INLINE bool _i_memb(_disjoint)(const i_type* self, const i_type* other) { + _i_assert(self->_size == other->_size); + return _cbits_disjoint(self->data64, other->data64, _i_memb(_size)(self)); } #if defined(i_implement) #if !defined i_len -STC_DEF i_type* cbits_copy(i_type* self, i_type other) { - if (self->data64 == other.data64) +STC_DEF i_type* cbits_copy(i_type* self, const i_type* other) { + if (self->data64 == other->data64) return self; - if (self->_size != other._size) - return _i_memb(_take)(self, _i_memb(_clone)(other)); - memcpy(self->data64, other.data64, _cbits_bytes(other._size)); + if (self->_size != other->_size) + return _i_memb(_take)(self, _i_memb(_clone)(*other)); + memcpy(self->data64, other->data64, _cbits_bytes(other->_size)); return self; } diff --git a/include/stc/ccommon.h b/include/stc/ccommon.h index ffdd0215..a0fa58fb 100644 --- a/include/stc/ccommon.h +++ b/include/stc/ccommon.h @@ -108,6 +108,7 @@ #define c_no_cmp (1<<3) #define c_static (1<<4) #define c_header (1<<5) +#define c_implement (1<<6) /* Generic algorithms */ @@ -254,7 +255,7 @@ STC_INLINE char* c_strnstrn(const char *s, const char *needle, #undef _i_static #undef i_implement -#if !c_option(c_static) && (c_option(c_header) || defined(STC_HEADER) || \ +#if !c_option(c_static) && (c_option(c_header|c_implement) || defined(STC_HEADER) || \ defined(STC_IMPLEMENT) || defined(STC_IMPLEMENTATION)) # define STC_API extern # define STC_DEF @@ -263,6 +264,7 @@ STC_INLINE char* c_strnstrn(const char *s, const char *needle, # define STC_API static inline # define STC_DEF static inline #endif -#if defined(_i_static) || defined(STC_IMPLEMENT) || defined(STC_IMPLEMENTATION) +#if defined(_i_static) || c_option(c_implement) || defined(STC_IMPLEMENT) \ + || defined(STC_IMPLEMENTATION) # define i_implement #endif -- cgit v1.2.3