Files
bluelib/object/array.c

381 lines
7.3 KiB
C

#include "array.h"
#include <blue/core/iterator.h>
#include <blue/core/stream.h>
#include <blue/object/array.h>
#include <blue/object/type.h>
#include <stdlib.h>
#include <string.h>
static void array_release(struct b_object *obj);
static void array_to_string(const struct b_object *obj, struct b_stream *out);
static struct b_object_type array_type = {
.t_flags = B_OBJECT_FUNDAMENTAL,
.t_id = B_OBJECT_TYPE_ARRAY,
.t_name = "corelib::array",
.t_instance_size = sizeof(struct b_array),
.t_release = array_release,
.t_to_string = array_to_string,
};
struct b_array *b_array_create(void)
{
struct b_array *array
= (struct b_array *)b_object_type_instantiate(&array_type);
if (!array) {
return NULL;
}
return array;
}
struct b_array *b_array_create_with_values(
struct b_object *const *values, size_t nr_values)
{
struct b_array *array = b_array_create();
if (!array) {
return NULL;
}
size_t real_nr_values = 0;
for (size_t i = 0; i < nr_values; i++) {
if (values[i]) {
real_nr_values++;
}
}
array->ar_len = real_nr_values;
array->ar_cap = real_nr_values;
array->ar_data = calloc(real_nr_values, sizeof(struct b_object *));
if (!array->ar_data) {
b_array_release(array);
return NULL;
}
size_t index = 0;
for (size_t i = 0; i < nr_values; i++) {
array->ar_data[index++] = b_retain(values[i]);
}
return array;
}
static b_status resize_array(struct b_array *array, size_t new_capacity)
{
if (array->ar_cap < new_capacity) {
void *new_data = realloc(
array->ar_data, new_capacity * sizeof(struct b_object *));
if (!new_data) {
return B_ERR_NO_MEMORY;
}
array->ar_data = new_data;
} else {
for (size_t i = new_capacity; i < array->ar_len; i++) {
b_release(array->ar_data[i]);
}
void *new_data = realloc(
array->ar_data, new_capacity * sizeof(struct b_object *));
if (!new_data) {
return B_ERR_NO_MEMORY;
}
array->ar_data = new_data;
}
array->ar_cap = new_capacity;
if (array->ar_len > new_capacity) {
array->ar_len = new_capacity;
}
return B_SUCCESS;
}
b_status b_array_append(struct b_array *array, struct b_object *value)
{
return b_array_insert(array, value, B_NPOS);
}
b_status b_array_prepend(struct b_array *array, struct b_object *value)
{
return b_array_insert(array, value, 0);
}
b_status b_array_insert(struct b_array *array, struct b_object *value, size_t at)
{
if (at == B_NPOS) {
at = array->ar_len;
}
if (at > array->ar_len) {
return B_ERR_OUT_OF_BOUNDS;
}
b_status status = B_SUCCESS;
if (array->ar_len + 1 > array->ar_cap) {
status = resize_array(array, array->ar_cap + 8);
if (status != B_SUCCESS) {
return status;
}
}
struct b_object **src = array->ar_data + at;
struct b_object **dest = src + 1;
size_t move_len = (array->ar_len - at) * sizeof(struct b_object *);
memmove(dest, src, move_len);
array->ar_data[at] = b_retain(value);
array->ar_len++;
return B_SUCCESS;
}
b_status b_array_remove(struct b_array *array, size_t at)
{
if (at >= array->ar_len) {
return B_ERR_OUT_OF_BOUNDS;
}
struct b_object **src = array->ar_data + at;
struct b_object **dest = src + 1;
size_t move_len = array->ar_len * sizeof(struct b_object *);
b_release(array->ar_data[at]);
memmove(dest, src, move_len);
array->ar_len--;
return B_SUCCESS;
}
b_status b_array_remove_front(struct b_array *array)
{
return b_array_remove(array, 0);
}
b_status b_array_remove_back(struct b_array *array)
{
return b_array_remove(array, array->ar_len - 1);
}
struct b_object *b_array_pop(b_array *array, size_t at)
{
if (at >= array->ar_len) {
return NULL;
}
struct b_object **src = array->ar_data + at;
struct b_object **dest = src + 1;
size_t move_len = array->ar_len * sizeof(struct b_object *);
struct b_object *out = array->ar_data[at];
memmove(dest, src, move_len);
array->ar_len--;
return out;
}
struct b_object *b_array_pop_front(struct b_array *array)
{
return b_array_pop(array, 0);
}
struct b_object *b_array_pop_back(struct b_array *array)
{
return b_array_pop(array, array->ar_len - 1);
}
struct b_object *b_array_at(const struct b_array *array, size_t at)
{
if (at >= array->ar_len) {
return NULL;
}
return array->ar_data[at];
}
struct b_object *b_array_get(struct b_array *array, size_t at)
{
if (at >= array->ar_len) {
return NULL;
}
return b_retain(array->ar_data[at]);
}
size_t b_array_size(const struct b_array *array)
{
return array->ar_len;
}
size_t b_array_capacity(const struct b_array *array)
{
return array->ar_cap;
}
static void array_to_string(const struct b_object *obj, struct b_stream *out)
{
struct b_array *array = B_ARRAY(obj);
if (!array->ar_len) {
b_stream_write_string(out, "[]", NULL);
return;
}
b_stream_write_string(out, "[\n", NULL);
b_stream_push_indent(out, 1);
size_t len = b_array_size(array);
b_array_iterator it;
b_array_foreach(&it, array)
{
bool is_string = b_typeid(it.value) == B_OBJECT_TYPE_STRING;
if (is_string) {
b_stream_write_char(out, '"');
}
b_to_string(it.value, out);
if (is_string) {
b_stream_write_char(out, '"');
}
if (it.i < len - 1) {
b_stream_write_string(out, ",", NULL);
}
b_stream_write_char(out, '\n');
}
b_stream_pop_indent(out);
b_stream_write_char(out, ']');
}
static void array_release(struct b_object *obj)
{
struct b_array *array = B_ARRAY(obj);
if (array->ar_data) {
for (size_t i = 0; i < array->ar_len; i++) {
b_release(array->ar_data[i]);
}
free(array->ar_data);
array->ar_data = NULL;
}
}
void b_array_clear(struct b_array *array)
{
while (array->ar_len) {
b_array_remove_back(array);
}
}
static bool array_iterator_next(struct b_iterator *it)
{
return b_array_iterator_next((struct b_array_iterator *)it);
}
static b_status array_iterator_erase(struct b_iterator *it)
{
return b_array_iterator_erase((struct b_array_iterator *)it);
}
static bool array_iterator_is_valid(const struct b_iterator *it)
{
return b_array_iterator_is_valid((const struct b_array_iterator *)it);
}
static b_iterator_ops it_ops = {
.it_next = array_iterator_next,
.it_close = NULL,
.it_erase = array_iterator_erase,
.it_is_valid = array_iterator_is_valid,
};
int b_array_iterator_begin(struct b_array *array, struct b_array_iterator *it)
{
it->_a = array;
it->i = 0;
it->_base.it_ops = &it_ops;
if (array->ar_len > 0) {
it->value = array->ar_data[0];
} else {
it->value = NULL;
}
return 0;
}
bool b_array_iterator_next(struct b_array_iterator *it)
{
struct b_array *array = it->_a;
if (it->value == NULL || it->i >= array->ar_len) {
return false;
}
it->i++;
if (it->i >= array->ar_len) {
it->value = NULL;
} else {
it->value = array->ar_data[it->i];
}
return it->value != NULL;
}
b_status b_array_iterator_erase(struct b_array_iterator *it)
{
struct b_array *array = it->_a;
if (it->i >= array->ar_len) {
return B_ERR_OUT_OF_BOUNDS;
}
if (array->ar_data[it->i] != it->value) {
return B_ERR_BAD_STATE;
}
b_array_remove(array, it->i);
if (it->i < array->ar_len) {
it->value = array->ar_data[it->i];
} else {
it->value = NULL;
}
return B_SUCCESS;
}
bool b_array_iterator_is_valid(const struct b_array_iterator *it)
{
struct b_array *array = it->_a;
if (it->i >= array->ar_len) {
return false;
}
if (array->ar_data[it->i] != it->value) {
return false;
}
return it->value != NULL;
}
b_object_type_id b_array_type_id(void)
{
return (b_object_type_id)&array_type;
}