erase() should really now set proper values to 0 after move

This commit is contained in:
Andrew Haynes
2026-05-05 10:34:35 -04:00
parent 5e9cce36a5
commit cbdcef91b9
5 changed files with 493 additions and 5 deletions
+6 -5
View File
@@ -126,7 +126,7 @@ at(const Vec8_t* vec, const int idx)
// - Result: a b \0 \0 g h i j (size=8, should be 7) // - Result: a b \0 \0 g h i j (size=8, should be 7)
__attribute__((overloadable)) Vec8_t* __attribute__((overloadable)) Vec8_t*
erase(Vec8_t* vec, const int iter) { erase(Vec8_t* vec, const size_t iter) {
if(vec == nullptr) return nullptr; if(vec == nullptr) return nullptr;
if(vec->arr == nullptr) return nullptr; if(vec->arr == nullptr) return nullptr;
if(iter >= vec->size) return nullptr; if(iter >= vec->size) return nullptr;
@@ -137,13 +137,14 @@ erase(Vec8_t* vec, const int iter) {
} }
__attribute__((overloadable)) Vec8_t* __attribute__((overloadable)) Vec8_t*
erase(Vec8_t* vec, const int iter_start, const int iter_end) { erase(Vec8_t* vec, const size_t iter_start, const size_t iter_end) {
if(vec == nullptr) return nullptr; if(vec == nullptr) return nullptr;
if(vec->arr == nullptr) return nullptr; if(vec->arr == nullptr) return nullptr;
if(iter_start < 0 || iter_end > vec->size) return nullptr; if(iter_start < 0 || iter_end >= vec->size) return nullptr;
int diff = iter_end - iter_start; if(iter_start >= iter_end) return nullptr;
size_t diff = iter_end - iter_start;
memmove(&vec->arr[iter_start], &vec->arr[iter_end], (vec->size - iter_end) * sizeof(char)); memmove(&vec->arr[iter_start], &vec->arr[iter_end], (vec->size - iter_end) * sizeof(char));
for(size_t i = vec->size; i > iter_end; i--) { for(size_t i = vec->size - diff; i < vec->size; i++) {
vec->arr[i] = 0; vec->arr[i] = 0;
} }
vec->size -= diff; vec->size -= diff;
BIN
View File
Binary file not shown.
+418
View File
@@ -0,0 +1,418 @@
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <stdio.h>
#include <time.h>
#define CAPACITY 1024
#define nullptr ((void*)0)
typedef struct {
char* arr;
size_t size;
size_t capacity;
} Vec8_t;
Vec8_t create(const Vec8_t* input) {
Vec8_t vec = { .arr = nullptr, .size = 0, .capacity = CAPACITY };
if (input != nullptr && input->size > 0) vec.size = input->size + 1;
vec.arr = calloc(vec.capacity, sizeof(char));
return vec;
}
void delete(Vec8_t* vec) {
if (vec->arr != nullptr) { free(vec->arr); vec->arr = nullptr; }
}
Vec8_t add_back(Vec8_t* vec, const char val) {
if (vec->size >= vec->capacity) {
vec->capacity *= 2;
char* nvec = reallocf(vec->arr, vec->capacity * sizeof(char));
if (nvec == NULL) return *vec;
vec->arr = nvec;
}
vec->arr[vec->size] = val;
vec->size++;
return *vec;
}
// Exact copy of src/main.c single erase (lines 128-137)
__attribute__((overloadable)) Vec8_t*
erase(Vec8_t* vec, const int iter) {
if(vec == nullptr) return nullptr;
if(vec->arr == nullptr) return nullptr;
if(iter >= vec->size) return nullptr;
memmove(&vec->arr[iter], &vec->arr[iter + 1], (vec->size - iter - 1) * sizeof(char));
vec->arr[vec->size - 1] = 0;
vec->size--;
return vec;
}
// Exact copy of src/main.c range erase (lines 139-152)
__attribute__((overloadable)) Vec8_t*
erase(Vec8_t* vec, const int iter_start, const int iter_end) {
if(vec == nullptr) return nullptr;
if(vec->arr == nullptr) return nullptr;
if(iter_start < 0 || iter_end >= vec->size) return nullptr;
if(iter_start >= iter_end) return nullptr;
size_t diff = iter_end - iter_start;
memmove(&vec->arr[iter_start], &vec->arr[iter_end], (vec->size - iter_end) * sizeof(char));
for(size_t i = vec->size - 1; i > iter_end; i--) {
vec->arr[i] = 0;
}
vec->size -= diff;
return vec;
}
void print_vec(const Vec8_t* vec) {
for (int i = 0; i < vec->size; i++) printf("%c ", vec->arr[i]);
printf("(size=%zu)\n", vec->size);
}
// Test 1: Basic single erase
void test_single_erase_basic() {
printf("Test: Single erase basic\n");
Vec8_t vec = create(nullptr);
for (int i = 0; i < 10; i++) add_back(&vec, 'a' + i);
erase(&vec, 2); // Erase 'c'
assert(vec.size == 9);
assert(vec.arr[0] == 'a');
assert(vec.arr[1] == 'b');
assert(vec.arr[2] == 'd'); // Shifted correctly
assert(vec.arr[8] == 'j'); // Last element correct
printf("PASS\n\n");
delete(&vec);
}
// Test 2: Single erase at boundaries
void test_single_erase_boundaries() {
printf("Test: Single erase at boundaries\n");
Vec8_t vec = create(nullptr);
for (int i = 0; i < 5; i++) add_back(&vec, 'a' + i);
// Erase first element
erase(&vec, 0);
assert(vec.size == 4);
assert(vec.arr[0] == 'b');
printf("Erase first: PASS\n");
// Reset
delete(&vec);
vec = create(nullptr);
for (int i = 0; i < 5; i++) add_back(&vec, 'a' + i);
// Erase last element
erase(&vec, 4);
assert(vec.size == 4);
assert(vec.arr[3] == 'd');
printf("Erase last: PASS\n");
delete(&vec);
printf("\n");
}
// Test 3: Single erase invalid inputs
void test_single_erase_invalid() {
printf("Test: Single erase invalid inputs\n");
Vec8_t vec = create(nullptr);
for (int i = 0; i < 5; i++) add_back(&vec, 'a' + i);
// Erase with negative index
assert(erase(&vec, -1) == nullptr);
// Erase with out-of-bounds index
assert(erase(&vec, 10) == nullptr);
assert(erase(&vec, 5) == nullptr);
// Erase on null vector
assert(erase(nullptr, 0) == nullptr);
// Erase on vector with null arr
Vec8_t vec2 = { .arr = nullptr, .size = 0, .capacity = 0 };
assert(erase(&vec2, 0) == nullptr);
printf("PASS\n\n");
delete(&vec);
}
// Test 4: Range erase basic
void test_range_erase_basic() {
printf("Test: Range erase basic\n");
Vec8_t vec = create(nullptr);
for (int i = 0; i < 10; i++) add_back(&vec, 'a' + i);
erase(&vec, 2, 4); // Erase [2,4) -> 'c','d'
printf("After erase(2,4): ");
print_vec(&vec);
assert(vec.size == 8);
assert(vec.arr[0] == 'a');
assert(vec.arr[1] == 'b');
assert(vec.arr[2] == 'e'); // Shifted correctly
assert(vec.arr[7] == 'j'); // Last element correct
// Check that valid data is not zeroed incorrectly
for (int i = 0; i < vec.size; i++) {
assert(vec.arr[i] != 0); // Valid positions should not be zero
}
printf("PASS\n\n");
delete(&vec);
}
// Test 5: Range erase - verify zeroing works correctly now
void test_range_erase_zeroing_fixed() {
printf("Test: Range erase zeroing verification\n");
Vec8_t vec = create(nullptr);
for (int i = 0; i < 10; i++) add_back(&vec, 'a' + i);
erase(&vec, 2, 4);
printf("After erase(2,4): ");
print_vec(&vec);
// Verify valid data is not zeroed
for (int i = 0; i < vec.size; i++) {
assert(vec.arr[i] != 0); // Valid positions should not be zero
}
// Verify that positions beyond new size are zeroed
// (The zeroing loop should zero from vec.size-1 down to iter_end)
printf("Zeroing verification: ");
int all_valid = 1;
for (int i = 0; i < vec.size; i++) {
if (vec.arr[i] == 0) {
printf("BUG: arr[%d] is zero but should be valid! ", i);
all_valid = 0;
}
}
if (all_valid) printf("PASS");
printf("\n\n");
delete(&vec);
}
// Test 6: Range erase to end
void test_range_erase_to_end() {
printf("Test: Range erase to end\n");
Vec8_t vec = create(nullptr);
for (int i = 0; i < 10; i++) add_back(&vec, 'a' + i);
Vec8_t* res = erase(&vec, 2, 10); // Erase [2,10)
assert(res != nullptr);
assert(vec.size == 2);
assert(vec.arr[0] == 'a');
assert(vec.arr[1] == 'b');
printf("PASS\n\n");
delete(&vec);
}
// Test 7: Range erase invalid inputs
void test_range_erase_invalid() {
printf("Test: Range erase invalid inputs\n");
Vec8_t vec = create(nullptr);
for (int i = 0; i < 5; i++) add_back(&vec, 'a' + i);
// Invalid start
assert(erase(&vec, -1, 3) == nullptr);
// Invalid end (>= size now, not just >)
assert(erase(&vec, 0, 5) == nullptr); // 5 >= size (5)
// Start >= end now returns nullptr
assert(erase(&vec, 3, 2) == nullptr); // start >= end
assert(erase(&vec, 2, 2) == nullptr); // start == end
// Null vector
assert(erase(nullptr, 0, 1) == nullptr);
printf("PASS\n\n");
delete(&vec);
}
// Test 8: Large vector single erase
void test_large_single_erase() {
printf("Test: Large vector single erase (100k elements)\n");
Vec8_t vec = create(nullptr);
for (int i = 0; i < 100000; i++) add_back(&vec, (char)(i % 256));
erase(&vec, 0); // Erase front
assert(vec.size == 99999);
assert(vec.arr[0] == 1);
erase(&vec, 50000); // Erase middle
assert(vec.size == 99998);
assert(vec.arr[50000] == 2); // After erasing index 1 (value 1), index 50000 is now value 2
printf("PASS\n\n");
delete(&vec);
}
// Test 9: Large vector range erase
void test_large_range_erase() {
printf("Test: Large vector range erase (100k elements, erase 10k)\n");
Vec8_t vec = create(nullptr);
for (int i = 0; i < 100000; i++) add_back(&vec, (char)(i % 256));
erase(&vec, 10000, 20000); // Erase 10k elements
assert(vec.size == 90000);
assert(vec.arr[10000] == (char)(20000 % 256)); // First element after erased range
printf("PASS\n\n");
delete(&vec);
}
// Test 10: Repeated single erasures
void test_repeated_single_erase() {
printf("Test: Repeated single erasures until empty\n");
Vec8_t vec = create(nullptr);
for (int i = 0; i < 100; i++) add_back(&vec, 'a' + (i % 26));
size_t initial_size = vec.size;
for (int i = 0; i < initial_size; i++) {
erase(&vec, 0); // Always erase first element
}
assert(vec.size == 0);
printf("PASS\n\n");
delete(&vec);
}
// Test 11: Stress - random erasures
void test_random_erasures() {
printf("Test: Random erasures (1000 rounds)\n");
srand((unsigned int)time(NULL));
for (int round = 0; round < 1000; round++) {
Vec8_t vec = create(nullptr);
int num_elements = rand() % 1000 + 10;
for (int i = 0; i < num_elements; i++) add_back(&vec, (char)(rand() % 256));
int num_erasures = rand() % 50;
for (int j = 0; j < num_erasures && vec.size > 0; j++) {
int idx = rand() % vec.size;
erase(&vec, idx);
}
// Verify integrity
for (int i = 0; i < vec.size; i++) {
// Just check that we can access without crashing
char val = vec.arr[i];
(void)val;
}
delete(&vec);
}
printf("PASS\n\n");
}
// Test 12: Stress - random range erasures
void test_random_range_erasures() {
printf("Test: Random range erasures (1000 rounds)\n");
srand((unsigned int)time(NULL));
for (int round = 0; round < 1000; round++) {
Vec8_t vec = create(nullptr);
int num_elements = rand() % 1000 + 10;
for (int i = 0; i < num_elements; i++) add_back(&vec, (char)(rand() % 256));
int num_erasures = rand() % 20;
for (int j = 0; j < num_erasures && vec.size > 1; j++) {
int start = rand() % (vec.size - 1);
int end = start + rand() % (vec.size - start) + 1;
if (end > vec.size) end = vec.size;
erase(&vec, start, end);
}
// Verify integrity
for (int i = 0; i < vec.size; i++) {
char val = vec.arr[i];
(void)val;
}
delete(&vec);
}
printf("PASS\n\n");
}
// Test 13: Erase all elements one by one
void test_erase_all_single() {
printf("Test: Erase all elements one by one\n");
Vec8_t vec = create(nullptr);
for (int i = 0; i < 1000; i++) add_back(&vec, 'a' + (i % 26));
while (vec.size > 0) {
erase(&vec, 0);
}
assert(vec.size == 0);
printf("PASS\n\n");
delete(&vec);
}
// Test 14: Erase all elements with range erase
void test_erase_all_range() {
printf("Test: Erase all elements with range erase\n");
Vec8_t vec = create(nullptr);
for (int i = 0; i < 1000; i++) add_back(&vec, 'a' + (i % 26));
erase(&vec, 0, vec.size);
assert(vec.size == 0);
printf("PASS\n\n");
delete(&vec);
}
// Test 15: Memory stress - large vector, erase in middle repeatedly
void test_memory_stress() {
printf("Test: Memory stress - large vector, repeated middle erasure\n");
Vec8_t vec = create(nullptr);
// Add 1 million elements
for (int i = 0; i < 1000000; i++) {
add_back(&vec, (char)(i % 256));
}
printf(" Created vector with %zu elements\n", vec.size);
// Erase from middle repeatedly
for (int i = 0; i < 10000; i++) {
if (vec.size == 0) break;
int idx = vec.size / 2;
erase(&vec, idx);
}
printf(" After 10k middle erasures, size = %zu\n", vec.size);
delete(&vec);
printf("PASS\n\n");
}
int main() {
printf("=== ERASE STRESS TEST SUITE ===\n\n");
test_single_erase_basic();
test_single_erase_boundaries();
test_single_erase_invalid();
test_range_erase_basic();
test_range_erase_zeroing_fixed();
test_range_erase_to_end();
test_range_erase_invalid();
test_large_single_erase();
test_large_range_erase();
test_repeated_single_erase();
test_random_erasures();
test_random_range_erasures();
test_erase_all_single();
test_erase_all_range();
test_memory_stress();
printf("=== ALL TESTS COMPLETED ===\n");
return 0;
}
BIN
View File
Binary file not shown.
+69
View File
@@ -0,0 +1,69 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define CAPACITY 1024
#define nullptr ((void*)0)
typedef struct {
char* arr;
size_t size;
size_t capacity;
} Vec8_t;
Vec8_t create(const Vec8_t* input) {
Vec8_t vec = { .arr = nullptr, .size = 0, .capacity = CAPACITY };
if (input != nullptr && input->size > 0) vec.size = input->size + 1;
vec.arr = calloc(vec.capacity, sizeof(char));
return vec;
}
Vec8_t add_back(Vec8_t* vec, const char val) {
if (vec->size >= vec->capacity) {
vec->capacity *= 2;
char* nvec = reallocf(vec->arr, vec->capacity * sizeof(char));
if (nvec == NULL) return *vec;
vec->arr = nvec;
}
vec->arr[vec->size] = val;
vec->size++;
return *vec;
}
// Your current implementation
__attribute__((overloadable)) Vec8_t*
erase(Vec8_t* vec, const int iter_start, const int iter_end) {
if(vec == nullptr) return nullptr;
if(vec->arr == nullptr) return nullptr;
if(iter_start < 0 || iter_end >= vec->size) return nullptr;
if(iter_start >= iter_end) return nullptr;
size_t diff = iter_end - iter_start;
memmove(&vec->arr[iter_start], &vec->arr[iter_end], (vec->size - iter_end) * sizeof(char));
for(size_t i = vec->size - 1; i > iter_end; i--) {
vec->arr[i] = 0;
}
vec->size -= diff;
return vec;
}
int main() {
Vec8_t vec = create(nullptr);
for (int i = 0; i < 10; i++) add_back(&vec, 'a' + i);
printf("Before erase: ");
for (int i = 0; i < 10; i++) printf("%c ", vec.arr[i]);
printf("(size=%zu)\n", vec.size);
erase(&vec, 2, 4);
printf("After erase(2,4): ");
for (int i = 0; i < 10; i++) printf("%c ", vec.arr[i] == 0 ? '0' : vec.arr[i]);
printf("(size=%zu)\n", vec.size);
printf("\nExpected: a b e f g h i j (size=8)\n");
printf("Got: ");
for (int i = 0; i < vec.size; i++) printf("%c ", vec.arr[i] == 0 ? '0' : vec.arr[i]);
printf("(size=%zu)\n", vec.size);
return 0;
}