aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLion Kortlepel <[email protected]>2026-02-01 16:22:14 +0000
committerLion Kortlepel <[email protected]>2026-02-01 16:22:14 +0000
commitf3b0599ff94a5bf5e13dba78281b0a3c7078c6b2 (patch)
tree26932d75abe3285331d5f33d9a68307210116b94
parent48302d5ce9bbf54610a41418449be3879d218d5a (diff)
downloadvec-f3b0599ff94a5bf5e13dba78281b0a3c7078c6b2.tar.zst
vec-f3b0599ff94a5bf5e13dba78281b0a3c7078c6b2.zip
feat!: breaking rename _clear to _freev2.0
this simply makes more sense, as _clear does not keep the capacity which would be expected.
-rw-r--r--README.md2
-rw-r--r--examples/simple.c4
-rw-r--r--ls_vec.h40
-rw-r--r--tests/ls_test.h50
-rw-r--r--tests/tests.c67
5 files changed, 128 insertions, 35 deletions
diff --git a/README.md b/README.md
index 03d98a3..803fe4c 100644
--- a/README.md
+++ b/README.md
@@ -34,7 +34,7 @@ Minimal, single-header dynamic array (vector) for C.
}
// Access elements via vec.data[i]
// Check size via vec.size
- int_vector_clear(&vec);
+ int_vector_free(&vec);
```
See [`ls_vec.h`](ls_vec.h) for detailed documentation and usage patterns.
diff --git a/examples/simple.c b/examples/simple.c
index 74a8750..aa9c656 100644
--- a/examples/simple.c
+++ b/examples/simple.c
@@ -3,9 +3,11 @@
LS_VEC_INLINE(int, int_vec)
int main(void) {
+ int v;
int_vec vec;
int_vec_init(&vec);
int_vec_push(&vec, 12);
- int_vec_clear(&vec);
+ int_vec_pop(&vec, &v);
+ int_vec_free(&vec);
return 0;
}
diff --git a/ls_vec.h b/ls_vec.h
index 8a72315..9fdec5c 100644
--- a/ls_vec.h
+++ b/ls_vec.h
@@ -1,6 +1,6 @@
/* Lion's Standard (LS) type-safe ANSI C vector.
*
- * Version: 1.0
+ * Version: 2.0
* Website: https://libls.org
* Repo: https://github.com/libls/vec
* SPDX-License-Identifier: MIT
@@ -41,7 +41,7 @@
* int_vector_init(&vec);
* int_vector_push(&vec, 42);
* // use vec.data, vec.size, etc.
- * int_vector_clear(&vec);
+ * int_vector_free(&vec);
*
* Alternative example with decl and impl split:
*
@@ -58,7 +58,7 @@
* // handle allocation failure
* }
* // access elements via vec.data[i]
- * int_vector_clear(&vec);
+ * int_vector_free(&vec);
*
* You can configure a custom memory allocator by defining the macros LS_REALLOC
* and LS_FREE globally. These are the only allocation functions required, and
@@ -115,7 +115,7 @@
T* data; \
} name; \
void name##_init(name* vec); \
- void name##_clear(name* vec); \
+ void name##_free(name* vec); \
int name##_reserve(name* vec, size_t count); \
int name##_push(name* vec, T value);
#define LS_VEC_IMPL(T, name) _ls_VEC_IMPL_DETAIL(T, name, )
@@ -130,7 +130,7 @@
#define _ls_VEC_IMPL_DETAIL(T, name, specifier) \
specifier void name##_init(name* vec) { memset(vec, 0, sizeof(*vec)); } \
- specifier void name##_clear(name* vec) { \
+ specifier void name##_free(name* vec) { \
if (vec->data) { \
LS_FREE(vec->data); \
vec->data = NULL; \
@@ -141,19 +141,20 @@
specifier int name##_reserve(name* vec, size_t count) { \
if (vec->capacity < count) { \
T* new_data; \
- size_t total; \
- if (count == 0) { \
- vec->capacity = 5; \
- } else { \
- size_t new_cap = (size_t)((float)vec->capacity * 1.6f + 1.0f); \
- vec->capacity = new_cap > count ? new_cap : count; \
+ size_t max_items = SIZE_MAX / sizeof(T); \
+ size_t new_cap = vec->capacity + vec->capacity / 2 + 8; \
+ if (new_cap < count) { \
+ new_cap = count; \
} \
- total = vec->capacity * sizeof(T); \
- if (vec->capacity != 0 && total / vec->capacity != sizeof(T)) \
- return 0; /* integer overflow */ \
- new_data = (T*)LS_REALLOC(vec->data, total); \
- if (!new_data) \
+ /* overflow check */ \
+ if (new_cap > max_items) { \
return 0; \
+ } \
+ new_data = (T*)LS_REALLOC(vec->data, new_cap * sizeof(T)); \
+ if (new_data == NULL) { \
+ return 0; \
+ } \
+ vec->capacity = new_cap; \
vec->data = new_data; \
} \
return 1; \
@@ -164,4 +165,11 @@
} \
vec->data[vec->size++] = value; \
return 1; \
+ } \
+ specifier int name##_pop(name* vec, T* out_value) { \
+ if (vec->size == 0) { \
+ return 0; \
+ } \
+ *out_value = vec->data[--vec->size]; \
+ return 1; \
}
diff --git a/tests/ls_test.h b/tests/ls_test.h
index 5dda4aa..2077477 100644
--- a/tests/ls_test.h
+++ b/tests/ls_test.h
@@ -1,6 +1,6 @@
/* Lion's Standard (LS) test harness.
*
- * Version: 1.1
+ * Version: 1.3
* Website: https://libls.org
* Repo: https://github.com/libls/test
* SPDX-License-Identifier: MIT
@@ -20,6 +20,8 @@
* you use asserts other than `ASSERT` (e.g. ASSERT_EQ), and the constructor
* attribute __attribute__((destructor)) for automatic test registration.
*
+ * Supports string comparisons using ASSERT_STR_EQ and ASSERT_STR_NEQ.
+ *
* ==== 2. HOW TO USE ====
*
* 1. Copy this file into your project and include it:
@@ -96,6 +98,8 @@
_func += 6; \
fprintf(stderr, "%s: FAILED: %s (%s:%d)\n", _func, #cond, \
__FILE__, __LINE__); \
+ ++lst_fail; \
+ return 1; \
} \
} while (0)
@@ -115,7 +119,38 @@
++lst_fail; \
return 1; \
} \
- ++lst_ok; \
+ } while (0)
+
+#define ASSERT_STR_EQ(a, b) \
+ do { \
+ __typeof__(a) _a = (a); \
+ __typeof__(b) _b = (b); \
+ if (strcmp(_a, _b) != 0) { \
+ const char* _func = __func__; \
+ if (strncmp(_func, "lst_t_", 6) == 0) \
+ _func += 6; \
+ fprintf(stderr, \
+ "%s: FAILED: %s == %s (actual: \"%s\" != \"%s\") (%s:%d)\n", \
+ _func, #a, #b, _a, _b, __FILE__, __LINE__); \
+ ++lst_fail; \
+ return 1; \
+ } \
+ } while (0)
+
+#define ASSERT_STR_NEQ(a, b) \
+ do { \
+ __typeof__(a) _a = (a); \
+ __typeof__(b) _b = (b); \
+ if (strcmp(_a, _b) == 0) { \
+ const char* _func = __func__; \
+ if (strncmp(_func, "lst_t_", 6) == 0) \
+ _func += 6; \
+ fprintf(stderr, \
+ "%s: FAILED: %s != %s (actual: \"%s\" == \"%s\") (%s:%d)\n", \
+ _func, #a, #b, _a, _b, __FILE__, __LINE__); \
+ ++lst_fail; \
+ return 1; \
+ } \
} while (0)
#define ASSERT_NEQ(a, b, fmt) \
@@ -132,7 +167,6 @@
++lst_fail; \
return 1; \
} \
- ++lst_ok; \
} while (0)
#define ASSERT_LT(a, b, fmt) \
@@ -149,7 +183,6 @@
++lst_fail; \
return 1; \
} \
- ++lst_ok; \
} while (0)
#define ASSERT_LE(a, b, fmt) \
@@ -166,7 +199,6 @@
++lst_fail; \
return 1; \
} \
- ++lst_ok; \
} while (0)
#define ASSERT_GT(a, b, fmt) \
@@ -183,7 +215,6 @@
++lst_fail; \
return 1; \
} \
- ++lst_ok; \
} while (0)
#define ASSERT_GE(a, b, fmt) \
@@ -200,7 +231,6 @@
++lst_fail; \
return 1; \
} \
- ++lst_ok; \
} while (0)
#define LS_CAT2(a, b) a##b
#define LS_CAT(a, b) LS_CAT2(a, b)
@@ -217,7 +247,6 @@ extern lst_func* lst_funcs;
extern int lst_n;
extern int lst_cap;
extern int lst_fail;
-extern int lst_ok;
void lst_reg(lst_func f);
@@ -229,7 +258,6 @@ lst_func* lst_funcs;
int lst_n;
int lst_cap;
int lst_fail = 0;
-int lst_ok = 0;
void lst_reg(lst_func f) {
if (lst_n == lst_cap) {
@@ -276,8 +304,8 @@ static int ls_test_main(int argc, char** argv) {
}
end:
- fprintf(stderr, "%d succeeded, %d failed, %d total\n", lst_ok, lst_fail,
- lst_ok + lst_fail);
+ fprintf(stderr, "%d succeeded, %d failed, %d total\n", lst_n - lst_fail,
+ lst_fail, lst_n);
free(lst_funcs);
if (lst_fail > 0) {
diff --git a/tests/tests.c b/tests/tests.c
index b679a3e..6d074a6 100644
--- a/tests/tests.c
+++ b/tests/tests.c
@@ -1,22 +1,77 @@
#define LS_TEST_IMPLEMENTATION
-#include "ls_test.h"
#include "../ls_vec.h"
+#include "ls_test.h"
LS_VEC_INLINE(int, vec_int)
-TEST_CASE(vec_init_clear) {
+TEST_CASE(vec_init_free) {
vec_int v;
vec_int_init(&v);
ASSERT_EQ(v.size, 0, "%zu");
ASSERT_EQ(v.capacity, 0, "%zu");
ASSERT_EQ(v.data, NULL, "%p");
- vec_int_clear(&v);
+ vec_int_free(&v);
ASSERT_EQ(v.size, 0, "%zu");
ASSERT_EQ(v.capacity, 0, "%zu");
ASSERT_EQ(v.data, NULL, "%p");
return 0;
}
+TEST_CASE(vec_push_pop_1000) {
+ int i;
+ vec_int v;
+ vec_int_init(&v);
+
+ // Push 1000 items
+ for (i = 0; i < 1000; ++i) {
+ ASSERT_EQ(vec_int_push(&v, i), 1, "%d");
+ }
+ ASSERT_EQ(v.size, 1000, "%zu");
+
+ // Pop 1000 items and check values
+ for (i = 999; i >= 0; --i) {
+ int val;
+ ASSERT_EQ(vec_int_pop(&v, &val), 1, "%d");
+ ASSERT_EQ(val, i, "%d");
+ }
+ ASSERT_EQ(v.size, 0, "%zu");
+
+ vec_int_free(&v);
+ return 0;
+}
+
+TEST_CASE(vec_reserve) {
+ vec_int v;
+ vec_int_init(&v);
+ ASSERT_EQ(vec_int_reserve(&v, 10), 1, "%d");
+ ASSERT_EQ(v.capacity, 10, "%zu");
+ vec_int_free(&v);
+ return 0;
+}
+
+TEST_CASE(vec_free) {
+ vec_int v;
+ int i = 0;
+ vec_int_init(&v);
+ for (i = 0; i < 100; ++i) {
+ vec_int_push(&v, i);
+ }
+ vec_int_free(&v);
+ ASSERT_EQ(v.size, 0, "%zu");
+ ASSERT_EQ(v.capacity, 0, "%zu");
+ ASSERT_EQ(v.data, NULL, "%p");
+ return 0;
+}
+
+TEST_CASE(vec_reserve_large) {
+ vec_int v;
+ vec_int_init(&v);
+ ASSERT_EQ(vec_int_reserve(&v, 10000), 1, "%d");
+ ASSERT_EQ(v.capacity, 10000, "%zu");
+ vec_int_free(&v);
+ return 0;
+}
+
TEST_CASE(vec_push_and_access) {
int i;
vec_int v;
@@ -26,7 +81,7 @@ TEST_CASE(vec_push_and_access) {
ASSERT_EQ(v.size, 10, "%zu");
for (i = 0; i < 10; ++i)
ASSERT_EQ(v.data[i], i, "%d");
- vec_int_clear(&v);
+ vec_int_free(&v);
return 0;
}
@@ -35,7 +90,7 @@ TEST_CASE(vec_reserve_grow) {
vec_int_init(&v);
ASSERT_EQ(vec_int_reserve(&v, 20), 1, "%d");
ASSERT_GE(v.capacity, 20, "%zu");
- vec_int_clear(&v);
+ vec_int_free(&v);
return 0;
}
@@ -46,7 +101,7 @@ TEST_CASE(vec_push_overflow) {
v.size = v.capacity;
int ret = vec_int_push(&v, 123);
ASSERT_EQ(ret, 0, "%d");
- vec_int_clear(&v);
+ vec_int_free(&v);
return 0;
}