Safer push_back and faster push_back (I went from 0(n^2) to 0(1))
This commit is contained in:
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user