queue and array v2

This commit is contained in:
tezc 2021-04-26 20:33:38 +03:00
parent 3c8e0f9f70
commit cce603126f
12 changed files with 667 additions and 843 deletions

View File

@ -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)

View File

@ -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);
}
```

View File

@ -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()

View File

@ -4,34 +4,42 @@
#include <stdio.h>
#include <stdlib.h>
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();

View File

@ -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;
}

View File

@ -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

View File

@ -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 ()

View File

@ -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;
}
```

View File

@ -2,29 +2,28 @@
#include <stdio.h>
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;
}

View File

@ -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()

View File

@ -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;
}

View File

@ -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