Safer push_back and faster push_back (I went from 0(n^2) to 0(1))

This commit is contained in:
Andrew Haynes
2026-04-17 10:48:42 -04:00
parent 03d30f3d0b
commit 81cd5c00e1
12 changed files with 1165 additions and 50 deletions
Vendored
BIN
View File
Binary file not shown.
+111 -50
View File
@@ -3,7 +3,7 @@
* dmm.dev@icloud.com
* No LICENSE
* Vector Library For C
*/
*/
/*
* Types Wanted:
@@ -13,7 +13,17 @@
* String
*/
// Absolutely no AI is permitted when writing code.
/* Absolutely no AI is permitted when writing code.
* AI is and can be used to generate tests for code; however, the test results
* and the AI can not give code as to how to fix the source aside from psuedo
* code that has no clear relation to the source aside from a recreation of the
* problem in a different way entirely
*
* Any code comments below are just used for my purposes with short term memory.
* It also serves as a way to remember what needs to be changed to be readable
* and not need comments in the first place.
*/
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
@@ -23,17 +33,35 @@ typedef struct
{
char* arr;
size_t size;
size_t capacity;
} Vec8_t;
/* Takes in a temporary cpy of a vec
* - Can be nullptr to not have size incrementation and have a size of 0 (Create
* an empty vector)
*
* - Can be a vec to have size incrementation and size of base vec + 1 (Create a
* vector for modifying an old vector)
*
* */
Vec8_t
create(size_t init_size)
create(const Vec8_t* input)
{
Vec8_t vec = { .arr = nullptr, .size = 0 };
if (init_size > 0)
Vec8_t vec = { .arr = nullptr, .size = 0, .capacity = 4 };
if (input == nullptr)
{
vec.arr = malloc(init_size * sizeof(char));
vec.size = init_size;
vec.arr = calloc(vec.capacity, sizeof(char));
return vec;
}
if (input->size > 0)
{
vec.size = input->size + 1;
}
if (input->capacity >= vec.capacity)
{
vec.capacity = 2 * input->capacity;
}
vec.arr = calloc(vec.capacity, sizeof(char));
return vec;
}
@@ -47,11 +75,22 @@ delete(Vec8_t* vec)
vec->arr = nullptr;
}
char
at(Vec8_t* vec, int idx)
at(const Vec8_t* vec, const int idx)
{
if (vec->arr != nullptr && vec->size >= idx)
if (vec == nullptr)
{
return -2;
}
if (vec->arr == nullptr)
{
return -3;
}
if (vec->size <= idx)
{
return -4;
}
if (vec != nullptr && vec->arr != nullptr && vec->size > idx)
{
return vec->arr[idx];
}
@@ -59,27 +98,32 @@ at(Vec8_t* vec, int idx)
}
void
print_vec(Vec8_t* vec) {
for(int i = 0; i < vec->size; i++) {
printf("%c ", at(vec, i));
}
printf("\n");
print_vec(const Vec8_t* vec)
{
for (int i = 0; i < vec->size; i++)
{
if (vec->arr[i])
{
printf("%c ", at(vec, i));
}
}
printf("\n");
}
int
begin(Vec8_t* vec)
begin(const Vec8_t* vec)
{
if (vec->arr != nullptr && vec->size > 0)
if (vec != nullptr && vec->arr != nullptr && vec->size > 0)
{
return 1;
return 0;
}
return -1;
}
int
end(Vec8_t* vec)
end(const Vec8_t* vec)
{
if (vec->arr != nullptr && vec->size > 0)
if (vec != nullptr && vec->arr != nullptr && vec->size > 0)
{
return (int)(vec->size - 1);
}
@@ -87,60 +131,77 @@ end(Vec8_t* vec)
}
char
front(Vec8_t* vec)
front(const Vec8_t* vec)
{
if (vec->arr != nullptr && vec->size > 0)
{
return vec->arr[0];
}
return -1;
return at(vec, 0);
}
char
back(Vec8_t* vec)
back(const Vec8_t* vec)
{
if (vec->arr != nullptr && vec->size > 0)
if (vec)
{
return vec->arr[vec->size - 1];
return at(vec, (int)vec->size - 1);
}
return -1;
}
/* Return a Vector
* if below capacity
*
*
* */
Vec8_t
add_back(Vec8_t* vec, char val)
add_back(Vec8_t* vec, const char val)
{
Vec8_t nvec = create(vec->size + 1);
memcpy(&nvec.arr[0], &vec->arr[0], vec->size * sizeof(char));
if (nvec.arr && (nvec.size > 0 || nvec.size > vec->size))
Vec8_t ret = { .arr = nullptr, .capacity = 0, .size = 0 };
if (vec->size < vec->capacity)
{
nvec.arr[vec->size] = val;
vec->arr[vec->size] = val;
vec->size++;
return *vec;
}
return nvec;
if (vec->size >= vec->capacity)
{
Vec8_t nvec = create(vec);
memcpy(&nvec.arr[0], &vec->arr[0], vec->size * sizeof(char));
if (nvec.arr && (nvec.size > 0 || nvec.size > vec->size))
{
nvec.arr[vec->size] = val;
}
free(vec->arr);
return nvec;
}
return ret;
}
Vec8_t
add_front(Vec8_t* vec, char val)
{
Vec8_t nvec = create(vec->size + 1);
memcpy(&nvec.arr[1], &vec->arr[0], vec->size * sizeof(char));
if (nvec.arr && (nvec.size > 0 || nvec.size > vec->size))
{
nvec.arr[0] = val;
}
return nvec;
}
// Vec8_t
// add_front(Vec8_t* vec, char val)
// {
// Vec8_t nvec = create(vec->size + 1);
// memcpy(&nvec.arr[1], &vec->arr[0], vec->size * sizeof(char));
// if (nvec.arr && (nvec.size > 0 || nvec.size > vec->size))
// {
// nvec.arr[0] = val;
// }
// return nvec;
// }
int
main()
{
Vec8_t vec = create(0);
Vec8_t vec = create(nullptr);
vec = add_back(&vec, '3');
vec = add_back(&vec, '4');
vec = add_back(&vec, '5');
vec = add_back(&vec, '6');
print_vec(&vec);
vec = add_front(&vec, 'c');
print_vec(&vec);
vec = add_back(&vec, '7');
print_vec(&vec);
// vec = add_back(&vec, '6');
// vec = add_front(&vec, 'c');
// print_vec(&vec);
// printf("%c", at(nullptr, 0));
delete(&vec);
return 0;
BIN
View File
Binary file not shown.
+187
View File
@@ -0,0 +1,187 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <pthread.h>
typedef struct {
char* arr;
size_t size;
size_t capacity;
} Vec8_t;
Vec8_t create(Vec8_t* input) {
Vec8_t vec = { .arr = nullptr, .size = 0, .capacity = 4 };
if(input == nullptr) {
vec.arr = calloc(vec.capacity, sizeof(char));
return vec;
}
if (input->size > 0) {
vec.size = input->size + 1;
}
if (input->capacity >= vec.capacity) {
vec.capacity = 2 * input->capacity;
}
vec.arr = calloc(vec.capacity, sizeof(char));
return vec;
}
void delete(Vec8_t* vec) {
if (vec->arr != nullptr) free(vec->arr);
vec->arr = nullptr;
}
char at(Vec8_t* vec, int idx) {
if (vec == nullptr) return -2;
if (vec->arr == nullptr) return -3;
if (vec->size <= idx) return -4;
if (vec != nullptr && vec->arr != nullptr && vec->size > idx) {
return vec->arr[idx];
}
return -1;
}
int begin(Vec8_t* vec) {
if (vec != nullptr && vec->arr != nullptr && vec->size > 0) return 0;
return -1;
}
int end(Vec8_t* vec) {
if (vec != nullptr && vec->arr != nullptr && vec->size > 0) return (int)(vec->size - 1);
return -1;
}
char front(Vec8_t* vec) {
return at(vec, 0);
}
char back(Vec8_t* vec) {
if (vec) return at(vec, (int)vec->size - 1);
return -1;
}
Vec8_t add_back(Vec8_t* vec, char val) {
Vec8_t ret = { .arr = nullptr, .capacity = 0, .size = 0 };
if (vec->size < vec->capacity) {
vec->arr[vec->size] = val;
vec->size++;
return *vec;
}
if (vec->size >= vec->capacity) {
Vec8_t nvec = create(vec);
memcpy(&nvec.arr[0], &vec->arr[0], vec->size * sizeof(char));
if (nvec.arr && (nvec.size > 0 || nvec.size > vec->size)) {
nvec.arr[vec->size] = val;
}
free(vec->arr);
return nvec;
}
return ret;
}
double time_diff(struct timespec start, struct timespec end_t) {
return (end_t.tv_sec - start.tv_sec) + (end_t.tv_nsec - start.tv_nsec) / 1e9;
}
int main() {
struct timespec start, end_t;
Vec8_t vec;
printf("=== CPU Stress Tests ===\n\n");
// Test 1: Rapid fire inserts (tight loop)
printf("--- Test 1: Rapid Fire (100M tight loop) ---\n");
vec = create(nullptr);
clock_gettime(CLOCK_MONOTONIC, &start);
for (int i = 0; i < 100000000; i++) {
vec = add_back(&vec, (char)i);
}
clock_gettime(CLOCK_MONOTONIC, &end_t);
double t1 = time_diff(start, end_t);
printf(" Time: %.3f sec\n", t1);
printf(" Rate: %.0f/sec\n", 100000000.0 / t1);
delete(&vec);
// Test 2: Read-heavy (iteration + at())
printf("\n--- Test 2: Read Every Element (100M at() calls) ---\n");
vec = create(nullptr);
for (int i = 0; i < 100000000; i++) vec = add_back(&vec, (char)i);
long sum = 0;
clock_gettime(CLOCK_MONOTONIC, &start);
for (size_t i = 0; i < vec.size; i++) {
sum += at(&vec, (int)i);
}
clock_gettime(CLOCK_MONOTONIC, &end_t);
double t2 = time_diff(start, end_t);
printf(" Time: %.3f sec\n", t2);
printf(" Sum: %ld\n", sum);
printf(" Rate: %.0f reads/sec\n", 100000000.0 / t2);
delete(&vec);
// Test 3: Mixed operations
printf("\n--- Test 3: Mixed Operations (50M each) ---\n");
vec = create(nullptr);
clock_gettime(CLOCK_MONOTONIC, &start);
for (int i = 0; i < 50000000; i++) {
vec = add_back(&vec, (char)i);
char f = front(&vec);
char b = back(&vec);
at(&vec, i / 2);
}
clock_gettime(CLOCK_MONOTONIC, &end_t);
double t3 = time_diff(start, end_t);
printf(" Time: %.3f sec\n", t3);
printf(" Rate: %.0f ops/sec\n", 50000000.0 * 3 / t3);
delete(&vec);
// Test 4: Cache thrashing (random access)
printf("\n--- Test 4: Random Access (100M random at() calls) ---\n");
vec = create(nullptr);
for (int i = 0; i < 100000000; i++) vec = add_back(&vec, (char)i);
srand(12345);
clock_gettime(CLOCK_MONOTONIC, &start);
sum = 0;
for (int i = 0; i < 100000000; i++) {
int idx = rand() % (int)vec.size;
sum += at(&vec, idx);
}
clock_gettime(CLOCK_MONOTONIC, &end_t);
double t4 = time_diff(start, end_t);
printf(" Time: %.3f sec\n", t4);
printf(" Sum: %ld\n", sum);
printf(" Rate: %.0f random reads/sec\n", 100000000.0 / t4);
delete(&vec);
// Test 5: Sequential scan (raw pointer access)
printf("\n--- Test 5: Sequential Scan (raw pointer, 500M bytes) ---\n");
vec = create(nullptr);
for (int i = 0; i < 500000000; i++) vec = add_back(&vec, (char)(i % 256));
sum = 0;
clock_gettime(CLOCK_MONOTONIC, &start);
char* ptr = vec.arr;
for (size_t i = 0; i < vec.size; i++) {
sum += ptr[i];
}
clock_gettime(CLOCK_MONOTONIC, &end_t);
double t5 = time_diff(start, end_t);
printf(" Time: %.3f sec\n", t5);
printf(" Throughput: %.1f GB/sec\n", (vec.size / 1e9) / t5);
printf(" Sum: %ld\n", sum);
delete(&vec);
// Test 6: Create/delete cycles (memory allocation stress)
printf("\n--- Test 6: Create/Delete Cycles (1M vectors) ---\n");
clock_gettime(CLOCK_MONOTONIC, &start);
for (int i = 0; i < 1000000; i++) {
vec = create(nullptr);
for (int j = 0; j < 100; j++) vec = add_back(&vec, (char)j);
delete(&vec);
}
clock_gettime(CLOCK_MONOTONIC, &end_t);
double t6 = time_diff(start, end_t);
printf(" Time: %.3f sec\n", t6);
printf(" Rate: %.0f create/delete cycles/sec\n", 1000000.0 / t6);
printf("\n=== CPU Stress Complete ===\n");
return 0;
}
BIN
View File
Binary file not shown.
+317
View File
@@ -0,0 +1,317 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <limits.h>
typedef struct {
char* arr;
size_t size;
size_t capacity;
} Vec8_t;
Vec8_t create(Vec8_t* input) {
Vec8_t vec = { .arr = nullptr, .size = 0, .capacity = 4 };
if(input == nullptr) {
vec.arr = calloc(vec.capacity, sizeof(char));
return vec;
}
if (input->size > 0) {
vec.size = input->size + 1;
}
if (input->capacity >= vec.capacity) {
vec.capacity = 2 * input->capacity;
}
vec.arr = calloc(vec.capacity, sizeof(char));
return vec;
}
void delete(Vec8_t* vec) {
if (vec->arr != nullptr) free(vec->arr);
vec->arr = nullptr;
}
char at(Vec8_t* vec, int idx) {
if (vec == nullptr) return -2;
if (vec->arr == nullptr) return -3;
if (vec->size <= idx) return -4;
if (vec != nullptr && vec->arr != nullptr && vec->size > idx) {
return vec->arr[idx];
}
return -1;
}
int begin(Vec8_t* vec) {
if (vec != nullptr && vec->arr != nullptr && vec->size > 0) return 0;
return -1;
}
int end(Vec8_t* vec) {
if (vec != nullptr && vec->arr != nullptr && vec->size > 0) return (int)(vec->size - 1);
return -1;
}
char front(Vec8_t* vec) {
return at(vec, 0);
}
char back(Vec8_t* vec) {
if (vec) return at(vec, (int)vec->size - 1);
return -1;
}
Vec8_t add_back(Vec8_t* vec, char val) {
Vec8_t ret = { .arr = nullptr, .capacity = 0, .size = 0 };
if (vec->size < vec->capacity) {
vec->arr[vec->size] = val;
vec->size++;
return *vec;
}
if (vec->size >= vec->capacity) {
Vec8_t nvec = create(vec);
memcpy(&nvec.arr[0], &vec->arr[0], vec->size * sizeof(char));
if (nvec.arr && (nvec.size > 0 || nvec.size > vec->size)) {
nvec.arr[vec->size] = val;
}
free(vec->arr);
return nvec;
}
return ret;
}
int tests_passed = 0;
int tests_failed = 0;
int test_num = 0;
void test(const char* name, int passed) {
test_num++;
if (passed) {
printf("[PASS] #%d: %s\n", test_num, name);
tests_passed++;
} else {
printf("[FAIL] #%d: %s\n", test_num, name);
tests_failed++;
}
}
int main() {
printf("=== EDGE CASE BRUTAL TESTS ===\n\n");
Vec8_t vec;
char* ptr;
// === Test 1: Use after reallocation (expected behavior) ===
printf("--- Reallocation Behavior ---\n");
vec = create(nullptr);
for (int i = 0; i < 10; i++) vec = add_back(&vec, (char)('A' + i));
ptr = vec.arr; // save pointer (will be freed on next realloc)
for (int i = 0; i < 100; i++) vec = add_back(&vec, (char)('0' + (i % 10)));
// Pointer changes after reallocation - this is expected
test("after reallocation, vec.arr is new", ptr != vec.arr);
test("data preserved in new vec", vec.arr[0] == 'A' && vec.arr[9] == 'J');
test("vec must be reassigned: vec = add_back()", 1);
delete(&vec);
// === Test 2: Stack-allocated struct (not created via create) ===
printf("\n--- Stack-Allocated Struct ---\n");
Vec8_t stack_vec;
stack_vec.arr = nullptr;
stack_vec.size = 0;
stack_vec.capacity = 0;
// This will fail because capacity starts at 0, not 4
// and there's no way to use add_back properly
// BUG: User can't easily use functions without calling create()
test("stack vec: add_back modifies nothing (capacity 0)", 1); // informational
// === Test 3: Integer overflow potential ===
printf("\n--- Integer Overflow ---\n");
// If someone passes SIZE_MAX or near-max to create
// The doubling could overflow
vec = create(nullptr);
vec.capacity = (size_t)-1; // max size_t
vec.size = 0;
vec.arr = calloc(1, 1); // tiny allocation
// BUG: 2 * capacity overflows to 0
size_t doubled = 2 * vec.capacity;
test("2 * SIZE_MAX overflows (demonstrates overflow)", 1); // informational
free(vec.arr);
// === Test 4: Negative index with signed int ===
printf("\n--- Signed/Unsigned Mixing ---\n");
vec = create(nullptr);
vec = add_back(&vec, 'X');
int neg_idx = -1;
// BUG: -1 as unsigned is huge, causing out-of-bounds access
char result = at(&vec, neg_idx);
test("at() with negative -1 returns error", result == -4);
neg_idx = -2147483648;
result = at(&vec, neg_idx);
test("at() with INT_MIN returns error", result == -4);
delete(&vec);
// === Test 5: Size very large ===
printf("\n--- Huge Size Values ---\n");
vec = create(nullptr);
vec.size = 1000000; // manually corrupt size (bad user code)
vec.capacity = 4;
vec.arr = calloc(4, 1);
// BUG: size > capacity, add_back will try to create with size+1 = 1000001
// which allocates based on old logic
result = at(&vec, 0);
// Should still work for valid indices within actual capacity
test("at() works for valid idx despite bad size", result == 0);
delete(&vec);
// === Test 6: Uninitialized struct ===
printf("\n--- Uninitialized Struct ---\n");
Vec8_t uninit;
// BUG: arr, size, capacity contain garbage
// Any function call is undefined behavior
printf(" Calling at() on uninitialized struct...\n");
printf(" This is undefined behavior - results unpredictable!\n");
test("uninitialized: expect garbage or crash (UB)", 1); // informational
// === Test 7: Capacity not matching allocation ===
printf("\n--- Capacity Mismatch ---\n");
vec = create(nullptr);
vec.arr = realloc(vec.arr, 1000); // manually grow
vec.capacity = 1000; // but don't tell anyone
for (int i = 0; i < 50; i++) vec = add_back(&vec, (char)i);
test("manual realloc: capacity grows (add_back doesn't know)", 1); // informational
delete(&vec);
// === Test 8: memcpy with null ===
printf("\n--- Memcpy Edge Cases ---\n");
vec = create(nullptr);
vec.size = 0;
// BUG: memcpy(&vec.arr[0], &vec.arr[0], 0) - this is actually fine (no-op)
for (int i = 0; i < 5; i++) vec = add_back(&vec, (char)('A' + i));
test("empty memcpy edge case: works fine", vec.size == 5);
delete(&vec);
// === Test 9: Return value ignored ===
printf("\n--- Ignored Return Values ---\n");
vec = create(nullptr);
add_back(&vec, 'X'); // Modifies in-place when capacity available
test("add_back modifies in-place when capacity available", vec.size == 1);
test("data stored correctly", vec.arr[0] == 'X');
// Fill to capacity (4): size goes 1->4
for (int i = 0; i < 3; i++) vec = add_back(&vec, (char)('A' + i));
// Now size=4, cap=4
// Add more: this triggers reallocation (size >= capacity)
char* old_ptr = vec.arr;
vec = add_back(&vec, 'Z'); // reallocates! size becomes 5, cap becomes 8
test("reallocation triggered", old_ptr != vec.arr);
test("realloc: data preserved in new vec", vec.arr[4] == 'Z');
delete(&vec);
// === Test 10: Multiple vectors, aliasing ===
printf("\n--- Aliasing Issues ---\n");
Vec8_t v1 = create(nullptr);
Vec8_t v2 = create(nullptr);
for (int i = 0; i < 5; i++) v1 = add_back(&v1, (char)('A' + i));
for (int i = 0; i < 5; i++) v2 = add_back(&v2, (char)('1' + i));
test("v1 and v2 are separate", v1.arr != v2.arr);
test("v1 data correct", v1.arr[0] == 'A');
test("v2 data correct", v2.arr[0] == '1');
delete(&v1);
delete(&v2);
// === Test 11: Freeing already-freed memory ===
printf("\n--- Double Free ---\n");
vec = create(nullptr);
vec = add_back(&vec, 'X');
char* data = vec.arr;
delete(&vec);
free(data); // BUG: double free!
printf(" Double free detected (would crash with sanitizers)\n");
test("double free: undefined behavior", 1); // informational
// === Test 12: Realloc failure ===
printf("\n--- Realloc Failure ---\n");
// Hard to test without mocking, but conceptually:
// If malloc/calloc fails, the library doesn't check
// BUG: nvec.arr could be NULL, then memcpy with NULL crashes
printf(" If realloc fails: nvec.arr is NULL, memcpy crashes\n");
test("no malloc failure check in create()", 1); // informational
// === Test 13: Reading freed memory ===
printf("\n--- Use After Delete ---\n");
vec = create(nullptr);
for (int i = 0; i < 10; i++) vec = add_back(&vec, (char)i);
delete(&vec);
// BUG: vec.arr is nullptr, vec.size is still 10
// User might expect size to be 0 after delete
test("after delete: arr is null", vec.arr == nullptr);
test("after delete: size is still 10 (not cleared)", vec.size == 10);
test("after delete: accessing returns error", at(&vec, 0) == -3);
// === Test 14: Size 0 with capacity 0 ===
printf("\n--- Size/Capacity Edge ---\n");
vec.arr = malloc(100);
vec.size = 0;
vec.capacity = 100;
// BUG: begin() checks size > 0, so returns -1 even though we have capacity
test("begin() returns -1 when size=0", begin(&vec) == -1);
test("end() returns -1 when size=0", end(&vec) == -1);
test("front() returns -4 when size=0", front(&vec) == -4);
test("back() returns -4 when size=0", back(&vec) == -4);
free(vec.arr);
// === Test 15: Concurrent modification (conceptual) ===
printf("\n--- Iterator Invalidation Concept ---\n");
vec = create(nullptr);
for (int i = 0; i < 10; i++) vec = add_back(&vec, (char)('A' + i));
// If user iterates while adding:
// for (int i = 0; i < 1000; i++) {
// if (i == 10) vec = add_back(&vec, 'Z'); // reallocation!
// use vec.arr[i]; // BUG: pointer invalid after realloc
// }
printf(" Iteration + reallocation invalidates pointers\n");
test("conceptual: iteration during add_back is unsafe", 1);
delete(&vec);
// === Summary ===
printf("\n=== Summary ===\n");
printf("Passed: %d\n", tests_passed);
printf("Failed: %d\n", tests_failed);
printf("Note: 'informational' tests always pass, real bugs above\n");
return 0;
}
BIN
View File
Binary file not shown.
+95
View File
@@ -0,0 +1,95 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
typedef struct {
char* arr;
size_t size;
} Vec8_t;
Vec8_t create(size_t init_size) {
Vec8_t vec = { .arr = nullptr, .size = 0 };
if (init_size > 0) {
vec.arr = malloc(init_size * sizeof(char));
vec.size = init_size;
}
return vec;
}
void delete(Vec8_t* vec) {
if (vec->arr != nullptr) free(vec->arr);
vec->arr = nullptr;
}
int main(int argc, char* argv[]) {
size_t count = 100000000; // 100M
int vec_count = 1;
if (argc > 1) count = atoll(argv[1]);
if (argc > 2) vec_count = atoi(argv[2]);
size_t total = count * (size_t)vec_count;
printf("=== Direct Allocation Memory Test ===\n");
printf("Elements per vector: %zu\n", count);
printf("Number of vectors: %d\n", vec_count);
printf("Total elements: %zu\n", total);
printf("Total memory: %.1f GB\n", (total * sizeof(char)) / (1024.0 * 1024.0 * 1024.0));
printf("\n");
struct timespec start, end;
Vec8_t* vecs = malloc(vec_count * sizeof(Vec8_t));
clock_gettime(CLOCK_MONOTONIC, &start);
printf("Allocating %d vectors of %zu elements each...\n", vec_count, count);
for (int v = 0; v < vec_count; v++) {
vecs[v] = create(count);
if (vecs[v].arr == nullptr) {
printf(" ERROR: Failed to allocate vector %d!\n", v);
return 1;
}
}
clock_gettime(CLOCK_MONOTONIC, &end);
double alloc_time = (end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec) / 1e9;
printf("Allocation time: %.2f seconds\n", alloc_time);
printf("\nFilling vectors...\n");
clock_gettime(CLOCK_MONOTONIC, &start);
for (int v = 0; v < vec_count; v++) {
for (size_t i = 0; i < count; i++) {
vecs[v].arr[i] = (char)(i % 256);
}
}
clock_gettime(CLOCK_MONOTONIC, &end);
double fill_time = (end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec) / 1e9;
printf("Fill time: %.2f seconds\n", fill_time);
printf("\nVerifying...\n");
int errors = 0;
for (int v = 0; v < vec_count; v++) {
if (vecs[v].size != count) errors++;
if (vecs[v].arr[0] != 0) errors++;
if (vecs[v].arr[count - 1] != (char)((count - 1) % 256)) errors++;
}
printf("Errors: %d\n", errors);
printf("\nFreeing...\n");
clock_gettime(CLOCK_MONOTONIC, &start);
for (int v = 0; v < vec_count; v++) {
delete(&vecs[v]);
}
clock_gettime(CLOCK_MONOTONIC, &end);
double free_time = (end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec) / 1e9;
printf("Free time: %.2f seconds\n", free_time);
free(vecs);
printf("\n=== Complete ===\n");
printf("Total time: %.2f seconds\n", alloc_time + fill_time + free_time);
return errors > 0 ? 1 : 0;
}
BIN
View File
Binary file not shown.
+264
View File
@@ -0,0 +1,264 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
typedef struct {
char* arr;
size_t size;
size_t capacity;
} Vec8_t;
Vec8_t create(Vec8_t* input) {
Vec8_t vec = { .arr = nullptr, .size = 0, .capacity = 4 };
if(input == nullptr) {
vec.arr = calloc(vec.capacity, sizeof(char));
return vec;
}
if (input->size > 0) {
vec.size = input->size + 1;
}
if (input->capacity >= vec.capacity) {
vec.capacity = 2 * input->capacity;
}
vec.arr = calloc(vec.capacity, sizeof(char));
return vec;
}
void delete(Vec8_t* vec) {
if (vec->arr != nullptr) free(vec->arr);
vec->arr = nullptr;
}
char at(Vec8_t* vec, int idx) {
if (vec == nullptr) return -2;
if (vec->arr == nullptr) return -3;
if (vec->size <= idx) return -4;
if (vec != nullptr && vec->arr != nullptr && vec->size > idx) {
return vec->arr[idx];
}
return -1;
}
int begin(Vec8_t* vec) {
if (vec != nullptr && vec->arr != nullptr && vec->size > 0) return 0;
return -1;
}
int end(Vec8_t* vec) {
if (vec != nullptr && vec->arr != nullptr && vec->size > 0) return (int)(vec->size - 1);
return -1;
}
char front(Vec8_t* vec) {
return at(vec, 0);
}
char back(Vec8_t* vec) {
if (vec) return at(vec, (int)vec->size - 1);
return -1;
}
Vec8_t add_back(Vec8_t* vec, char val) {
Vec8_t ret = { .arr = nullptr, .capacity = 0, .size = 0 };
if (vec->size < vec->capacity) {
vec->arr[vec->size] = val;
vec->size++;
return *vec;
}
if (vec->size >= vec->capacity) {
Vec8_t nvec = create(vec);
memcpy(&nvec.arr[0], &vec->arr[0], vec->size * sizeof(char));
if (nvec.arr && (nvec.size > 0 || nvec.size > vec->size)) {
nvec.arr[vec->size] = val;
}
free(vec->arr);
return nvec;
}
return ret;
}
int tests_passed = 0;
int tests_failed = 0;
int test_num = 0;
void test(const char* name, int passed) {
test_num++;
if (passed) {
printf("[PASS] #%d: %s\n", test_num, name);
tests_passed++;
} else {
printf("[FAIL] #%d: %s\n", test_num, name);
tests_failed++;
}
}
int main() {
printf("=== Reliability Tests ===\n\n");
Vec8_t vec;
// === Basic Initialization ===
printf("--- Initialization ---\n");
vec = create(nullptr);
test("create(nullptr) returns valid vec", vec.arr != nullptr);
test("create(nullptr) capacity = 4", vec.capacity == 4);
test("create(nullptr) size = 0", vec.size == 0);
delete(&vec);
// === Empty Vector Operations ===
printf("\n--- Empty Vector Operations ---\n");
vec = create(nullptr);
test("at() on empty returns error", at(&vec, 0) == -4);
test("at() on empty (neg idx) returns error", at(&vec, -1) == -4);
test("at() on empty (large idx) returns error", at(&vec, 100) == -4);
test("front() on empty returns error", front(&vec) == -4);
test("back() on empty returns error", back(&vec) == -4);
test("begin() on empty returns -1", begin(&vec) == -1);
test("end() on empty returns -1", end(&vec) == -1);
delete(&vec);
// === Single Element ===
printf("\n--- Single Element ---\n");
vec = create(nullptr);
vec = add_back(&vec, 'X');
test("single element: size = 1", vec.size == 1);
test("single element: at(0) = 'X'", at(&vec, 0) == 'X');
test("single element: front() = 'X'", front(&vec) == 'X');
test("single element: back() = 'X'", back(&vec) == 'X');
test("single element: begin() = 0", begin(&vec) == 0);
test("single element: end() = 0", end(&vec) == 0);
delete(&vec);
// === Capacity Boundaries ===
printf("\n--- Capacity Boundaries ---\n");
vec = create(nullptr);
for (int i = 0; i < 4; i++) vec = add_back(&vec, (char)('A' + i));
test("at capacity (4): size = 4", vec.size == 4);
test("at capacity (4): capacity = 4", vec.capacity == 4);
vec = add_back(&vec, 'E');
test("at capacity (4): size = 5", vec.size == 5);
test("at capacity (4): capacity doubled = 8", vec.capacity == 8);
test("at capacity (4): arr[4] = 'E'", vec.arr[4] == 'E');
delete(&vec);
// === Data Integrity ===
printf("\n--- Data Integrity ---\n");
vec = create(nullptr);
for (int i = 0; i < 100; i++) vec = add_back(&vec, (char)i);
test("data integrity: size = 100", vec.size == 100);
for (int i = 0; i < 100; i++) {
test("data integrity: arr[i] = i", vec.arr[i] == (char)i);
}
delete(&vec);
// === Large Capacity ===
printf("\n--- Large Capacity ---\n");
vec = create(nullptr);
for (int i = 0; i < 1000; i++) vec = add_back(&vec, (char)(i % 256));
test("large vec: size = 1000", vec.size == 1000);
test("large vec: capacity >= size", vec.capacity >= 1000);
test("large vec: first preserved", vec.arr[0] == 0);
test("large vec: last preserved", vec.arr[999] == (char)(999 % 256));
delete(&vec);
// === NULL Safety ===
printf("\n--- NULL Safety ---\n");
test("at(nullptr) = -2", at(nullptr, 0) == -2);
test("front(nullptr) = -2", front(nullptr) == -2);
test("back(nullptr) = -1", back(nullptr) == -1);
test("begin(nullptr) = -1", begin(nullptr) == -1);
test("end(nullptr) = -1", end(nullptr) == -1);
// === Memory Leak Check (basic) ===
printf("\n--- Memory Leak Check ---\n");
for (int round = 0; round < 100; round++) {
vec = create(nullptr);
for (int i = 0; i < 50; i++) vec = add_back(&vec, (char)i);
delete(&vec);
}
test("100 create/delete cycles: no crash", 1);
// === Double Delete ===
printf("\n--- Double Delete Safety ---\n");
vec = create(nullptr);
vec = add_back(&vec, 'T');
delete(&vec);
delete(&vec); // Should not crash
test("double delete: no crash", 1);
// === Delete Then Access ===
printf("\n--- Delete Then Access ---\n");
vec = create(nullptr);
vec = add_back(&vec, 'A');
vec = add_back(&vec, 'B');
delete(&vec);
test("after delete: at() returns error", at(&vec, 0) == -3);
test("after delete: front() returns error", front(&vec) == -3);
test("after delete: back() returns -3 (size still 2)", back(&vec) == -3);
// === Extreme Values ===
printf("\n--- Extreme Values ---\n");
vec = create(nullptr);
vec = add_back(&vec, 0);
vec = add_back(&vec, 127);
vec = add_back(&vec, -128);
test("extreme: zero preserved", vec.arr[0] == 0);
test("extreme: max char preserved", vec.arr[1] == 127);
test("extreme: min char preserved", vec.arr[2] == -128);
delete(&vec);
// === Rapid Small Operations ===
printf("\n--- Rapid Small Operations ---\n");
for (int round = 0; round < 10; round++) {
vec = create(nullptr);
for (int i = 0; i < 10; i++) vec = add_back(&vec, (char)('A' + i));
test("rapid small: round preserves data",
vec.arr[0] == 'A' && vec.arr[9] == 'J');
delete(&vec);
}
// === Alternating Add/Read ===
printf("\n--- Alternating Add/Read ---\n");
vec = create(nullptr);
for (int i = 0; i < 50; i++) {
vec = add_back(&vec, (char)('A' + i));
test("alternating: read during add",
at(&vec, i) == (char)('A' + i));
}
delete(&vec);
// === Capacity Exact Match ===
printf("\n--- Capacity Exact Match ---\n");
vec = create(nullptr);
for (int i = 0; i < 8; i++) vec = add_back(&vec, (char)i);
test("exact cap: size = 8", vec.size == 8);
test("exact cap: capacity = 8", vec.capacity == 8);
vec = add_back(&vec, 'X');
test("exact cap +1: size = 9", vec.size == 9);
test("exact cap +1: capacity = 16", vec.capacity == 16);
delete(&vec);
// === Summary ===
printf("\n=== Summary ===\n");
printf("Passed: %d\n", tests_passed);
printf("Failed: %d\n", tests_failed);
printf("Total: %d\n", tests_passed + tests_failed);
return tests_failed > 0 ? 1 : 0;
}
BIN
View File
Binary file not shown.
+191
View File
@@ -0,0 +1,191 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
typedef struct {
char* arr;
size_t size;
size_t capacity;
} Vec8_t;
Vec8_t create(Vec8_t* input) {
Vec8_t vec = { .arr = nullptr, .size = 0, .capacity = 4 };
if(input == nullptr) {
vec.arr = calloc(vec.capacity, sizeof(char));
return vec;
}
if (input->size > 0) {
vec.size = input->size + 1;
}
if (input->capacity >= vec.capacity) {
vec.capacity = 2 * input->capacity;
}
vec.arr = calloc(vec.capacity, sizeof(char));
return vec;
}
void delete(Vec8_t* vec) {
if (vec->arr != nullptr) free(vec->arr);
vec->arr = nullptr;
}
char at(Vec8_t* vec, int idx) {
if (vec == nullptr) return -2;
if (vec->arr == nullptr) return -3;
if (vec->size <= idx) return -4;
if (vec != nullptr && vec->arr != nullptr && vec->size > idx) {
return vec->arr[idx];
}
return -1;
}
int begin(Vec8_t* vec) {
if (vec != nullptr && vec->arr != nullptr && vec->size > 0) return 0;
return -1;
}
int end(Vec8_t* vec) {
if (vec != nullptr && vec->arr != nullptr && vec->size > 0) return (int)(vec->size - 1);
return -1;
}
char front(Vec8_t* vec) {
return at(vec, 0);
}
char back(Vec8_t* vec) {
if (vec) return at(vec, (int)vec->size - 1);
return -1;
}
Vec8_t add_back(Vec8_t* vec, char val) {
Vec8_t ret = { .arr = nullptr, .capacity = 0, .size = 0 };
if (vec->size < vec->capacity) {
vec->arr[vec->size] = val;
vec->size++;
return *vec;
}
if (vec->size >= vec->capacity) {
Vec8_t nvec = create(vec);
memcpy(&nvec.arr[0], &vec->arr[0], vec->size * sizeof(char));
if (nvec.arr && (nvec.size > 0 || nvec.size > vec->size)) {
nvec.arr[vec->size] = val;
}
free(vec->arr);
return nvec;
}
return ret;
}
int tests_passed = 0;
int tests_failed = 0;
void test(const char* name, int passed) {
if (passed) { printf("[PASS] %s\n", name); tests_passed++; }
else { printf("[FAIL] %s\n", name); tests_failed++; }
}
double time_diff(struct timespec start, struct timespec end_t) {
return (end_t.tv_sec - start.tv_sec) + (end_t.tv_nsec - start.tv_nsec) / 1e9;
}
int main() {
printf("=== Stress Testing libvec (doubling) ===\n\n");
struct timespec start, end_t;
Vec8_t vec;
printf("--- Basic correctness ---\n");
vec = create(nullptr);
test("create nullptr: capacity = 4", vec.capacity == 4);
test("create nullptr: size = 0", vec.size == 0);
vec = add_back(&vec, 'a');
test("add_back 1: size = 1", vec.size == 1);
test("add_back 1: capacity = 4", vec.capacity == 4);
vec = add_back(&vec, 'b');
test("add_back 2: size = 2", vec.size == 2);
vec = add_back(&vec, 'c');
test("add_back 3: size = 3", vec.size == 3);
vec = add_back(&vec, 'd');
test("add_back 4: size = 4", vec.size == 4);
vec = add_back(&vec, 'e');
test("add_back 5: capacity = 8 (doubled)", vec.capacity == 8);
test("add_back 5: arr[4] = 'e'", vec.arr[4] == 'e');
delete(&vec);
printf("\n--- NULL safety ---\n");
test("at(nullptr) returns -2", at(nullptr, 0) == -2);
test("front(nullptr) returns -2", front(nullptr) == -2);
test("back(nullptr) returns -1", back(nullptr) == -1);
test("begin(nullptr) == -1", begin(nullptr) == -1);
test("end(nullptr) == -1", end(nullptr) == -1);
printf("\n--- Capacity growth pattern ---\n");
vec = create(nullptr);
size_t prev_cap = vec.capacity;
printf(" cap=%zu", vec.capacity);
for (int i = 1; i <= 100; i++) {
vec = add_back(&vec, (char)i);
if (vec.capacity != prev_cap) {
printf(" -> %zu (at size %d)", vec.capacity, i);
prev_cap = vec.capacity;
}
}
printf("\n");
test("final capacity = 128", vec.capacity == 128);
delete(&vec);
printf("\n--- 1M add_back ---\n");
vec = create(nullptr);
clock_gettime(CLOCK_MONOTONIC, &start);
for (int i = 0; i < 1000000; i++) vec = add_back(&vec, (char)i);
clock_gettime(CLOCK_MONOTONIC, &end_t);
double t1 = time_diff(start, end_t);
printf(" Time: %.3f sec (%.0f/sec)\n", t1, 1000000.0/t1);
test("1M: size correct", vec.size == 1000000);
test("1M: data correct", vec.arr[0] == 0 && vec.arr[999999] == (char)999999);
delete(&vec);
printf("\n--- 10M add_back ---\n");
vec = create(nullptr);
clock_gettime(CLOCK_MONOTONIC, &start);
for (int i = 0; i < 10000000; i++) vec = add_back(&vec, (char)i);
clock_gettime(CLOCK_MONOTONIC, &end_t);
double t10 = time_diff(start, end_t);
printf(" Time: %.3f sec (%.0f/sec)\n", t10, 10000000.0/t10);
test("10M: size correct", vec.size == 10000000);
test("10M: data correct", vec.arr[0] == 0 && vec.arr[9999999] == (char)(9999999 % 256));
delete(&vec);
printf("\n--- 100M add_back ---\n");
vec = create(nullptr);
clock_gettime(CLOCK_MONOTONIC, &start);
for (int i = 0; i < 100000000; i++) vec = add_back(&vec, (char)i);
clock_gettime(CLOCK_MONOTONIC, &end_t);
double t100 = time_diff(start, end_t);
printf(" Time: %.3f sec (%.0f/sec)\n", t100, 100000000.0/t100);
test("100M: size correct", vec.size == 100000000);
test("100M: data correct", vec.arr[0] == 0 && vec.arr[99999999] == (char)(99999999 % 256));
delete(&vec);
printf("\n--- 1B add_back ---\n");
vec = create(nullptr);
clock_gettime(CLOCK_MONOTONIC, &start);
for (int i = 0; i < 1000000000; i++) vec = add_back(&vec, (char)i);
clock_gettime(CLOCK_MONOTONIC, &end_t);
double t1b = time_diff(start, end_t);
printf(" Time: %.3f sec (%.0f/sec)\n", t1b, 1000000000.0/t1b);
test("1B: size correct", vec.size == 1000000000);
test("1B: data correct", vec.arr[0] == 0 && vec.arr[999999999] == (char)(999999999 % 256));
delete(&vec);
printf("\n=== Summary ===\n");
printf("Passed: %d, Failed: %d\n", tests_passed, tests_failed);
return tests_failed > 0 ? 1 : 0;
}