diff --git a/array/CMakeLists.txt b/array/CMakeLists.txt index 18695cf..f64ccf3 100644 --- a/array/CMakeLists.txt +++ b/array/CMakeLists.txt @@ -5,10 +5,10 @@ set(CMAKE_C_STANDARD 99) set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_C_EXTENSIONS OFF) -add_library( - sc_array SHARED - sc_array.c +add_library(sc_array SHARED sc_array.h) +set_target_properties(sc_array PROPERTIES LINKER_LANGUAGE C) + target_include_directories(sc_array PUBLIC ${CMAKE_CURRENT_LIST_DIR}) @@ -37,7 +37,7 @@ if (SC_BUILD_TEST) enable_testing() - add_executable(${PROJECT_NAME}_test array_test.c sc_array.c) + add_executable(${PROJECT_NAME}_test array_test.c) target_compile_options(${PROJECT_NAME}_test PRIVATE -DSC_ARRAY_MAX=140000ul) diff --git a/array/README.md b/array/README.md index 5b0c16f..27a58d3 100644 --- a/array/README.md +++ b/array/README.md @@ -2,8 +2,9 @@ ### Overview -- Type generic array/vector. -- Index access is possible (e.g float* arr; 'printf("%f", arr[i]')). +- Growable array/vector. +- It comes with predefined types, check out at the end of sc_array.h, you can + add there (sc_array_def) if you need more. ### Usage @@ -14,39 +15,40 @@ void example_str() { - char **p, *it; + const char *it; + struct sc_array_str arr; - sc_array_create(p, 0); - - sc_array_add(p, "item0"); - sc_array_add(p, "item1"); - sc_array_add(p, "item2"); + sc_array_init(&arr); - printf("\nDelete first element \n\n"); - sc_array_del(p, 0); + sc_array_add(&arr, "item0"); + sc_array_add(&arr, "item1"); + sc_array_add(&arr, "item2"); - sc_array_foreach (p, it) { - printf("Elem = %s \n", it); - } + printf("\nDelete first element \n\n"); + sc_array_del(&arr, 0); - sc_array_destroy(p); + sc_array_foreach (&arr, it) { + printf("Elem = %s \n", it); + } + + sc_array_term(&arr); } void example_int() { - int *p; + struct sc_array_int arr; - sc_array_create(p, 0); + sc_array_init(&arr); - sc_array_add(p, 0); - sc_array_add(p, 1); - sc_array_add(p, 2); + sc_array_add(&arr, 0); + sc_array_add(&arr, 1); + sc_array_add(&arr, 2); - for (size_t i = 0; i < sc_array_size(p); i++) { - printf("Elem = %d \n", p[i]); - } + for (size_t i = 0; i < sc_array_size(&arr); i++) { + printf("Elem = %d \n", arr.elems[i]); + } - sc_array_destroy(p); + sc_array_term(&arr); } int main(int argc, char *argv[]) @@ -57,45 +59,3 @@ int main(int argc, char *argv[]) return 0; } ``` - -##### Note - -Array pointer is not stable. If you pass the array to another function which -can add items, do it by passing reference of the array pointer : - -```c -void some_function_to_add_elems(long **p) -{ - sc_array_add(*p, 500); -} - -int main(int argc, char *argv[]) -{ - long *p; - - sc_array_create(p, 0); - sc_array_add(p, 300); - - // Pass via address of p - some_function_to_add_elems(&p); - sc_array_destroy(p); -} - -``` - -Most probably you will hold array pointer in a struct anyway, so you have a -stable address of the pointer, "*numbers" might change but "numbers" is stable : - -```c -struct my_app { - int *numbers; -}; - -void func(struct my_app* app) -{ - sc_array_add(app->numbers, 0); - sc_array_add(app->numbers, 1); - sc_array_add(app->numbers, 2); -} - -``` \ No newline at end of file diff --git a/array/array_example.c b/array/array_example.c index 5ed358c..c521f5e 100644 --- a/array/array_example.c +++ b/array/array_example.c @@ -4,39 +4,40 @@ void example_str() { - char **p, *it; + const char *it; + struct sc_array_str arr; - sc_array_create(p, 0); + sc_array_init(&arr); - sc_array_add(p, "item0"); - sc_array_add(p, "item1"); - sc_array_add(p, "item2"); + sc_array_add(&arr, "item0"); + sc_array_add(&arr, "item1"); + sc_array_add(&arr, "item2"); printf("\nDelete first element \n\n"); - sc_array_del(p, 0); + sc_array_del(&arr, 0); - sc_array_foreach (p, it) { - printf("Elem = %s \n", it); - } + sc_array_foreach (&arr, it) { + printf("Elem = %s \n", it); + } - sc_array_destroy(p); + sc_array_term(&arr); } void example_int() { - int *p; + struct sc_array_int arr; - sc_array_create(p, 0); + sc_array_init(&arr); - sc_array_add(p, 0); - sc_array_add(p, 1); - sc_array_add(p, 2); + sc_array_add(&arr, 0); + sc_array_add(&arr, 1); + sc_array_add(&arr, 2); - for (size_t i = 0; i < sc_array_size(p); i++) { - printf("Elem = %d \n", p[i]); + for (size_t i = 0; i < sc_array_size(&arr); i++) { + printf("Elem = %d \n", arr.elems[i]); } - sc_array_destroy(p); + sc_array_term(&arr); } int main() diff --git a/array/array_test.c b/array/array_test.c index 134d36c..33c1a6c 100644 --- a/array/array_test.c +++ b/array/array_test.c @@ -4,34 +4,42 @@ #include #include -int example() +void example_str() { - int *p; - int val; + const char *it; + struct sc_array_str arr; - sc_array_create(p, 0); + sc_array_init(&arr); - sc_array_add(p, 0); - sc_array_add(p, 1); - sc_array_add(p, 3); + sc_array_add(&arr, "item0"); + sc_array_add(&arr, "item1"); + sc_array_add(&arr, "item2"); - printf("\nRemoving first element \n\n"); - sc_array_del(p, 0); + printf("\nDelete first element \n\n"); + sc_array_del(&arr, 0); - printf("Capacity %zu \n", sc_array_cap(p)); - printf("Element count %zu \n", sc_array_size(p)); - - for (size_t i = 0; i < sc_array_size(p); i++) { - printf("Elem = %d \n", p[i]); + sc_array_foreach (&arr, it) { + printf("Elem = %s \n", it); } - sc_array_foreach (p, val) { - printf("Elem = %d \n", val); + sc_array_term(&arr); +} + +void example_int() +{ + struct sc_array_int arr; + + sc_array_init(&arr); + + sc_array_add(&arr, 0); + sc_array_add(&arr, 1); + sc_array_add(&arr, 2); + + for (size_t i = 0; i < sc_array_size(&arr); i++) { + printf("Elem = %d \n", arr.elems[i]); } - sc_array_destroy(p); - - return 0; + sc_array_term(&arr); } static int compare(const void *a, const void *b) @@ -44,154 +52,164 @@ static int compare(const void *a, const void *b) static void test1(void) { - int *arr, total = 0; - sc_array_create(arr, 10); - assert(arr != NULL); - sc_array_destroy(arr); - assert(sc_array_cap(arr) == 0); - assert(sc_array_size(arr) == 0); - sc_array_add(arr, 1); - sc_array_add(arr, 2); - sc_array_add(arr, 3); - assert(arr[0] == 1); - assert(arr[1] == 2); - assert(arr[2] == 3); - sc_array_destroy(arr); + int total = 0; + struct sc_array_int arr; - sc_array_create(arr, 5); - sc_array_add(arr, 3); - sc_array_add(arr, 4); - sc_array_add(arr, 5); + sc_array_init(&arr); + sc_array_term(&arr); + assert(sc_array_size(&arr) == 0); + sc_array_add(&arr, 1); + sc_array_add(&arr, 2); + sc_array_add(&arr, 3); + assert(arr.elems[0] == 1); + assert(arr.elems[1] == 2); + assert(arr.elems[2] == 3); + sc_array_term(&arr); - assert(sc_array_size(arr) == 3); + sc_array_init(&arr); + sc_array_add(&arr, 3); + sc_array_add(&arr, 4); + sc_array_add(&arr, 5); - sc_array_del(arr, 0); - assert(arr[0] == 4); - sc_array_del_last(arr); - assert(arr[0] == 4); + assert(sc_array_size(&arr) == 3); - sc_array_add(arr, 1); - sc_array_add(arr, 3); - sc_array_add(arr, 2); - sc_array_add(arr, 0); + sc_array_del(&arr, 0); + assert(arr.elems[0] == 4); + sc_array_del_last(&arr); + assert(arr.elems[0] == 4); - assert(sc_array_last(arr) == 0); + sc_array_add(&arr, 1); + sc_array_add(&arr, 3); + sc_array_add(&arr, 2); + sc_array_add(&arr, 0); - sc_array_sort(arr, compare); + assert(sc_array_last(&arr) == 0); - for (size_t i = 0; i < sc_array_size(arr); i++) { - total += arr[i]; + sc_array_sort(&arr, compare); + + for (size_t i = 0; i < sc_array_size(&arr); i++) { + total += arr.elems[i]; } assert(total == 10); - for (size_t i = 0; i < sc_array_size(arr); i++) { - assert(arr[i] == (int) i); + for (size_t i = 0; i < sc_array_size(&arr); i++) { + assert(arr.elems[i] == (int) i); } - sc_array_destroy(arr); + sc_array_term(&arr); } void test2() { - int *arr; int val; - bool b; + struct sc_array_int arr; - b = sc_array_create(arr, 0); - assert(b); + sc_array_init(&arr); - sc_array_foreach (arr, val) { + sc_array_foreach (&arr, val) { assert(true); } - sc_array_destroy(arr); + sc_array_term(&arr); - b = sc_array_create(arr, 2); - assert(b); + sc_array_init(&arr); - sc_array_foreach (arr, val) { + sc_array_foreach (&arr, val) { assert(true); } - sc_array_destroy(arr); + sc_array_term(&arr); - b = sc_array_create(arr, 2); - assert(b); + sc_array_init(&arr); - b = sc_array_add(arr, 1); - assert(b); + sc_array_add(&arr, 1); + assert(!sc_array_oom(&arr)); - sc_array_foreach (arr, val) { + sc_array_foreach (&arr, val) { assert(val == 1); } - sc_array_del_last(arr); - sc_array_foreach (arr, val) { + sc_array_del_last(&arr); + sc_array_foreach (&arr, val) { assert(true); } - b = sc_array_add(arr, 1); - assert(b == true); - sc_array_del_unordered(arr, 0); - sc_array_foreach (arr, val) { + sc_array_add(&arr, 1); + assert(!sc_array_oom(&arr)); + + sc_array_del_unordered(&arr, 0); + + sc_array_foreach (&arr, val) { assert(true); } - sc_array_destroy(arr); + sc_array_term(&arr); + + sc_array_init(&arr); + sc_array_add(&arr, 100); + sc_array_add(&arr, 200); + sc_array_add(&arr, 300); + assert(sc_array_at(&arr, 0) == 100); + sc_array_del_last(&arr); + assert(sc_array_at(&arr, 0) == 100); + sc_array_del(&arr, 0); + assert(sc_array_at(&arr, 0) == 200); + sc_array_term(&arr); } void bounds_test() { - int *arr, total = 0; + int total = 0; int val; + struct sc_array_int arr; - sc_array_create(arr, 2); - sc_array_add(arr, 3); - sc_array_add(arr, 4); + sc_array_init(&arr); + sc_array_add(&arr, 3); + sc_array_add(&arr, 4); - sc_array_foreach (arr, val) { + sc_array_foreach (&arr, val) { total += val; } assert(total == 7); - sc_array_destroy(arr); + sc_array_term(&arr); total = 0; - sc_array_create(arr, 0); - sc_array_foreach (arr, val) { + sc_array_init(&arr); + sc_array_foreach (&arr, val) { total += val; } - sc_array_foreach (arr, val) { + sc_array_foreach (&arr, val) { total += val; } assert(total == 0); - sc_array_destroy(arr); + sc_array_term(&arr); - sc_array_create(arr, 0); - sc_array_add(arr, 0); - sc_array_add(arr, 1); - sc_array_add(arr, 2); - sc_array_add(arr, 4); - sc_array_add(arr, 3); + sc_array_init(&arr); + sc_array_add(&arr, 0); + sc_array_add(&arr, 1); + sc_array_add(&arr, 2); + sc_array_add(&arr, 4); + sc_array_add(&arr, 3); - sc_array_del(arr, 3); - for (size_t i = 0; i < sc_array_size(arr); i++) { - assert((int) i == arr[i]); + sc_array_del(&arr, 3); + for (size_t i = 0; i < sc_array_size(&arr); i++) { + assert((int) i == arr.elems[i]); } - sc_array_add(arr, 3); - sc_array_add(arr, 4); + sc_array_add(&arr, 3); + sc_array_add(&arr, 4); - sc_array_del(arr, 3); - for (size_t i = 0; i < sc_array_size(arr); i++) { - assert((int) i == arr[i]); + sc_array_del(&arr, 3); + for (size_t i = 0; i < sc_array_size(&arr); i++) { + assert((int) i == arr.elems[i]); } - sc_array_destroy(arr); + sc_array_term(&arr); } #ifdef SC_HAVE_WRAP @@ -210,99 +228,108 @@ void *__wrap_realloc(void *p, size_t n) void fail_test() { int tmp; - int *arr, total = 0; + int total = 0; + struct sc_array_int arr; - assert(sc_array_create(arr, SIZE_MAX) == false); - assert(arr == NULL); - assert(sc_array_create(arr, 0) == true); - assert(arr != NULL); - sc_array_destroy(arr); - assert(sc_array_cap(arr) == 0); - assert(sc_array_size(arr) == 0); + sc_array_init(&arr); - sc_array_foreach (arr, tmp) { + sc_array_add(&arr, 0); + assert(!sc_array_oom(&arr)); + sc_array_term(&arr); + + sc_array_init(&arr); + assert(sc_array_size(&arr) == 0); + + sc_array_foreach (&arr, tmp) { assert(false); } - assert(sc_array_create(arr, 0) == true); - - size_t count = SC_ARRAY_MAX / sizeof(*arr); - bool success = false; + size_t count = SC_ARRAY_MAX / sizeof(int); for (size_t i = 0; i < count + 5; i++) { - success = sc_array_add(arr, i); + sc_array_add(&arr, i); } - assert(!success); + assert(sc_array_oom(&arr)); + sc_array_del(&arr, 0); + sc_array_add(&arr, 400); + assert(sc_array_oom(&arr) == false); - sc_array_destroy(arr); - sc_array_create(arr, 0); - assert(sc_array_size(arr) == 0); + sc_array_term(&arr); + + sc_array_init(&arr); + assert(sc_array_size(&arr) == 0); fail_realloc = true; - success = sc_array_add(arr, 0); - assert(!success); + sc_array_add(&arr, 0); + assert(sc_array_oom(&arr)); fail_realloc = false; - success = sc_array_add(arr, 222); - assert(success); - sc_array_destroy(arr); + sc_array_add(&arr, 222); + assert(!sc_array_oom(&arr)); + sc_array_term(&arr); fail_realloc = true; - assert(sc_array_create(arr, 222) == false); + sc_array_init(&arr); + sc_array_add(&arr, 3); + assert(sc_array_oom(&arr)); fail_realloc = false; + sc_array_add(&arr, 3); + assert(sc_array_size(&arr) == 1); + assert(sc_array_oom(&arr) == false); + sc_array_term(&arr); - assert(sc_array_create(arr, 0) == true); + sc_array_init(&arr); fail_realloc = true; - success = sc_array_add(arr, 222); - assert(!success); + sc_array_add(&arr, 222); + assert(sc_array_oom(&arr)); fail_realloc = false; - sc_array_add(arr, 3); - sc_array_add(arr, 4); - sc_array_add(arr, 5); + sc_array_add(&arr, 3); + sc_array_add(&arr, 4); + sc_array_add(&arr, 5); - assert(sc_array_size(arr) == 3); + assert(sc_array_size(&arr) == 3); - sc_array_del(arr, 0); - assert(arr[0] == 4); - sc_array_del_last(arr); - assert(arr[0] == 4); + sc_array_del(&arr, 0); + assert(arr.elems[0] == 4); + sc_array_del_last(&arr); + assert(arr.elems[0] == 4); - sc_array_add(arr, 1); - sc_array_add(arr, 3); - sc_array_add(arr, 2); - sc_array_add(arr, 0); + sc_array_add(&arr, 1); + sc_array_add(&arr, 3); + sc_array_add(&arr, 2); + sc_array_add(&arr, 0); - sc_array_sort(arr, compare); + sc_array_sort(&arr, compare); - for (size_t i = 0; i < sc_array_size(arr); i++) { - total += arr[i]; + for (size_t i = 0; i < sc_array_size(&arr); i++) { + total += arr.elems[i]; } assert(total == 10); - for (size_t i = 0; i < sc_array_size(arr); i++) { - assert(arr[i] == (int) i); + for (size_t i = 0; i < sc_array_size(&arr); i++) { + assert(arr.elems[i] == (int) i); } total = 0; - sc_array_foreach (arr, tmp) { + sc_array_foreach (&arr, tmp) { total += tmp; } assert(total == 10); - sc_array_sort(arr, compare); - sc_array_del_unordered(arr, 0); - assert(arr[0] == 4); - assert(sc_array_size(arr) == 4); - sc_array_clear(arr); - assert(sc_array_size(arr) == 0); - sc_array_add(arr, 10); - assert(sc_array_size(arr) == 1); - assert(arr[0] == 10); + sc_array_sort(&arr, compare); + sc_array_del_unordered(&arr, 0); + assert(arr.elems[0] == 4); + assert(sc_array_size(&arr) == 4); + sc_array_clear(&arr); + assert(sc_array_size(&arr) == 0); + sc_array_add(&arr, 10); + assert(sc_array_size(&arr) == 1); + assert(arr.elems[0] == 10); - sc_array_destroy(arr); + sc_array_term(&arr); } #else @@ -316,7 +343,8 @@ int main(int argc, char *argv[]) (void) argc; (void) argv; - example(); + example_str(); + example_int(); test1(); test2(); fail_test(); diff --git a/array/sc_array.c b/array/sc_array.c deleted file mode 100644 index e403bf3..0000000 --- a/array/sc_array.c +++ /dev/null @@ -1,111 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2021 Ozan Tezcan - * - * 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 "sc_array.h" - -#ifndef SC_ARRAY_MAX -#define SC_ARRAY_MAX SIZE_MAX -#endif - -/** - * Empty array instance. - * Zero element arrays point at it to avoid initial allocation, so unused - * arrays will not allocate memory. - */ -static const struct sc_array sc_empty = {.size = 0, .cap = 0}; - -bool sc_array_init(void *a, size_t elem_size, size_t cap) -{ - const size_t max = SC_ARRAY_MAX / elem_size; - const size_t bytes = sizeof(struct sc_array) + (elem_size * cap); - - void **p = a; - struct sc_array *m; - - if (cap == 0) { - *p = (void *) sc_empty.elems; - return true; - } - - if (cap > max) { - *p = NULL; - return false; - } - - m = sc_array_realloc(NULL, bytes); - if (m == NULL) { - *p = NULL; - return false; - } - - m->size = 0; - m->cap = cap; - *p = m->elems; - - return true; -} - -void sc_array_term(void *a) -{ - void **p = a; - struct sc_array *m = sc_array_meta(*p); - - if (m != &sc_empty) { - sc_array_free(m); - } - - *p = (void *) sc_empty.elems; -} - -bool sc_array_expand(void *a, size_t elem_size) -{ - const size_t max = SC_ARRAY_MAX / elem_size; - - size_t size, cap, bytes; - void **p = a; - struct sc_array *prev; - struct sc_array *m = sc_array_meta(*p); - - if (m->size == m->cap) { - if (m->cap > max / 2) { - return false; - } - - size = m->size; - cap = (m != &sc_empty) ? m->cap * 2 : 2; - prev = (m != &sc_empty) ? m : NULL; - - bytes = sizeof(*m) + (elem_size * cap); - m = sc_array_realloc(prev, bytes); - if (m == NULL) { - return false; - } - - m->size = size; - m->cap = cap; - *p = m->elems; - } - - return true; -} diff --git a/array/sc_array.h b/array/sc_array.h index c804438..6317844 100644 --- a/array/sc_array.h +++ b/array/sc_array.h @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - #ifndef SC_ARRAY_H #define SC_ARRAY_H @@ -38,63 +37,103 @@ #include "config.h" #else #define sc_array_realloc realloc -#define sc_array_free free +#define sc_array_free free #endif -// Internals, do not use -struct sc_array { - size_t size; - size_t cap; - unsigned char elems[]; -}; +#ifndef SC_ARRAY_MAX +#define SC_ARRAY_MAX SIZE_MAX +#endif -#define sc_array_meta(a) \ - ((struct sc_array *) ((char *) (a) -offsetof(struct sc_array, elems))) - -bool sc_array_init(void *a, size_t elem_size, size_t cap); -void sc_array_term(void *a); -bool sc_array_expand(void *a, size_t elem_size); -#define sc_array_sizeof(a) (sizeof(a)) // NOLINT -// Internals end +#define sc_array_def(T, name) \ + struct sc_array_##name { \ + bool oom; \ + size_t cap; \ + size_t size; \ + /* NOLINTNEXTLINE */ \ + T *elems; \ + } +/** + * Init array + * @param a array + */ +#define sc_array_init(a) \ + do { \ + memset((a), 0, sizeof(*(a))); \ + } while (0) /** - * @param arr array - * @param cap initial capacity. '0' is a valid initial capacity. - * @return 'true' on success, 'false' on out of memory + * Term array + * @param a array */ -#define sc_array_create(a, cap) sc_array_init(&(a), sc_array_sizeof(*(a)), cap) +#define sc_array_term(a) \ + do { \ + sc_array_free((a)->elems); \ + sc_array_init(a); \ + } while (0) /** - * @param a array to be destroyed + * Add elem to array, call sc_array_oom(v) to see if 'add' failed because of out + * of memory. + * + * @param a array + * @param k elem */ -#define sc_array_destroy(a) sc_array_term(&(a)); +#define sc_array_add(a, k) \ + do { \ + const size_t _max = SC_ARRAY_MAX / sizeof(*(a)->elems); \ + size_t _cap; \ + void *_p; \ + \ + if ((a)->cap == (a)->size) { \ + if ((a)->cap > _max / 2) { \ + (a)->oom = true; \ + break; \ + } \ + _cap = (a)->cap == 0 ? 8 : (a)->cap * 2; \ + _p = sc_array_realloc((a)->elems, \ + _cap * sizeof(*((a)->elems))); \ + if (_p == NULL) { \ + (a)->oom = true; \ + break; \ + } \ + (a)->cap = _cap; \ + (a)->elems = _p; \ + } \ + (a)->oom = false; \ + (a)->elems[(a)->size++] = k; \ + } while (0) /** - * @param a array - * @return current allocated capacity + * Deletes items from the array without deallocating underlying memory + * @param a array */ -#define sc_array_cap(a) (sc_array_meta((a))->cap) +#define sc_array_clear(a) \ + do { \ + (a)->cap = 0; \ + (a)->size = 0; \ + (a)->oom = false; \ + } while (0) /** - * @param a array - * @return current element count + * @param a array + * @return true if last add operation failed, false otherwise. */ -#define sc_array_size(a) (sc_array_meta((a))->size) +#define sc_array_oom(a) ((a)->oom) /** - * Deletes items from the array without deallocating underlying memory - * @param a array + * Get element at index i, if 'i' is out of range, result is undefined. + * + * @param a array + * @param i index + * @return element at index 'i' */ -#define sc_array_clear(a) (sc_array_meta((a))->size = 0) +#define sc_array_at(a, i) ((a)->elems[i]) /** - * @param a array - * @param elem element to be appended - * @return 'true' on success, 'false' on out of memory. + * @param a array + * @return element count */ -#define sc_array_add(a, elem) \ - sc_array_expand(&(a), sc_array_sizeof(*(a))) == true ? \ - (a)[sc_array_meta(a)->size++] = (elem), true : false +#define sc_array_size(a) ((a)->size) /** * @param a array @@ -102,61 +141,71 @@ bool sc_array_expand(void *a, size_t elem_size); */ #define sc_array_del(a, i) \ do { \ - assert((i) < sc_array_meta(a)->size); \ - const size_t cnt = sc_array_size(a) - (i) -1; \ - if (cnt > 0) { \ - memmove(&(a)[i], &(a)[(i) + 1], cnt * sizeof(*(a))); \ + assert((i) < (a)->size); \ + const size_t _cnt = (a)->size - (i) -1; \ + if (_cnt > 0) { \ + memmove(&((a)->elems[i]), &((a)->elems[(i) + 1]), \ + _cnt * sizeof(*((a)->elems))); \ } \ - sc_array_meta((a))->size--; \ + (a)->size--; \ } while (0) /** - * Deletes the element at index i, replaces last element with the deleted - * element unless deleted element is the last element. This is faster than - * moving elements but elements will no longer be in the 'add order' + * Deletes the element at index i, replaces last element with the deleted + * element unless deleted element is the last element. This is faster than + * moving elements but elements will no longer be in the 'add order' * - * arr[a,b,c,d,e,f] -> sc_array_del_unordered(vec, 2) - > arr[a,b,f,d,e] + * arr[a,b,c,d,e,f] -> sc_array_del_unordered(arr, 2) - > arr[a,b,f,d,e] * - * @param a array - * @param i index. If 'i' is out of the range, result is undefined. + * @param a array + * @param i index. If 'i' is out of the range, result is undefined. */ #define sc_array_del_unordered(a, i) \ do { \ - assert((i) < sc_array_meta(a)->size); \ - (a)[i] = (a)[(--sc_array_meta((a))->size)]; \ + assert((i) < (a)->size); \ + (a)->elems[i] = (a)->elems[(--(a)->size)]; \ } while (0) /** - * Deletes the last element. If current size is zero, result is undefined. - * @param a array + * Deletes the last element. If current size is zero, result is undefined. + * @param a array */ #define sc_array_del_last(a) \ do { \ - assert(sc_array_meta(a)->size != 0); \ - sc_array_meta(a)->size--; \ + assert((a)->size != 0); \ + (a)->size--; \ } while (0) /** - * Sorts the array using qsort() - * @param a array. - * @param cmp comparator, check qsort() documentation for details + * Sorts the array using qsort() + * @param a array + * @param cmp comparator, check qsort() documentation for details */ -#define sc_array_sort(a, cmp) \ - (qsort((a), sc_array_size((a)), sc_array_sizeof(*(a)), cmp)) +#define sc_array_sort(a, cmp) (qsort((a)->elems, (a)->size, *(a)->elems, cmp)) /** - * @param a array - * @param elem elem + * Returns last element. If array is empty, result is undefined. + * @param a array + */ +#define sc_array_last(a) (a)->elems[(a)->size - 1] + +/** + * @param a array + * @param elem elem */ #define sc_array_foreach(a, elem) \ - for (size_t _k = 1, _i = 0; _k && _i != sc_array_size(a); \ - _k = !_k, _i++) \ - for ((elem) = (a)[_i]; _k; _k = !_k) + for (size_t _k = 1, _i = 0; _k && _i != (a)->size; _k = !_k, _i++) \ + for ((elem) = (a)->elems[_i]; _k; _k = !_k) -/** - * Returns last element. If array is empty, result is undefined. - * @param a array - */ -#define sc_array_last(a) (a)[sc_array_size(a) - 1] +// (type, name) +sc_array_def(int, int); +sc_array_def(unsigned int, uint); +sc_array_def(long, long); +sc_array_def(unsigned long, ulong); +sc_array_def(uint32_t, 32); +sc_array_def(uint64_t, 64); +sc_array_def(double, double); +sc_array_def(const char *, str); +sc_array_def(void *, ptr); #endif diff --git a/queue/CMakeLists.txt b/queue/CMakeLists.txt index 8c51aa7..29838ed 100644 --- a/queue/CMakeLists.txt +++ b/queue/CMakeLists.txt @@ -7,9 +7,10 @@ set(CMAKE_C_EXTENSIONS OFF) add_library( sc_queue SHARED - sc_queue.c sc_queue.h) +set_target_properties(sc_queue PROPERTIES LINKER_LANGUAGE C) + target_include_directories(sc_queue PUBLIC ${CMAKE_CURRENT_LIST_DIR}) if (NOT CMAKE_C_COMPILER_ID MATCHES "MSVC") @@ -37,7 +38,7 @@ if (SC_BUILD_TEST) enable_testing() - add_executable(${PROJECT_NAME}_test queue_test.c sc_queue.c) + add_executable(${PROJECT_NAME}_test queue_test.c) target_compile_options(${PROJECT_NAME}_test PRIVATE -DSC_QUEUE_MAX=1400000ul) @@ -48,7 +49,7 @@ if (SC_BUILD_TEST) target_compile_options(${PROJECT_NAME}_test PRIVATE -fno-omit-frame-pointer) target_compile_options(${PROJECT_NAME}_test PRIVATE -DSC_HAVE_WRAP) target_compile_options(${PROJECT_NAME}_test PRIVATE -fno-builtin) - target_link_options(${PROJECT_NAME}_test PRIVATE -Wl,--wrap=realloc) + target_link_options(${PROJECT_NAME}_test PRIVATE -Wl,--wrap=calloc) endif () endif () diff --git a/queue/README.md b/queue/README.md index b976bef..13fcb76 100644 --- a/queue/README.md +++ b/queue/README.md @@ -2,9 +2,11 @@ ### Overview -- Type generic queue which grows when you add elements. +- Queue implementation which grows when you add elements. - Add/remove from head/tail is possible so it can be used as list, stack, queue, dequeue etc. +- It comes with predefined types, check out at the end of sc_queue.h, you can + add there (sc_queue_def) if you need more. ### Usage @@ -17,68 +19,27 @@ int main(int argc, char *argv[]) { - int *queue; - int elem; + int elem; + struct sc_queue_int queue; - sc_queue_create(queue, 0); + sc_queue_init(&queue); + + sc_queue_add_last(&queue, 2); + sc_queue_add_last(&queue, 3); + sc_queue_add_last(&queue, 4); + sc_queue_add_first(&queue, 1); - sc_queue_add_last(queue, 2); - sc_queue_add_last(queue, 3); - sc_queue_add_last(queue, 4); - sc_queue_add_first(queue, 1); + sc_queue_foreach (&queue, elem) { + printf("elem = [%d] \n", elem); + } - sc_queue_foreach (queue, elem) { - printf("elem = [%d] \n", elem); - } + elem = sc_queue_del_last(&queue); + printf("Last element was : [%d] \n", elem); - elem = sc_queue_del_last(queue); - printf("Last element was : [%d] \n", elem); + elem = sc_queue_del_first(&queue); + printf("First element was : [%d] \n", elem); - elem = sc_queue_del_first(queue); - printf("First element was : [%d] \n", elem); - - sc_queue_destroy(queue); - - return 0; -} -``` - -### Note - -Queue pointer is not stable, it may change if it expands the memory. If you -pass the queue to another function which can add items, do it by passing -reference of the queue pointer. - -```c -void some_function_to_add_elems(long **q) -{ - sc_queue_add_last(*q, 500); -} - -int main(int argc, char *argv[]) -{ - long *q; - - sc_queue_create(q, 0); - sc_queue_add_last(q, 300); - - some_function_to_add_elems(&q); - sc_array_destroy(q); -} -``` - -Most probably you will hold queue pointer in a struct anyway, so you have a -stable address of the pointer, "*numbers" might change but "numbers" is stable : - -```c -struct my_app { - int *numbers; -}; - -void func(struct my_app* app) -{ - sc_queue_add_last(app->numbers, 300); - sc_queue_add_last(app->numbers, 400); - sc_queue_add_last(app->numbers, 500); + sc_queue_term(&queue); + return 0; } ``` diff --git a/queue/queue_example.c b/queue/queue_example.c index fe43ca1..aab811d 100644 --- a/queue/queue_example.c +++ b/queue/queue_example.c @@ -2,29 +2,28 @@ #include -int main() +int main(int argc, char *argv[]) { - int *queue; int elem; + struct sc_queue_int queue; - sc_queue_create(queue, 0); + sc_queue_init(&queue); - sc_queue_add_last(queue, 2); - sc_queue_add_last(queue, 3); - sc_queue_add_last(queue, 4); - sc_queue_add_first(queue, 1); + sc_queue_add_last(&queue, 2); + sc_queue_add_last(&queue, 3); + sc_queue_add_last(&queue, 4); + sc_queue_add_first(&queue, 1); - sc_queue_foreach (queue, elem) { - printf("elem = [%d] \n", elem); - } + sc_queue_foreach (&queue, elem) { + printf("elem = [%d] \n", elem); + } - elem = sc_queue_del_last(queue); + elem = sc_queue_del_last(&queue); printf("Last element was : [%d] \n", elem); - elem = sc_queue_del_first(queue); + elem = sc_queue_del_first(&queue); printf("First element was : [%d] \n", elem); - sc_queue_destroy(queue); - + sc_queue_term(&queue); return 0; } diff --git a/queue/queue_test.c b/queue/queue_test.c index 56076ad..c890d35 100644 --- a/queue/queue_test.c +++ b/queue/queue_test.c @@ -5,62 +5,90 @@ #ifdef SC_HAVE_WRAP -bool fail_realloc = false; -void *__real_realloc(void *p, size_t size); -void *__wrap_realloc(void *p, size_t n) +bool fail_calloc = false; +void *__real_calloc(size_t m, size_t n); +void *__wrap_calloc(size_t m, size_t n) { - if (fail_realloc) { + if (fail_calloc) { return NULL; } - return __real_realloc(p, n); + return __real_calloc(m, n); } void fail_test(void) { - double *q; + struct sc_queue_int q; - fail_realloc = true; - assert(sc_queue_create(q, 1000) == false); - fail_realloc = false; - assert(sc_queue_create(q, 1000)); + fail_calloc = true; + sc_queue_init(&q); + assert(sc_queue_oom(&q) == true); + fail_calloc = false; + sc_queue_init(&q); + assert(sc_queue_oom(&q) == false); + fail_calloc = true; + for (int i = 0; i < 10; i++) { + sc_queue_add_last(&q, 3); + } - fail_realloc = true; - bool success = false; - for (int i = 0; i < 1024; i++) { - success = sc_queue_add_last(q, i); - if (!success) { + assert(sc_queue_oom(&q) == true); + fail_calloc = false; + sc_queue_add_last(&q, 3); + assert(sc_queue_oom(&q) == false); + sc_queue_term(&q); + + sc_queue_init(&q); + fail_calloc = true; + for (int i = 0; i < 10; i++) { + sc_queue_add_first(&q, 3); + } + assert(sc_queue_oom(&q) == true); + fail_calloc = false; + sc_queue_add_first(&q, 3); + assert(sc_queue_oom(&q) == false); + sc_queue_term(&q); + + sc_queue_init(&q); + + fail_calloc = true; + for (int i = 0; i < 8; i++) { + sc_queue_add_last(&q, i); + if (sc_queue_oom(&q)) { break; } } - assert(!success); - assert(sc_queue_size(q) == 1023); - fail_realloc = false; - assert(sc_queue_add_last(q, 1023) == true); - for (int i = 0; i < 1024; i++) { - assert(sc_queue_del_first(q) == i); - } - assert(sc_queue_size(q) == 0); - assert(sc_queue_cap(q) == 2048); + assert(sc_queue_oom(&q)); + assert(sc_queue_size(&q) == 7); + fail_calloc = false; + sc_queue_add_last(&q, 7); + assert(sc_queue_oom(&q) == false); - fail_realloc = false; + for (int i = 0; i < 8; i++) { + assert(sc_queue_del_first(&q) == i); + } + assert(sc_queue_size(&q) == 0); + + fail_calloc = false; size_t max = SC_QUEUE_MAX; - success = true; for (size_t i = 0; i < max + 500; i++) { - success = sc_queue_add_last(q, i); - if (!success) { + sc_queue_add_last(&q, i); + if (sc_queue_oom(&q)) { break; } } - assert(!success); - sc_queue_clear(q); - assert(sc_queue_size(q) == 0); - sc_queue_destroy(q); - assert(sc_queue_create(q, max + 100) == false); - fail_realloc = false; + assert(sc_queue_oom(&q)); + fail_calloc = false; + sc_queue_add_last(&q, 100); + assert(sc_queue_oom(&q)); + sc_queue_clear(&q); + assert(sc_queue_size(&q) == 0); + sc_queue_term(&q); + sc_queue_init(&q); + sc_queue_term(&q); + fail_calloc = false; } #else void fail_test(void) @@ -71,29 +99,27 @@ void fail_test(void) void example(void) { - int *queue; int elem; + struct sc_queue_int queue; - sc_queue_create(queue, 0); - sc_queue_destroy(queue); + sc_queue_init(&queue); - sc_queue_create(queue, 0); - sc_queue_add_last(queue, 2); - sc_queue_add_last(queue, 3); - sc_queue_add_last(queue, 4); - sc_queue_add_first(queue, 1); + sc_queue_add_last(&queue, 2); + sc_queue_add_last(&queue, 3); + sc_queue_add_last(&queue, 4); + sc_queue_add_first(&queue, 1); - sc_queue_foreach (queue, elem) { + sc_queue_foreach (&queue, elem) { printf("elem = [%d] \n", elem); } - elem = sc_queue_del_last(queue); + elem = sc_queue_del_last(&queue); printf("Last element was : [%d] \n", elem); - elem = sc_queue_del_first(queue); + elem = sc_queue_del_first(&queue); printf("First element was : [%d] \n", elem); - sc_queue_destroy(queue); + sc_queue_term(&queue); } void test1(void) @@ -101,81 +127,101 @@ void test1(void) int count = 0; int t; int i = 0; - int *p; + struct sc_queue_int p; - p = NULL; - sc_queue_destroy(p); + sc_queue_init(&p); + sc_queue_term(&p); - assert(sc_queue_create(p, 2) == true); - sc_queue_destroy(p); - sc_queue_destroy(p); + sc_queue_init(&p); + sc_queue_term(&p); + sc_queue_term(&p); - sc_queue_add_first(p, 1); - sc_queue_add_first(p, 2); - sc_queue_add_first(p, 3); - assert(sc_queue_del_first(p) == 3); - assert(sc_queue_del_first(p) == 2); - assert(sc_queue_del_first(p) == 1); - sc_queue_destroy(p); + sc_queue_init(&p); + sc_queue_add_first(&p, 1); + sc_queue_add_first(&p, 2); + sc_queue_add_first(&p, 3); + assert(sc_queue_del_first(&p) == 3); + assert(sc_queue_del_first(&p) == 2); + assert(sc_queue_del_first(&p) == 1); + sc_queue_term(&p); - assert(sc_queue_create(p, 0) == true); + sc_queue_init(&p); + assert(sc_queue_oom(&p) == false); - sc_queue_foreach (p, t) { + sc_queue_foreach (&p, t) { (void) t; count++; } assert(count == 0); - assert(sc_queue_empty(p) == true); - assert(sc_queue_size(p) == 0); + assert(sc_queue_empty(&p) == true); + assert(sc_queue_size(&p) == 0); - assert(sc_queue_add_first(p, 2) == true); - assert(sc_queue_add_first(p, 3) == true); - assert(sc_queue_add_first(p, 4) == true); - assert(sc_queue_add_first(p, 5) == true); - assert(sc_queue_add_first(p, 6) == true); - assert(sc_queue_add_last(p, 1) == true); - assert(sc_queue_add_last(p, 0) == true); + sc_queue_add_first(&p, 2); + sc_queue_add_first(&p, 3); + sc_queue_add_first(&p, 4); + sc_queue_add_first(&p, 5); + sc_queue_add_first(&p, 6); + sc_queue_add_last(&p, 1); + sc_queue_add_last(&p, 0); - assert(sc_queue_empty(p) == false); + assert(sc_queue_empty(&p) == false); i = 6; - sc_queue_foreach (p, t) { + sc_queue_foreach (&p, t) { assert(t == i--); count += t; } assert(count == 6 * 7 / 2); - assert(sc_queue_size(p) == 7); + assert(sc_queue_size(&p) == 7); - assert(sc_queue_peek_first(p) == 6); - assert(sc_queue_size(p) == 7); - assert(sc_queue_peek_last(p) == 0); - assert(sc_queue_size(p) == 7); + assert(sc_queue_peek_first(&p) == 6); + assert(sc_queue_size(&p) == 7); + assert(sc_queue_peek_last(&p) == 0); + assert(sc_queue_size(&p) == 7); - t = sc_queue_del_first(p); + t = sc_queue_del_first(&p); assert(t == 6); - assert(sc_queue_size(p) == 6); + assert(sc_queue_size(&p) == 6); - t = sc_queue_del_last(p); + t = sc_queue_del_last(&p); assert(t == 0); - assert(sc_queue_size(p) == 5); + assert(sc_queue_size(&p) == 5); - sc_queue_clear(p); - assert(sc_queue_size(p) == 0); - assert(sc_queue_cap(p) == 8); - assert(sc_queue_empty(p) == true); + sc_queue_clear(&p); + assert(sc_queue_size(&p) == 0); + assert(sc_queue_empty(&p) == true); - sc_queue_destroy(p); - sc_queue_destroy(p); + sc_queue_term(&p); + sc_queue_term(&p); - assert(sc_queue_create(p, 0) == true); - sc_queue_add_first(p, 100); - sc_queue_add_first(p, 200); - sc_queue_add_first(p, 300); - sc_queue_add_first(p, 400); - sc_queue_add_first(p, 500); - assert(sc_queue_at(p, 0) == 500); - assert(sc_queue_at(p, 4) == 100); - sc_queue_destroy(p); + sc_queue_init(&p); + sc_queue_add_first(&p, 100); + sc_queue_add_first(&p, 200); + sc_queue_add_first(&p, 300); + sc_queue_add_first(&p, 400); + sc_queue_add_first(&p, 500); + assert(sc_queue_at(&p, 0) == 500); + assert(sc_queue_at(&p, 4) == 100); + sc_queue_term(&p); + + sc_queue_init(&p); + + sc_queue_foreach (&p, t) { + assert(true); + } + + sc_queue_add_last(&p, 2); + sc_queue_add_first(&p, 1); + sc_queue_add_first(&p, 0); + sc_queue_add_last(&p, 3); + + i = 0; + sc_queue_foreach (&p, t) { + assert(sc_queue_at(&p, i) == i); + i++; + } + + sc_queue_term(&p); } int main() diff --git a/queue/sc_queue.c b/queue/sc_queue.c deleted file mode 100644 index d4bd8f2..0000000 --- a/queue/sc_queue.c +++ /dev/null @@ -1,158 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2021 Ozan Tezcan - * - * 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 "sc_queue.h" - -#ifndef SC_QUEUE_MAX -#define SC_QUEUE_MAX ((SIZE_MAX - sizeof(struct sc_queue)) / 2ul) -#endif - -static const struct sc_queue sc_empty = {.cap = 1, .first = 0, .last = 0}; - -static void *queue_alloc(void *prev, size_t elem_size, size_t *cap) -{ - size_t alloc, v = *cap; - - if (*cap > SC_QUEUE_MAX) { - return NULL; - } - - // Find next power of two. - v = v < 4 ? 4 : v; - v--; - for (size_t i = 1; i < sizeof(v) * 8; i *= 2) { - v |= v >> i; - } - v++; - - *cap = v; - alloc = sizeof(struct sc_queue) + (elem_size * v); - - return sc_queue_realloc(prev, alloc); -} - -bool sc_queue_init(void *q, size_t elem_size, size_t cap) -{ - size_t p = cap; - void **ptr = q; - struct sc_queue *meta; - - if (cap == 0) { - *ptr = (void *) sc_empty.elems; - return true; - } - - meta = queue_alloc(NULL, elem_size, &p); - if (meta == NULL) { - *ptr = NULL; - return false; - } - - meta->cap = p; - meta->first = 0; - meta->last = 0; - *ptr = meta->elems; - - return true; -} - -void sc_queue_term(void *q) -{ - struct sc_queue *meta; - void **ptr = q; - - if (*ptr == NULL) { - return; - } - - meta = sc_queue_meta(*ptr); - - if (meta != &sc_empty) { - sc_queue_free(meta); - } - - *ptr = (void *) sc_empty.elems; -} - -bool sc_queue_expand(void *q, size_t elem_size) -{ - void **ptr = q; - struct sc_queue *tmp; - struct sc_queue *meta = sc_queue_meta(*ptr); - size_t cap, cnt, sz; - size_t pos = (meta->last + 1) & (meta->cap - 1); - uint8_t *e; - - if (pos == meta->first) { - if (meta == &sc_empty) { - return sc_queue_init(ptr, elem_size, 4); - } - - cap = meta->cap * 2; - tmp = queue_alloc(meta, elem_size, &cap); - if (tmp == NULL) { - return false; - } - - /** - * Move items to make empty slots at the end. - * - * Doing a lot here to work with realloc, normally - * 1 - allocating new memory - * 2 - copy items - * 3 - free old memory - * - * would be easier. But I use this code with a specific - * allocator, it's much more efficient with realloc, so leaving - * this one as it is for now. - * - * e.g : - * last first - * | | - * Step 0 : | 2 | 3 | - | 1 | // tmp->cap : 4 - * Step 1 : | 2 | 3 | - | 1 | - | - | - | - | // realloc - * Step 2 : | 2 | 3 | - | 1 | 1 | - | - | - | // memcpy - * Step 3 : | 2 | 2 | 3 | 1 | 1 | - | - | - | // memmove - * Step 4 : | 1 | 2 | 3 | 1 | 1 | - | - | - | // memcpy - * Step 5 : | 1 | 2 | 3 | - | - | - | - | - | // tmp->last = - * cap - 1; | | first last - * - */ - - e = tmp->elems; - cnt = tmp->cap - tmp->first; - sz = elem_size; - - memcpy(e + (sz * tmp->cap), e + (sz * tmp->first), cnt * sz); - memmove(e + (cnt * sz), e, tmp->first * sz); - memcpy(e, e + (sz * tmp->cap), cnt * sz); - - tmp->last = tmp->cap - 1; - tmp->first = 0; - tmp->cap = cap; - *ptr = tmp->elems; - } - - return true; -} diff --git a/queue/sc_queue.h b/queue/sc_queue.h index d41850f..138b3e7 100644 --- a/queue/sc_queue.h +++ b/queue/sc_queue.h @@ -37,93 +37,97 @@ #ifdef SC_HAVE_CONFIG_H #include "config.h" #else -#define sc_queue_realloc realloc -#define sc_queue_free free +#define sc_queue_calloc calloc +#define sc_queue_free free #endif -// Internals -struct sc_queue { - size_t cap; - size_t first; - size_t last; - unsigned char elems[]; -}; +#ifndef SC_QUEUE_MAX +#define SC_QUEUE_MAX (SIZE_MAX) +#endif -#define sc_queue_meta(q) \ - ((struct sc_queue *) ((char *) (q) -offsetof(struct sc_queue, elems))) +#define sc_queue_def(T, name) \ + struct sc_queue_##name { \ + bool oom; \ + size_t cap; \ + size_t first; \ + size_t last; \ + /* NOLINTNEXTLINE */ \ + T *elems; \ + } -static inline size_t sc_queue_inc_first(void *q) -{ - struct sc_queue *m = sc_queue_meta(q); - size_t tmp = m->first; - - m->first = (m->first + 1) & (m->cap - 1); - return tmp; -} - -static inline size_t sc_queue_inc_last(void *q) -{ - struct sc_queue *m = sc_queue_meta(q); - size_t tmp = m->last; - - m->last = (m->last + 1) & (m->cap - 1); - return tmp; -} - -static inline size_t sc_queue_dec_first(void *q) -{ - struct sc_queue *m = sc_queue_meta(q); - - m->first = (m->first - 1) & (m->cap - 1); - return m->first; -} - -static inline size_t sc_queue_dec_last(void *q) -{ - struct sc_queue *m = sc_queue_meta(q); - - m->last = (m->last - 1) & (m->cap - 1); - return m->last; -} - -bool sc_queue_init(void *q, size_t elem_size, size_t cap); -void sc_queue_term(void *q); -bool sc_queue_expand(void *q, size_t elem_size); -#define sc_queue_sizeof(a) (sizeof(a)) // NOLINT +#define sc_queue_expand(q) \ + do { \ + size_t _cap, _len, _off; \ + size_t _pos = ((q)->last + 1) & ((q)->cap - 1); \ + void *_dst, *_src; \ + \ + if (_pos == (q)->first) { \ + if ((q)->cap > SC_QUEUE_MAX / 2ul) { \ + (q)->oom = true; \ + break; \ + } \ + _cap = (q)->cap * 2; \ + _dst = sc_queue_calloc(_cap, sizeof(*((q)->elems))); \ + if (_dst == NULL) { \ + (q)->oom = true; \ + break; \ + } \ + _len = ((q)->cap - (q)->first) * sizeof(*(q)->elems); \ + _off = ((q)->first * sizeof(*((q)->elems))); \ + _src = ((char *) (q)->elems) + _off; \ + \ + memcpy(_dst, _src, _len); \ + memcpy(((char *) _dst) + _len, (q)->elems, _off); \ + (q)->oom = false; \ + (q)->last = (q)->cap - 1; \ + (q)->first = 0; \ + (q)->cap = _cap; \ + sc_queue_free((q)->elems); \ + (q)->elems = _dst; \ + } \ + } while (0) /** - * @param q queue - * @param count initial capacity, '0' is accepted. - * @return 'true' on success, 'false' on out of memory. - */ -#define sc_queue_create(q, count) \ - sc_queue_init(&(q), sc_queue_sizeof(*(q)), count) - -/** - * Destroy queue + * Init queue. Call sc_queue_oom(q) to see if memory allocation succeeded. * @param q queue */ -#define sc_queue_destroy(q) sc_queue_term((&(q))) +#define sc_queue_init(q) \ + do { \ + (q)->oom = false; \ + (q)->cap = 8; \ + (q)->first = 0; \ + (q)->last = 0; \ + (q)->elems = sc_queue_calloc(1, sizeof(*(q)->elems) * 8); \ + if ((q)->elems == NULL) { \ + (q)->oom = true; \ + } \ + } while (0) /** + * Term queue * @param q queue - * @return current capacity */ -#define sc_queue_cap(q) (sc_queue_meta((q))->cap) +#define sc_queue_term(q) \ + do { \ + sc_queue_free((q)->elems); \ + (q)->elems = NULL; \ + (q)->cap = 0; \ + (q)->first = 0; \ + (q)->last = 0; \ + (q)->oom = false; \ + } while (0) + +/** + * @param q queue + * @return true if last add operation failed, false otherwise. + */ +#define sc_queue_oom(q) ((q)->oom) /** * @param q queue * @return element count */ -#define sc_queue_size(q) \ - ((sc_queue_meta(q)->last - sc_queue_meta(q)->first) & \ - (sc_queue_meta(q)->cap - 1)) - -/** - * @param q queue - * @return true if queue is empty - */ -#define sc_queue_empty(q) ((sc_queue_meta(q)->last == sc_queue_meta(q)->first)) +#define sc_queue_size(q) (((q)->last - (q)->first) & ((q)->cap - 1)) /** * Clear the queue without deallocating underlying memory. @@ -131,27 +135,36 @@ bool sc_queue_expand(void *q, size_t elem_size); */ #define sc_queue_clear(q) \ do { \ - sc_queue_meta(q)->first = 0; \ - sc_queue_meta(q)->last = 0; \ + (q)->first = 0; \ + (q)->last = 0; \ + (q)->oom = false; \ } while (0) +/** + * @param q queue + * @return true if queue is empty + */ +#define sc_queue_empty(q) (((q)->last == (q)->first)) + /** * @param q queue * @return index of the first element. If queue is empty, result is undefined. */ -#define sc_queue_first(q) (sc_queue_meta(q)->first) +#define sc_queue_first(q) ((q)->first) /** * @param q queue * @return index of the last element. If queue is empty, result is undefined. */ -#define sc_queue_last(q) (sc_queue_meta(q)->last) +#define sc_queue_last(q) ((q)->last) /** - * @return index of the next element after i, if i is the last element, + * @param q queue + * @param i index + * @return index of the next element after i, if i is the last element, * result is undefined. */ -#define sc_queue_next(q, i) (((i) + 1) & (sc_queue_meta(q)->cap - 1)) +#define sc_queue_next(q, i) (((i) + 1) & ((q)->cap - 1)) /** * Returns element at index 'i', so regular loops are possible : @@ -163,59 +176,83 @@ bool sc_queue_expand(void *q, size_t elem_size); * @param q queue * @return element at index i */ -#define sc_queue_at(q, i) \ - (q)[((sc_queue_meta(q)->first) + (i)) & (sc_queue_cap(q) - 1)] +#define sc_queue_at(q, i) (q)->elems[(((q)->first) + (i)) & ((q)->cap - 1)] /** * @param q queue * @return peek first element, if queue is empty, result is undefined */ -#define sc_queue_peek_first(q) ((q)[sc_queue_meta(q)->first]) +#define sc_queue_peek_first(q) ((q)->elems[(q)->first]) /** * @param q queue * @return peek last element, if queue is empty, result is undefined */ -#define sc_queue_peek_last(q) \ - (q)[(sc_queue_meta(q)->last - 1) & (sc_queue_meta(q)->cap - 1)] +#define sc_queue_peek_last(q) (q)->elems[((q)->last - 1) & ((q)->cap - 1)] /** + * Call sc_queue_oom(q) after this function to check out of memory condition. + * * @param q queue * @param elem elem to be added at the end of the list - * @return 'true' on success, 'false' on out of memory. */ #define sc_queue_add_last(q, elem) \ - sc_queue_expand(&(q), sc_queue_sizeof(*(q))) == true ? \ - (q)[sc_queue_inc_last((q))] = (elem), true : false + do { \ + sc_queue_expand(q); \ + if ((q)->oom) { \ + break; \ + } \ + (q)->oom = false; \ + (q)->elems[(q)->last] = elem; \ + (q)->last = ((q)->last + 1) & ((q)->cap - 1); \ + } while (0) /** * @param q queue * @return delete the last element from the queue and return its value. * If queue is empty, result is undefined. */ -#define sc_queue_del_last(q) ((q)[sc_queue_dec_last((q))]) +#define sc_queue_del_last(q) \ + ((q)->elems[((q)->last = ((q)->last - 1) & ((q)->cap - 1))]) /** + * Call sc_queue_oom(q) after this function to check out of memory condition. + * * @param q queue. * @param elem elem to be added at the head of the list. - * @return 'true' on success, 'false' on out of memory. */ #define sc_queue_add_first(q, elem) \ - sc_queue_expand(&(q), sc_queue_sizeof(*(q))) == true ? \ - (q)[sc_queue_dec_first((q))] = (elem), true : false + do { \ + sc_queue_expand(q); \ + if ((q)->oom) { \ + break; \ + } \ + (q)->oom = false; \ + (q)->first = ((q)->first - 1) & ((q)->cap - 1); \ + (q)->elems[(q)->first] = elem; \ + } while (0) + +static inline size_t sc_queue_inc_first(size_t* first, size_t cap) +{ + size_t tmp = *first; + + *first = (*first + 1) & (cap - 1); + return tmp; +} /** * @param q queue * @return delete the first element from the queue and return its value. * If queue is empty, result is undefined. */ -#define sc_queue_del_first(q) (q)[sc_queue_inc_first((q))] +#define sc_queue_del_first(q) \ + (q)->elems[sc_queue_inc_first(&(q)->first, (q)->cap)] /** * For each loop, * * int *queue; - * sc_queue_create(queue, 4); + * sc_queue_create(queue, 4);" * * int elem; * sc_queue_foreach(queue, elem) { @@ -226,6 +263,17 @@ bool sc_queue_expand(void *q, size_t elem_size); for (size_t _k = 1, _i = sc_queue_first(q); \ _k && _i != sc_queue_last(q); \ _k = !_k, _i = sc_queue_next(q, _i)) \ - for ((elem) = (q)[_i]; _k; _k = !_k) + for ((elem) = (q)->elems[_i]; _k; _k = !_k) + +// (type, name) +sc_queue_def(int, int); +sc_queue_def(unsigned int, uint); +sc_queue_def(long, long); +sc_queue_def(unsigned long, ulong); +sc_queue_def(uint32_t, 32); +sc_queue_def(uint64_t, 64); +sc_queue_def(double, double); +sc_queue_def(const char *, str); +sc_queue_def(void *, ptr); #endif