added contiguous dynamic length array List
This commit is contained in:
parent
a7846a48d9
commit
30c1e780a1
148
src/list.c
Normal file
148
src/list.c
Normal file
|
@ -0,0 +1,148 @@
|
|||
#include "list.h"
|
||||
#include "stdint.h"
|
||||
#include "stdlib.h"
|
||||
#include "string.h"
|
||||
#include "debug.h"
|
||||
|
||||
#ifndef LIST_DEFAULT_RESERVE
|
||||
#define LIST_DEFAULT_RESERVE 4
|
||||
#endif
|
||||
|
||||
List list_init(size_t element_size) {
|
||||
List self = {
|
||||
.element_size = element_size,
|
||||
.cap = LIST_DEFAULT_RESERVE,
|
||||
.len = 0,
|
||||
.data = malloc(element_size * LIST_DEFAULT_RESERVE),
|
||||
};
|
||||
|
||||
if(self.data == NULL) {
|
||||
LOG_ERROR("Failed to allocate list with starting capacity of %d", LIST_DEFAULT_RESERVE);
|
||||
self.cap = 0;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
void list_empty(List* self) {
|
||||
if(self->data == NULL || self->cap == 0)
|
||||
return;
|
||||
self->data = realloc(self->data, LIST_DEFAULT_RESERVE * self->element_size);
|
||||
self->cap = LIST_DEFAULT_RESERVE;
|
||||
self->len = 0;
|
||||
}
|
||||
|
||||
void list_reserve(List* self, size_t at_least) {
|
||||
if(at_least < self->cap)
|
||||
return;
|
||||
|
||||
size_t new_cap = self->cap > 0 ? self->cap : LIST_DEFAULT_RESERVE;
|
||||
while(at_least >= new_cap) {
|
||||
new_cap *= 2;
|
||||
}
|
||||
|
||||
void* new = realloc(self->data, new_cap * self->element_size);
|
||||
ASSERT_RETURN(new != NULL,, "Failed to reserve space for %zu extra elements in list", new_cap);
|
||||
|
||||
self->data = new;
|
||||
self->cap = new_cap;
|
||||
}
|
||||
|
||||
static inline
|
||||
void* list_at_unchecked(List* self, size_t at) {
|
||||
union {
|
||||
uint8_t* as_byte;
|
||||
void* as_void;
|
||||
} data = {
|
||||
.as_void = self->data
|
||||
};
|
||||
|
||||
return data.as_byte + self->element_size * at;
|
||||
}
|
||||
|
||||
void* list_at(List* self, size_t at) {
|
||||
ASSERT_RETURN(at < self->len, NULL, "Index %zu out of bounds", at);
|
||||
return list_at_unchecked(self, at);
|
||||
}
|
||||
|
||||
size_t list_add(List* self, void* item) {
|
||||
list_reserve(self, self->len + 1);
|
||||
union {
|
||||
uint8_t* as_byte;
|
||||
void* as_void;
|
||||
} data = {
|
||||
.as_void = self->data
|
||||
};
|
||||
|
||||
uint8_t* into = data.as_byte + self->element_size * self->len;
|
||||
|
||||
memcpy(into, item, self->element_size);
|
||||
++self->len;
|
||||
|
||||
return self->len - 1;
|
||||
}
|
||||
|
||||
void list_insert(List* self, void* item, size_t at) {
|
||||
ASSERT_RETURN(at < self->len,, "Index %zu out of bounds", at);
|
||||
|
||||
list_reserve(self, self->len + 1);
|
||||
|
||||
if(at == self->len - 1) {
|
||||
list_add(self, item);
|
||||
return;
|
||||
}
|
||||
|
||||
union {
|
||||
uint8_t* as_byte;
|
||||
void* as_void;
|
||||
} data = {
|
||||
.as_void = self->data
|
||||
};
|
||||
uint8_t* from = data.as_byte + self->element_size * at;
|
||||
uint8_t* into = data.as_byte + self->element_size * (at + 1);
|
||||
uint8_t* end = data.as_byte + self->element_size * self->len;
|
||||
memmove(into, from, end - from);
|
||||
memcpy(from, item, self->element_size);
|
||||
}
|
||||
|
||||
void list_erase(List* self, size_t at) {
|
||||
ASSERT_RETURN(at < self->len,, "Index %zu out of bounds", at);
|
||||
|
||||
union {
|
||||
uint8_t* as_byte;
|
||||
void* as_void;
|
||||
} data = {
|
||||
.as_void = self->data
|
||||
};
|
||||
|
||||
uint8_t* into = data.as_byte + at * self->element_size;
|
||||
uint8_t* from = data.as_byte + (at + 1) * self->element_size;
|
||||
|
||||
if(at < self->len - 1)
|
||||
memmove(into, from, (self->len - at) * self->element_size);
|
||||
--self->len;
|
||||
|
||||
size_t new_cap = self->cap;
|
||||
while(new_cap > self->len) {
|
||||
new_cap /= 2;
|
||||
}
|
||||
new_cap *= 2;
|
||||
|
||||
|
||||
if(new_cap == self->cap)
|
||||
return;
|
||||
|
||||
void* shrunk = realloc(self->data, new_cap * self->element_size);
|
||||
ASSERT_RETURN(shrunk != NULL || new_cap == 0,, "Failed to shrink List to %zu", new_cap);
|
||||
|
||||
self->data = shrunk;
|
||||
self->cap = new_cap;
|
||||
}
|
||||
|
||||
void* list_iterator_begin(List* self) {
|
||||
return list_at_unchecked(self, 0);
|
||||
}
|
||||
|
||||
void* list_iterator_end(List* self) {
|
||||
return list_at_unchecked(self, self->len);
|
||||
}
|
31
src/list.h
Normal file
31
src/list.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
#ifndef _fencer_list_h
|
||||
#define _fencer_list_h
|
||||
|
||||
#include "stddef.h"
|
||||
|
||||
typedef struct List List;
|
||||
struct List {
|
||||
void* data;
|
||||
size_t cap;
|
||||
size_t len;
|
||||
|
||||
size_t element_size;
|
||||
};
|
||||
|
||||
extern List list_init(size_t element_size);
|
||||
extern void list_empty(List* list);
|
||||
|
||||
extern void list_reserve(List* self, size_t at_least);
|
||||
extern void* list_at(List* list, size_t at);
|
||||
|
||||
extern size_t list_add(List* self, void* item);
|
||||
extern void list_insert(List* self, void* item, size_t at);
|
||||
extern void list_erase(List* self, size_t at);
|
||||
|
||||
extern void* list_iterator_begin(List* self);
|
||||
extern void* list_iterator_end(List* self);
|
||||
|
||||
#define list_from_type(T) list_init(sizeof(T))
|
||||
#define list_foreach(T, iter, list) for(T* iter = list_iterator_begin(list); iter != (T*)list_iterator_end(list); ++iter)
|
||||
|
||||
#endif // !_fencer_list_h
|
Loading…
Reference in a new issue