2026-04-13 17:55:45 -06:00
|
|
|
#include "arraylist.h"
|
|
|
|
|
#include <stdbool.h>
|
2026-04-15 06:50:04 -06:00
|
|
|
#include <stdint.h>
|
2026-04-13 17:55:45 -06:00
|
|
|
#include <stdlib.h>
|
2026-04-14 19:33:15 -06:00
|
|
|
#include <string.h>
|
2026-04-13 17:55:45 -06:00
|
|
|
|
2026-04-14 20:30:02 -06:00
|
|
|
struct ArrayList{
|
2026-04-15 06:50:04 -06:00
|
|
|
uint8_t *buffer;
|
2026-04-14 20:30:02 -06:00
|
|
|
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) {
|
2026-04-13 17:55:45 -06:00
|
|
|
if (capacity == 0) {
|
2026-04-14 20:30:02 -06:00
|
|
|
return NULL;
|
2026-04-13 17:55:45 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (elem_size == 0) {
|
2026-04-14 20:30:02 -06:00
|
|
|
return NULL;
|
2026-04-13 17:55:45 -06:00
|
|
|
}
|
|
|
|
|
|
2026-04-14 20:30:02 -06:00
|
|
|
ArrayList *arr = malloc(sizeof(ArrayList));
|
|
|
|
|
if (arr == NULL) {
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
arr->capacity = capacity;
|
|
|
|
|
arr->elem_size = elem_size;
|
|
|
|
|
arr->len = 0;
|
2026-04-15 06:50:04 -06:00
|
|
|
arr->buffer = malloc(capacity * elem_size);
|
|
|
|
|
if (arr->buffer == NULL) {
|
2026-04-14 20:30:02 -06:00
|
|
|
free(arr);
|
|
|
|
|
return NULL;
|
2026-04-13 17:55:45 -06:00
|
|
|
}
|
|
|
|
|
|
2026-04-14 20:30:02 -06:00
|
|
|
return arr;
|
2026-04-13 17:55:45 -06:00
|
|
|
}
|
2026-04-14 19:33:15 -06:00
|
|
|
|
|
|
|
|
ArrayListErr arraylist_destroy(ArrayList *arr) {
|
|
|
|
|
if (arr == NULL) {
|
|
|
|
|
return ARRLIST_NULL_ARG;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
arr->capacity = 0;
|
|
|
|
|
arr->elem_size = 0;
|
|
|
|
|
arr->len = 0;
|
2026-04-15 06:50:04 -06:00
|
|
|
free(arr->buffer);
|
|
|
|
|
arr->buffer = NULL;
|
2026-04-14 20:30:02 -06:00
|
|
|
free(arr);
|
2026-04-14 19:33:15 -06:00
|
|
|
return ARRLIST_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ArrayListErr arraylist_clear(ArrayList *arr) {
|
|
|
|
|
if (arr == NULL) {
|
|
|
|
|
return ARRLIST_NULL_ARG;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-15 06:50:04 -06:00
|
|
|
memset(arr->buffer, 0, arr->capacity * arr->elem_size);
|
2026-04-14 19:33:15 -06:00
|
|
|
arr->capacity = 0;
|
|
|
|
|
arr->elem_size = 0;
|
|
|
|
|
arr->len = 0;
|
|
|
|
|
return ARRLIST_OK;
|
|
|
|
|
}
|
2026-04-14 19:38:56 -06:00
|
|
|
|
2026-04-14 20:30:02 -06:00
|
|
|
size_t arraylist_size(const ArrayList *arr) {
|
2026-04-14 19:38:56 -06:00
|
|
|
if (arr == NULL) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
return arr->len;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-14 20:30:02 -06:00
|
|
|
size_t arraylist_capacity(const ArrayList *arr) {
|
2026-04-14 19:38:56 -06:00
|
|
|
if (arr == NULL) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
return arr->capacity;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-14 20:30:02 -06:00
|
|
|
bool arraylist_is_empty(const ArrayList *arr) {
|
2026-04-14 19:38:56 -06:00
|
|
|
if (arr == NULL) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return arr->capacity == 0;
|
|
|
|
|
}
|
2026-04-14 20:30:02 -06:00
|
|
|
|
|
|
|
|
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;
|
2026-04-15 06:50:04 -06:00
|
|
|
void *tmp = realloc(arr->buffer, new_capacity * arr->elem_size);
|
2026-04-14 20:30:02 -06:00
|
|
|
if (tmp == NULL) {
|
|
|
|
|
return ARRLIST_BAD_ALLOC;
|
|
|
|
|
}
|
|
|
|
|
arr->capacity = new_capacity;
|
2026-04-15 06:50:04 -06:00
|
|
|
arr->buffer = tmp;
|
2026-04-14 20:30:02 -06:00
|
|
|
return ARRLIST_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static ArrayListErr arraylist_shrink(ArrayList *arr) {
|
|
|
|
|
size_t new_capacity = arr->capacity / 2;
|
2026-04-15 06:50:04 -06:00
|
|
|
void *tmp = realloc(arr->buffer, new_capacity *arr->elem_size);
|
2026-04-14 20:30:02 -06:00
|
|
|
if (tmp == NULL) {
|
|
|
|
|
return ARRLIST_BAD_ALLOC;
|
|
|
|
|
}
|
|
|
|
|
arr->capacity = new_capacity;
|
2026-04-15 06:50:04 -06:00
|
|
|
arr->buffer = tmp;
|
2026-04-14 20:30:02 -06:00
|
|
|
return ARRLIST_OK;
|
|
|
|
|
}
|
2026-04-15 06:50:04 -06:00
|
|
|
|
|
|
|
|
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, 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
|
|
|
|
|
for (size_t i = arr->len - 1; i >= 0; i--) {
|
|
|
|
|
memcpy(
|
|
|
|
|
arr->buffer + ((i + 1) * arr->elem_size),
|
|
|
|
|
arr->buffer + (i * arr->elem_size),
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (size_t i = arr->len - 1; i >= index; i--) {
|
|
|
|
|
memcpy(
|
|
|
|
|
arr->buffer + ((i + 1) * arr->elem_size),
|
|
|
|
|
arr->buffer + (i * arr->elem_size),
|
|
|
|
|
arr->elem_size);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memcpy(arr->buffer + (index * arr->elem_size), data, arr->elem_size);
|
|
|
|
|
arr->len++;
|
|
|
|
|
|
|
|
|
|
return ARRLIST_OK;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-15 07:15:35 -06:00
|
|
|
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
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < arr->len; i++) {
|
|
|
|
|
memcpy(
|
|
|
|
|
arr->buffer + (i * arr->elem_size),
|
|
|
|
|
arr->buffer + ((i + 1) * arr->elem_size),
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-15 06:50:04 -06:00
|
|
|
|