177 lines
4.2 KiB
C
177 lines
4.2 KiB
C
|
#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;
|
||
|
}
|
||
|
|
||
|
List list_copy(const List* source) {
|
||
|
List self = list_init(source->element_size);
|
||
|
list_reserve(&self, source->cap);
|
||
|
if(self.cap > 0) {
|
||
|
memcpy(self.data, source->data, source->element_size * source->len);
|
||
|
self.len = source->len;
|
||
|
} else {
|
||
|
LOG_ERROR("Failed to reserve space");
|
||
|
}
|
||
|
return self;
|
||
|
}
|
||
|
|
||
|
void list_empty(List* self) {
|
||
|
if(self->data == NULL || self->cap == 0)
|
||
|
return;
|
||
|
self->data = NULL;
|
||
|
self->cap = 0;
|
||
|
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;
|
||
|
if(self->data == NULL)
|
||
|
new = malloc(new_cap * self->element_size);
|
||
|
else
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
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) {
|
||
|
list_reserve(self, self->len + 1);
|
||
|
|
||
|
if(at >= self->len) {
|
||
|
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);
|
||
|
++self->len;
|
||
|
}
|
||
|
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
size_t list_contains(List* self, void* query) {
|
||
|
union {
|
||
|
uint8_t* as_byte;
|
||
|
void* as_void;
|
||
|
} data = {
|
||
|
.as_void = self->data
|
||
|
};
|
||
|
for(size_t i = 0; i < self->len; ++i) {
|
||
|
if(memcmp(data.as_byte + (i * self->element_size), query, self->element_size) == 0)
|
||
|
return i;
|
||
|
}
|
||
|
return self->len;
|
||
|
}
|