#include "arraylist.h" #include #include #include #include struct ArrayList{ uint8_t *buffer; size_t capacity; size_t len; size_t elem_size; }; // Grow and shrink factor is 2, 1.5 might give different results i guess // but i'm basing myself in that Rust implementation of array does use // 2 as the factor so i guess is good :) static ArrayListErr arraylist_grow(ArrayList *arr); static ArrayListErr arraylist_shrink(ArrayList *arr); ArrayList *arraylist_init(size_t capacity, size_t elem_size) { if (capacity == 0) { return NULL; } if (elem_size == 0) { return NULL; } if (capacity > SIZE_MAX / elem_size) { return NULL; } ArrayList *arr = malloc(sizeof(ArrayList)); if (arr == NULL) { return NULL; } arr->capacity = capacity; arr->elem_size = elem_size; arr->len = 0; arr->buffer = malloc(capacity * elem_size); if (arr->buffer == NULL) { free(arr); return NULL; } return arr; } ArrayListErr arraylist_destroy(ArrayList **arr) { if (arr == NULL || *arr == NULL) { return ARRLIST_NULL_ARG; } // No problem on using free on NULL :) free((*arr)->buffer); free(*arr); *arr = NULL; return ARRLIST_OK; } ArrayListErr arraylist_clear(ArrayList *arr) { if (arr == NULL) { return ARRLIST_NULL_ARG; } memset(arr->buffer, 0, arr->capacity * arr->elem_size); arr->capacity = 0; arr->elem_size = 0; arr->len = 0; return ARRLIST_OK; } size_t arraylist_size(const ArrayList *arr) { if (arr == NULL) { return 0; } return arr->len; } size_t arraylist_capacity(const ArrayList *arr) { if (arr == NULL) { return 0; } return arr->capacity; } bool arraylist_is_empty(const ArrayList *arr) { if (arr == NULL) { return true; } return arr->len == 0; } static ArrayListErr arraylist_grow(ArrayList *arr) { // We can asume is not null because is internal // and only used by funtions that already verified // arr is not null, same with shrink. size_t new_capacity = arr->capacity * 2; if (new_capacity > SIZE_MAX / arr->elem_size) { return ARRLIST_ALLOC_OVERFLOW; } void *tmp = realloc(arr->buffer, new_capacity * arr->elem_size); if (tmp == NULL) { return ARRLIST_BAD_ALLOC; } arr->capacity = new_capacity; arr->buffer = tmp; return ARRLIST_OK; } static ArrayListErr arraylist_shrink(ArrayList *arr) { size_t new_capacity = arr->capacity / 2; if (new_capacity > SIZE_MAX / arr->elem_size) { return ARRLIST_ALLOC_OVERFLOW; } void *tmp = realloc(arr->buffer, new_capacity *arr->elem_size); if (tmp == NULL) { return ARRLIST_BAD_ALLOC; } arr->capacity = new_capacity; arr->buffer = tmp; return ARRLIST_OK; } ArrayListErr arraylist_push_back(ArrayList *arr, void *data) { if (arr == NULL || data == NULL) { return ARRLIST_NULL_ARG; } if (arr->len >= arr->capacity) { ArrayListErr err = arraylist_grow(arr); if (err != ARRLIST_OK) { return err; } } memcpy(arr->buffer + (arr->len * arr->elem_size), data, arr->elem_size); arr->len++; return ARRLIST_OK; } ArrayListErr arraylist_push_front(ArrayList *arr, void *data) { if (arr == NULL || data == NULL) { return ARRLIST_NULL_ARG; } if (arr->len >= arr->capacity) { ArrayListErr err = arraylist_grow(arr); if (err != ARRLIST_OK) { return err; } } // No problem if array is full because it grows if it's full memmove( arr->buffer + arr->elem_size, arr->buffer, (arr->len) * arr->elem_size ); memcpy( arr->buffer, data, arr->elem_size); arr->len++; return ARRLIST_OK; } ArrayListErr arraylist_insert(ArrayList *arr, size_t index, void *data) { if (arr == NULL || data == NULL) { return ARRLIST_NULL_ARG; } if (index > arr->len) { return ARRLIST_OUT_OF_BOUNDS; } if (arr->len >= arr->capacity) { ArrayListErr err = arraylist_grow(arr); if (err != ARRLIST_OK) { return err; } } memmove( arr->buffer + ((index + 1) * arr->elem_size), arr->buffer + (index * arr->elem_size), (arr->len - index) * arr->elem_size); memcpy(arr->buffer + (index * arr->elem_size), data, arr->elem_size); arr->len++; return ARRLIST_OK; } ArrayListErr arraylist_pop_back(ArrayList *arr, void *out) { if (arr == NULL) { return ARRLIST_NULL_ARG; } if (arr->len < 1) { return ARRLIST_EMPTY; } if (arr->capacity / 4 > arr->len) { ArrayListErr err = arraylist_shrink(arr); if (err != ARRLIST_OK) { return err; } } if (out != NULL) { memcpy( out, arr->buffer + (arr->len - 1) * arr->elem_size, arr->elem_size); } arr->len--; return ARRLIST_OK; } ArrayListErr arraylist_pop_front(ArrayList *arr, void *out) { if (arr == NULL) { return ARRLIST_NULL_ARG; } if (arr->len < 1) { return ARRLIST_EMPTY; } if (arr->capacity / 4 > arr->len) { ArrayListErr err = arraylist_shrink(arr); if (err != ARRLIST_OK) { return err; } } if (out != NULL) { memcpy( out, arr->buffer, arr->elem_size ); } memmove( arr->buffer + arr->elem_size, arr->buffer, (arr->len - 1) * arr->elem_size); arr->len--; return ARRLIST_OK; } ArrayListErr arraylist_remove_at(ArrayList *arr, size_t index, void *out) { if (arr == NULL) { return ARRLIST_NULL_ARG; } if (index >= arr->len) { return ARRLIST_OUT_OF_BOUNDS; } if (arr->len < 1) { return ARRLIST_OK; } if (out != NULL) { memcpy( out, arr->buffer + (index * arr->elem_size), arr->elem_size ); } for (size_t i = index; i < arr->len; i++) { memcpy( arr->buffer + (index * arr->elem_size), arr->buffer + ((i + 1) * arr->elem_size), arr->elem_size ); } arr->len--; return ARRLIST_OK; } ArrayListErr arraylist_get(const ArrayList *arr, size_t index, void *out) { if (arr == NULL || out == NULL) { return ARRLIST_NULL_ARG; } if (index >= arr->capacity) { return ARRLIST_OUT_OF_BOUNDS; } memcpy( out, arr->buffer + (index * arr->elem_size), arr->elem_size); return ARRLIST_OK; } ArrayListErr arraylist_set(ArrayList *arr, size_t index, void *data) { if (arr == NULL || data == NULL) { return ARRLIST_NULL_ARG; } if (index >= arr->capacity) { return ARRLIST_OUT_OF_BOUNDS; } memcpy( arr->buffer + (index * arr->elem_size), data, arr->elem_size); return ARRLIST_OK; } ArrayListErr arraylist_resize(ArrayList *arr, size_t new_capacity) { if (arr == NULL) { return ARRLIST_NULL_ARG; } if (new_capacity < 1) { return ARRLIST_INVALID_CAPACITY; } if (new_capacity > SIZE_MAX / arr->elem_size) { return ARRLIST_ALLOC_OVERFLOW; } uint8_t *tmp = realloc(arr->buffer, new_capacity * arr->elem_size); if (tmp == NULL) { return ARRLIST_BAD_ALLOC; } arr->buffer = tmp; arr->capacity = new_capacity; return ARRLIST_OK; } ArrayListErr arraylist_reserve(ArrayList *arr, size_t size_to_reserve) { if (arr == NULL) { return ARRLIST_NULL_ARG; } if (size_to_reserve < 1) { return ARRLIST_INVALID_CAPACITY; } if (arr->capacity + size_to_reserve > SIZE_MAX / arr->elem_size) { return ARRLIST_ALLOC_OVERFLOW; } uint8_t *tmp = realloc( arr->buffer, (arr->capacity + size_to_reserve) * arr->elem_size); if (tmp == NULL) { return ARRLIST_BAD_ALLOC; } arr->buffer = tmp; arr->capacity += size_to_reserve; return ARRLIST_OK; }