parent
1e86a8736f
commit
8e29343f3e
7 changed files with 188 additions and 123 deletions
@ -0,0 +1,2 @@ |
||||
*.out |
||||
vgcore* |
@ -0,0 +1,10 @@ |
||||
#+TITLE: CVec: A Simple Vector Implementation in C |
||||
#+AUTHOR: Lucien Cartier-Tilet |
||||
#+EMAIL: lucien@phundrak.com |
||||
#+DATE: 2020-10-03 |
||||
|
||||
* What is this project? |
||||
CVec is a simple Vector implementation in C, inspired by C++’s ~std::vector~ |
||||
and Rust’s ~std::vec::Vec~. It doesn’t try to be on par with these in terms of |
||||
features, but simply tries to provide basic features most people will find |
||||
useful. |
@ -0,0 +1,101 @@ |
||||
#include "vector.h" |
||||
|
||||
Vector vec_new(Destructor destructor) |
||||
{ |
||||
Vector self; |
||||
self.length = 0; |
||||
self.capacity = INITIAL_CAPACITY; |
||||
self.offset = sizeof(void *); |
||||
self.elements = (void *)malloc(self.offset * INITIAL_CAPACITY); |
||||
self.destroy = destructor; |
||||
return self; |
||||
} |
||||
|
||||
Vector vec_with_capacity(Destructor t_destructor, size_t t_capacity) |
||||
{ |
||||
Vector self = vec_new(t_destructor); |
||||
free(self.elements); |
||||
self.elements = (void **)malloc(self.offset * t_capacity); |
||||
self.capacity = t_capacity; |
||||
return self; |
||||
} |
||||
|
||||
void *vec_at(Vector *self, size_t t_index) |
||||
{ |
||||
return self->elements[t_index]; |
||||
} |
||||
|
||||
void *vec_safe_at(Vector *self, size_t t_index) |
||||
{ |
||||
return (t_index >= vec_length(self)) ? NULL : vec_at(self, t_index); |
||||
} |
||||
|
||||
static void vec_maybe_destroy_element(Vector *self, size_t t_index) |
||||
{ |
||||
void *element = vec_at(self, t_index); |
||||
if (self->destroy) { |
||||
self->destroy(element); |
||||
} |
||||
} |
||||
|
||||
void *vec_last(Vector *self) |
||||
{ |
||||
return vec_at(self, vec_length(self) - 1); |
||||
} |
||||
|
||||
size_t vec_length(Vector *self) |
||||
{ |
||||
return self->length; |
||||
} |
||||
|
||||
size_t vec_capacity(Vector *self) |
||||
{ |
||||
return self->capacity; |
||||
} |
||||
|
||||
static void vec_realloc(Vector *self) |
||||
{ |
||||
self->capacity *= 2; |
||||
self->elements = realloc(self->elements, sizeof(void *) * vec_capacity(self)); |
||||
if (!self->elements) { |
||||
PDEB("Could not reallocate Vector’s memory, aborting...", NULL); |
||||
exit(ERR_MEM_ALLOC); |
||||
} |
||||
} |
||||
|
||||
void vec_push(Vector *self, void *t_element) |
||||
{ |
||||
if (vec_length(self) >= vec_capacity(self)) { |
||||
vec_realloc(self); |
||||
} |
||||
self->elements[(*self).length++] = t_element; |
||||
} |
||||
|
||||
void vec_pop(Vector *self) |
||||
{ |
||||
if (vec_length(self) <= 0) { |
||||
return; |
||||
} |
||||
vec_maybe_destroy_element(self, vec_length(self) - 1); |
||||
--(*self).length; |
||||
} |
||||
|
||||
void vec_shrink_to_fit(Vector *self) |
||||
{ |
||||
self->capacity = self->length; |
||||
self->elements = realloc(self->elements, sizeof(void *) * vec_capacity(self)); |
||||
if (!self->elements) { |
||||
PDEB("Could not reallocate Vector’s memory, aborting...", NULL); |
||||
exit(ERR_MEM_ALLOC); |
||||
} |
||||
} |
||||
|
||||
void vec_delete(Vector *self) |
||||
{ |
||||
if (self->destroy) { |
||||
for (size_t i = 0; i < vec_length(self); ++i) { |
||||
self->destroy(self->elements[i]); |
||||
} |
||||
} |
||||
free(self->elements); |
||||
} |
@ -0,0 +1,74 @@ |
||||
#ifndef VECTOR_H |
||||
#define VECTOR_H |
||||
|
||||
#include <stdint.h> |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
|
||||
#ifndef DEBUG |
||||
# define DEBUG 0 |
||||
#endif |
||||
#define ERR_MEM_ALLOC 1 |
||||
#define NO_COLOR "\x1b[0m" |
||||
#define RED "\x1b[31m" |
||||
#define GREEN "\x1b[32m" |
||||
#define BROWN "\x1b[33m" |
||||
#define BLUE "\x1b[34m" |
||||
#define MAGENTA "\x1b[35m" |
||||
#define CYAN "\x1b[36m" |
||||
#define GRAY "\x1b[37m" |
||||
|
||||
#define UNINPLEMENTED printf("%s:%d: Not yet implemented", __FILE__, __LINE__) |
||||
#define INITIAL_CAPACITY 4 |
||||
#define PDEB(format, ...) \ |
||||
if (DEBUG) { \
|
||||
fprintf(stderr, \
|
||||
GREEN "%s:%d\t" NO_COLOR format "\n", \
|
||||
__FILE__, \
|
||||
__LINE__, \
|
||||
__VA_ARGS__); \
|
||||
} |
||||
#define foreach(item, vector) \ |
||||
for (int keep = 1, \
|
||||
count = 0, \
|
||||
size = sizeof(vector->elements) / sizeof vector->elements; \
|
||||
keep && count != size; \
|
||||
keep = !keep, count++) \
|
||||
for (item = \
|
||||
(void *)((char *)(vector->elements) + (count * sizeof(void *))); \
|
||||
keep; \
|
||||
keep = !keep) |
||||
|
||||
#ifndef ARRAY_SIZE |
||||
# define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) |
||||
#endif |
||||
|
||||
// TODO
|
||||
// `sizeof()' does NOT work with dynamically allocated memory. Vector capacity
|
||||
// must be tracked manually
|
||||
|
||||
/* Destructor typedef */ |
||||
typedef void (*Destructor)(void *element); |
||||
|
||||
typedef struct Vector_s { |
||||
size_t length; |
||||
size_t capacity; |
||||
size_t offset; |
||||
void ** elements; |
||||
Destructor destroy; |
||||
} Vector; |
||||
|
||||
Vector vec_new(Destructor destructor); |
||||
Vector vec_with_capacity(Destructor destructor, size_t capacity); |
||||
|
||||
void * vec_at(Vector *self, size_t index); |
||||
void * vec_safe_at(Vector *self, size_t index); |
||||
void * vec_last(Vector *self); |
||||
size_t vec_length(Vector *self); |
||||
size_t vec_capacity(Vector *self); |
||||
void vec_push(Vector *self, void *element); |
||||
void vec_pop(Vector *self); |
||||
void vec_shrink_to_fit(Vector *self); |
||||
void vec_delete(Vector *self); |
||||
|
||||
#endif /* VECTOR_H */ |
@ -1,84 +0,0 @@ |
||||
#include "vector.h" |
||||
|
||||
#include <stdlib.h> |
||||
|
||||
Vector *vec_new(uint32_t obj_size, Destructor destructor) |
||||
{ |
||||
Vector *self = (Vector *)malloc(sizeof(Vector)); |
||||
(*self).elements = NULL; |
||||
(*self).obj_size = obj_size; |
||||
(*self).len = 0; |
||||
(*self).destructor = destructor; |
||||
return self; |
||||
} |
||||
|
||||
Vector *vec_new_with_capacity(uint32_t obj_size, |
||||
uint32_t obj_number, |
||||
Destructor destructor) |
||||
{ |
||||
Vector *self = (Vector *)malloc(sizeof(Vector)); |
||||
(*self).elements = (void **)malloc(obj_number); |
||||
(*self).obj_size = obj_size; |
||||
(*self).len = 0; |
||||
(*self).destructor = destructor; |
||||
return self; |
||||
} |
||||
|
||||
uint32_t vec_len(Vector *self) |
||||
{ |
||||
return self->len; |
||||
} |
||||
|
||||
uint32_t vec_capacity(Vector *self) |
||||
{ |
||||
return sizeof(self->elements); |
||||
} |
||||
|
||||
uint8_t vec_realloc(Vector *self) |
||||
{ |
||||
void ** elements = self->elements; |
||||
uint32_t new_capacity = (vec_capacity(self) > 0) ? vec_capacity(self) * 2 : 1; |
||||
self->elements = realloc(self->elements, new_capacity); |
||||
if (!self->elements) { |
||||
self->elements = elements; |
||||
return 1; |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
uint8_t vec_push(Vector *self, void *element) |
||||
{ |
||||
UNINPLEMENTED; |
||||
return 1; |
||||
/* if (self->len + 1 > vec_capacity(self)) { */ |
||||
/* vec_realloc(self); */ |
||||
/* } */ |
||||
/* ++(*self).len; */ |
||||
} |
||||
|
||||
uint8_t vec_pop(Vector *self) { |
||||
UNINPLEMENTED; |
||||
return 1; |
||||
} // TODO
|
||||
|
||||
void *vec_at(Vector *self, uint32_t index) |
||||
{ |
||||
return self->elements + (index * self->obj_size); |
||||
} |
||||
|
||||
void *vec_at_safe(Vector *self, uint32_t index) |
||||
{ |
||||
if (index < 0 || index >= self->len) { |
||||
return NULL; |
||||
} |
||||
return vec_at(self, index); |
||||
} |
||||
|
||||
void vec_delete(Vector *self) |
||||
{ |
||||
foreach (void *elem, self) { |
||||
self->destructor(elem); |
||||
} |
||||
free(self->elements); |
||||
free(self); |
||||
} |
@ -1,38 +0,0 @@ |
||||
#include <stdint.h> |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
|
||||
#define UNINPLEMENTED printf("%s:%d: Not yet implemented", __FILE__, __LINE__) |
||||
|
||||
#define foreach(item, vector) \ |
||||
for (int keep = 1, \
|
||||
count = 0, \
|
||||
size = sizeof(vector->elements) / sizeof *(vector->elements); \
|
||||
keep && count != size; \
|
||||
keep = !keep, count++) \
|
||||
for (item = (vector->elements) + (count * vector->obj_size); keep; \
|
||||
keep = !keep) |
||||
|
||||
typedef void (*Destructor)(void *element); |
||||
|
||||
struct Vector_s { |
||||
void ** elements; |
||||
uint32_t obj_size; |
||||
uint32_t len; |
||||
Destructor destructor; |
||||
}; |
||||
|
||||
typedef struct Vector_s Vector; |
||||
|
||||
Vector * vec_new_with_capacity(uint32_t obj_size, |
||||
uint32_t obj_number, |
||||
Destructor destructor); // DONE
|
||||
Vector * vec_new(uint32_t obj_size, |
||||
Destructor destructor); // DONE
|
||||
uint32_t vec_len(Vector *self); // DONE
|
||||
uint32_t vec_capacity(Vector *self); // DONE
|
||||
uint8_t vec_push(Vector *self, void *element); // TODO
|
||||
uint8_t vec_pop(Vector *self); // TODO
|
||||
void * vec_at(Vector *self, uint32_t index); // DONE
|
||||
void * vec_at_safe(Vector *self, uint32_t index); // DONE
|
||||
void vec_delete(Vector *self); // DONE
|
Loading…
Reference in new issue