meta: rename legacy object module to 'ds'
This commit is contained in:
3
ds/CMakeLists.txt
Normal file
3
ds/CMakeLists.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
include(../cmake/Templates.cmake)
|
||||
|
||||
add_bluelib_module(NAME ds DEPENDENCIES core)
|
||||
380
ds/array.c
Normal file
380
ds/array.c
Normal file
@@ -0,0 +1,380 @@
|
||||
#include "array.h"
|
||||
|
||||
#include <blue/core/iterator.h>
|
||||
#include <blue/core/stream.h>
|
||||
#include <blue/ds/array.h>
|
||||
#include <blue/ds/type.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static void array_release(struct b_dsref *obj);
|
||||
static void array_to_string(struct b_dsref *obj, struct b_stream *out);
|
||||
|
||||
static struct b_dsref_type array_type = {
|
||||
.t_flags = B_DSREF_FUNDAMENTAL,
|
||||
.t_id = B_DSREF_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_dsref_type_instantiate(&array_type);
|
||||
if (!array) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
struct b_array *b_array_create_with_values(
|
||||
struct b_dsref *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_dsref *));
|
||||
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_dsref *));
|
||||
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_dsref *));
|
||||
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_dsref *value)
|
||||
{
|
||||
return b_array_insert(array, value, B_NPOS);
|
||||
}
|
||||
|
||||
b_status b_array_prepend(struct b_array *array, struct b_dsref *value)
|
||||
{
|
||||
return b_array_insert(array, value, 0);
|
||||
}
|
||||
|
||||
b_status b_array_insert(struct b_array *array, struct b_dsref *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_dsref **src = array->ar_data + at;
|
||||
struct b_dsref **dest = src + 1;
|
||||
size_t move_len = (array->ar_len - at) * sizeof(struct b_dsref *);
|
||||
|
||||
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_dsref **src = array->ar_data + at;
|
||||
struct b_dsref **dest = src + 1;
|
||||
size_t move_len = array->ar_len * sizeof(struct b_dsref *);
|
||||
|
||||
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_dsref *b_array_pop(b_array *array, size_t at)
|
||||
{
|
||||
if (at >= array->ar_len) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct b_dsref **src = array->ar_data + at;
|
||||
struct b_dsref **dest = src + 1;
|
||||
size_t move_len = array->ar_len * sizeof(struct b_dsref *);
|
||||
|
||||
struct b_dsref *out = array->ar_data[at];
|
||||
|
||||
memmove(dest, src, move_len);
|
||||
|
||||
array->ar_len--;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
struct b_dsref *b_array_pop_front(struct b_array *array)
|
||||
{
|
||||
return b_array_pop(array, 0);
|
||||
}
|
||||
|
||||
struct b_dsref *b_array_pop_back(struct b_array *array)
|
||||
{
|
||||
return b_array_pop(array, array->ar_len - 1);
|
||||
}
|
||||
|
||||
struct b_dsref *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_dsref *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(struct b_dsref *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_DSREF_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_dsref *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_dsref_type_id b_array_type_id(void)
|
||||
{
|
||||
return (b_dsref_type_id)&array_type;
|
||||
}
|
||||
15
ds/array.h
Normal file
15
ds/array.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef _BLUELIB_ARRAY_H_
|
||||
#define _BLUELIB_ARRAY_H_
|
||||
|
||||
#include "../object.h"
|
||||
|
||||
struct b_array {
|
||||
struct b_dsref ar_base;
|
||||
/* number of items in array */
|
||||
unsigned int ar_len;
|
||||
/* maximum number of items that can currently be stored in array */
|
||||
unsigned int ar_cap;
|
||||
struct b_dsref **ar_data;
|
||||
};
|
||||
|
||||
#endif
|
||||
0
ds/bitbuffer.c
Normal file
0
ds/bitbuffer.c
Normal file
0
ds/bitbuffer.h
Normal file
0
ds/bitbuffer.h
Normal file
167
ds/bitmap.c
Normal file
167
ds/bitmap.c
Normal file
@@ -0,0 +1,167 @@
|
||||
#include <string.h>
|
||||
#include <blue/ds/bitmap.h>
|
||||
#include <blue/core/bitop.h>
|
||||
|
||||
void b_bitmap_zero(b_bitmap_word *map, unsigned long nbits)
|
||||
{
|
||||
unsigned long words = B_BITMAP_WORDS(nbits);
|
||||
memset(map, 0x00, words * sizeof *map);
|
||||
}
|
||||
|
||||
void b_bitmap_fill(b_bitmap_word *map, unsigned long nbits)
|
||||
{
|
||||
unsigned long words = B_BITMAP_WORDS(nbits);
|
||||
memset(map, 0xFF, words * sizeof *map);
|
||||
}
|
||||
|
||||
void b_bitmap_set(b_bitmap_word *map, unsigned long bit)
|
||||
{
|
||||
unsigned long index = bit / Z__B_BITS_PER_WORD;
|
||||
unsigned long offset = (Z__B_BITS_PER_WORD - bit - 1) & (Z__B_BITS_PER_WORD - 1);
|
||||
unsigned long mask = 1ul << offset;
|
||||
|
||||
map[index] |= mask;
|
||||
}
|
||||
|
||||
void b_bitmap_clear(b_bitmap_word *map, unsigned long bit)
|
||||
{
|
||||
unsigned long index = bit / Z__B_BITS_PER_WORD;
|
||||
unsigned long offset = bit & (Z__B_BITS_PER_WORD - 1);
|
||||
unsigned long mask = 1ul << offset;
|
||||
|
||||
map[index] &= ~mask;
|
||||
}
|
||||
|
||||
bool b_bitmap_check(const b_bitmap_word *map, unsigned long bit)
|
||||
{
|
||||
unsigned long index = bit / Z__B_BITS_PER_WORD;
|
||||
unsigned long offset = (Z__B_BITS_PER_WORD - bit - 1) & (Z__B_BITS_PER_WORD - 1);
|
||||
unsigned long mask = 1ul << offset;
|
||||
|
||||
return (map[index] & mask) != 0;
|
||||
}
|
||||
|
||||
unsigned int b_bitmap_count_set(const b_bitmap_word *map, unsigned long nbits)
|
||||
{
|
||||
unsigned long words = B_BITMAP_WORDS(nbits);
|
||||
unsigned int set_bits = 0;
|
||||
|
||||
for (unsigned long i = 0; i < words; i++) {
|
||||
set_bits += b_popcountl(map[i]);
|
||||
}
|
||||
|
||||
if (set_bits > nbits) {
|
||||
set_bits = nbits;
|
||||
}
|
||||
|
||||
return set_bits;
|
||||
}
|
||||
|
||||
unsigned int b_bitmap_count_clear(const b_bitmap_word *map, unsigned long nbits)
|
||||
{
|
||||
unsigned long words = B_BITMAP_WORDS(nbits);
|
||||
unsigned int clear_bits = 0;
|
||||
|
||||
for (unsigned long i = 0; i < words; i++) {
|
||||
clear_bits += b_popcountl(~map[i]);
|
||||
}
|
||||
|
||||
if (clear_bits > nbits) {
|
||||
clear_bits = nbits;
|
||||
}
|
||||
|
||||
return clear_bits;
|
||||
}
|
||||
|
||||
unsigned int b_bitmap_highest_set(const b_bitmap_word *map, unsigned long nbits)
|
||||
{
|
||||
unsigned long words = B_BITMAP_WORDS(nbits);
|
||||
unsigned long bit_index = 0;
|
||||
b_bitmap_word last_word = 0;
|
||||
|
||||
unsigned long i;
|
||||
for (i = 0; i < words; i++) {
|
||||
if (map[i] != 0x00) {
|
||||
last_word = map[i];
|
||||
bit_index = i * Z__B_BITS_PER_WORD;
|
||||
}
|
||||
}
|
||||
|
||||
if (last_word == 0x00) {
|
||||
return B_BITMAP_NPOS;
|
||||
}
|
||||
|
||||
return bit_index + (Z__B_BITS_PER_WORD - b_ctzl(last_word) - 1);
|
||||
}
|
||||
|
||||
unsigned int b_bitmap_highest_clear(const b_bitmap_word *map, unsigned long nbits)
|
||||
{
|
||||
unsigned long words = B_BITMAP_WORDS(nbits);
|
||||
unsigned long bit_index = 0;
|
||||
b_bitmap_word last_word = ~(b_bitmap_word)0;
|
||||
|
||||
for (unsigned long i = 0; i < words; i++) {
|
||||
if (map[i] != (~(unsigned long)0)) {
|
||||
last_word = map[i];
|
||||
bit_index = i * Z__B_BITS_PER_WORD;
|
||||
}
|
||||
}
|
||||
|
||||
if (last_word == ~(unsigned long)0) {
|
||||
return B_BITMAP_NPOS;
|
||||
}
|
||||
|
||||
if (last_word == 0) {
|
||||
return bit_index + Z__B_BITS_PER_WORD - 1;
|
||||
}
|
||||
|
||||
return bit_index + (Z__B_BITS_PER_WORD - b_ctzl(~last_word)) - 1;
|
||||
}
|
||||
|
||||
unsigned int b_bitmap_lowest_set(const b_bitmap_word *map, unsigned long nbits)
|
||||
{
|
||||
unsigned long words = B_BITMAP_WORDS(nbits);
|
||||
unsigned long bit_index = 0;
|
||||
b_bitmap_word last_word = 0;
|
||||
|
||||
unsigned long i;
|
||||
for (i = 0; i < words; i++) {
|
||||
if (map[i] != 0x00) {
|
||||
last_word = map[i];
|
||||
bit_index = i * Z__B_BITS_PER_WORD;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (last_word == 0x00) {
|
||||
return B_BITMAP_NPOS;
|
||||
}
|
||||
|
||||
return bit_index + b_clzl(last_word);
|
||||
}
|
||||
|
||||
unsigned int b_bitmap_lowest_clear(const b_bitmap_word *map, unsigned long nbits)
|
||||
{
|
||||
unsigned long words = B_BITMAP_WORDS(nbits);
|
||||
unsigned long bit_index = 0;
|
||||
b_bitmap_word last_word = 0;
|
||||
|
||||
unsigned long i;
|
||||
for (i = 0; i < words; i++) {
|
||||
if (map[i] != (~(unsigned long)0)) {
|
||||
last_word = map[i];
|
||||
bit_index = i * Z__B_BITS_PER_WORD;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (last_word == 0) {
|
||||
return bit_index;
|
||||
}
|
||||
|
||||
if (last_word == (~(b_bitmap_word)0)) {
|
||||
return B_BITMAP_NPOS;
|
||||
}
|
||||
|
||||
return bit_index + b_clzl(~last_word);
|
||||
}
|
||||
325
ds/buffer.c
Normal file
325
ds/buffer.c
Normal file
@@ -0,0 +1,325 @@
|
||||
#include "buffer.h"
|
||||
|
||||
#include <blue/core/iterator.h>
|
||||
#include <blue/ds/buffer.h>
|
||||
#include <blue/ds/type.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static void buffer_release(struct b_dsref *obj);
|
||||
|
||||
static struct b_dsref_type buffer_type = {
|
||||
.t_flags = B_DSREF_FUNDAMENTAL,
|
||||
.t_id = B_DSREF_TYPE_BUFFER,
|
||||
.t_name = "corelib::buffer",
|
||||
.t_instance_size = sizeof(struct b_buffer),
|
||||
.t_release = buffer_release,
|
||||
};
|
||||
|
||||
struct b_buffer *b_buffer_create(size_t item_sz)
|
||||
{
|
||||
struct b_buffer *buffer
|
||||
= (struct b_buffer *)b_dsref_type_instantiate(&buffer_type);
|
||||
if (!buffer) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
buffer->buf_itemsz = item_sz;
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
struct b_buffer *b_buffer_create_from_bytes(const void *p, size_t len)
|
||||
{
|
||||
struct b_buffer *buffer = b_buffer_create(1);
|
||||
if (!buffer) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
buffer->buf_len = len;
|
||||
buffer->buf_cap = len;
|
||||
buffer->buf_itemsz = 1;
|
||||
buffer->buf_data = calloc(len, 1);
|
||||
if (!buffer->buf_data) {
|
||||
b_buffer_release(buffer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(buffer->buf_data, p, len);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
struct b_buffer *b_buffer_create_from_array(const void *p, size_t item_sz, size_t len)
|
||||
{
|
||||
struct b_buffer *buffer = b_buffer_create(1);
|
||||
if (!buffer) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
buffer->buf_len = len;
|
||||
buffer->buf_cap = len;
|
||||
buffer->buf_itemsz = item_sz;
|
||||
buffer->buf_data = calloc(len, item_sz);
|
||||
if (!buffer->buf_data) {
|
||||
b_buffer_release(buffer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(buffer->buf_data, p, len * item_sz);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static b_status resize_buffer(struct b_buffer *buffer, size_t new_capacity)
|
||||
{
|
||||
if (buffer->buf_cap < new_capacity) {
|
||||
void *new_data = realloc(
|
||||
buffer->buf_data, new_capacity * buffer->buf_itemsz);
|
||||
if (!new_data) {
|
||||
return B_ERR_NO_MEMORY;
|
||||
}
|
||||
|
||||
buffer->buf_data = new_data;
|
||||
} else {
|
||||
void *new_data = realloc(
|
||||
buffer->buf_data, new_capacity * buffer->buf_itemsz);
|
||||
if (!new_data) {
|
||||
return B_ERR_NO_MEMORY;
|
||||
}
|
||||
|
||||
buffer->buf_data = new_data;
|
||||
}
|
||||
|
||||
buffer->buf_cap = new_capacity;
|
||||
if (buffer->buf_len > new_capacity) {
|
||||
buffer->buf_len = new_capacity;
|
||||
}
|
||||
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
void *b_buffer_steal(struct b_buffer *buf)
|
||||
{
|
||||
void *p = buf->buf_data;
|
||||
|
||||
buf->buf_data = NULL;
|
||||
buf->buf_len = 0;
|
||||
buf->buf_cap = 0;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
enum b_status b_buffer_reserve(struct b_buffer *buf, size_t capacity)
|
||||
{
|
||||
if (buf->buf_cap >= capacity) {
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
return resize_buffer(buf, capacity);
|
||||
}
|
||||
|
||||
enum b_status b_buffer_resize(struct b_buffer *buf, size_t length)
|
||||
{
|
||||
enum b_status status = resize_buffer(buf, length);
|
||||
|
||||
if (!B_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
buf->buf_len = length;
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
enum b_status b_buffer_append(struct b_buffer *buffer, const void *p, size_t count)
|
||||
{
|
||||
return b_buffer_insert(buffer, p, count, B_NPOS);
|
||||
}
|
||||
|
||||
enum b_status b_buffer_prepend(struct b_buffer *buffer, const void *p, size_t count)
|
||||
{
|
||||
return b_buffer_insert(buffer, p, count, 0);
|
||||
}
|
||||
|
||||
enum b_status b_buffer_insert(
|
||||
struct b_buffer *buffer, const void *p, size_t count, size_t at)
|
||||
{
|
||||
if (at == B_NPOS) {
|
||||
at = buffer->buf_len;
|
||||
}
|
||||
|
||||
if (at > buffer->buf_len) {
|
||||
return B_ERR_OUT_OF_BOUNDS;
|
||||
}
|
||||
|
||||
b_status status = B_SUCCESS;
|
||||
|
||||
if (buffer->buf_len + count > buffer->buf_cap) {
|
||||
status = resize_buffer(buffer, buffer->buf_cap + count);
|
||||
|
||||
if (status != B_SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char *src
|
||||
= (unsigned char *)buffer->buf_data + (at * buffer->buf_itemsz);
|
||||
unsigned char *dest = src + (count * buffer->buf_itemsz);
|
||||
size_t move_len = (buffer->buf_len - at) * buffer->buf_itemsz;
|
||||
|
||||
memmove(dest, src, move_len);
|
||||
memcpy(src, p, count * buffer->buf_itemsz);
|
||||
|
||||
buffer->buf_len += count;
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
enum b_status b_buffer_remove(struct b_buffer *buffer, size_t at, size_t count)
|
||||
{
|
||||
if (at >= buffer->buf_len) {
|
||||
return B_ERR_OUT_OF_BOUNDS;
|
||||
}
|
||||
|
||||
if (at + count >= buffer->buf_len) {
|
||||
count = buffer->buf_len - at;
|
||||
}
|
||||
|
||||
unsigned char *dest = buffer->buf_data + (at * buffer->buf_itemsz);
|
||||
unsigned char *src = dest + (count * buffer->buf_itemsz);
|
||||
size_t move_len = (buffer->buf_len - at - count) * buffer->buf_itemsz;
|
||||
|
||||
memmove(dest, src, move_len);
|
||||
|
||||
buffer->buf_len -= count;
|
||||
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
void *b_buffer_ptr(const struct b_buffer *buffer)
|
||||
{
|
||||
return buffer->buf_data;
|
||||
}
|
||||
|
||||
void *b_buffer_get(const struct b_buffer *buffer, size_t at)
|
||||
{
|
||||
if (at >= buffer->buf_len) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (unsigned char *)buffer->buf_data + (at * buffer->buf_itemsz);
|
||||
}
|
||||
|
||||
size_t b_buffer_size(const struct b_buffer *buffer)
|
||||
{
|
||||
return buffer->buf_len;
|
||||
}
|
||||
|
||||
size_t b_buffer_capacity(const struct b_buffer *buffer)
|
||||
{
|
||||
return buffer->buf_cap;
|
||||
}
|
||||
|
||||
void buffer_release(struct b_dsref *obj)
|
||||
{
|
||||
struct b_buffer *buffer = B_BUFFER(obj);
|
||||
|
||||
if (buffer->buf_data) {
|
||||
free(buffer->buf_data);
|
||||
buffer->buf_data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
enum b_status b_buffer_clear(struct b_buffer *buffer)
|
||||
{
|
||||
buffer->buf_len = 0;
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
enum b_status b_buffer_push_back(struct b_buffer *buf, size_t count, void **p)
|
||||
{
|
||||
enum b_status status = B_SUCCESS;
|
||||
|
||||
if (buf->buf_len + count > buf->buf_cap) {
|
||||
status = resize_buffer(buf, buf->buf_len + count);
|
||||
}
|
||||
|
||||
if (!B_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
buf->buf_len += count;
|
||||
*p = b_buffer_get(buf, buf->buf_len - count);
|
||||
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
enum b_status b_buffer_push_front(struct b_buffer *buf, size_t count, void **p)
|
||||
{
|
||||
enum b_status status = B_SUCCESS;
|
||||
|
||||
if (buf->buf_len + count > buf->buf_cap) {
|
||||
status = resize_buffer(buf, buf->buf_len + count);
|
||||
}
|
||||
|
||||
if (!B_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
void *src = buf->buf_data;
|
||||
void *dest = b_buffer_get(buf->buf_data, count);
|
||||
size_t len = count * buf->buf_itemsz;
|
||||
|
||||
memmove(dest, src, len);
|
||||
buf->buf_len += count;
|
||||
|
||||
*p = b_buffer_get(buf, 0);
|
||||
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
enum b_status b_buffer_pop_back(struct b_buffer *buf, size_t count)
|
||||
{
|
||||
if (count > buf->buf_len) {
|
||||
return B_ERR_OUT_OF_BOUNDS;
|
||||
}
|
||||
|
||||
buf->buf_len -= count;
|
||||
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
enum b_status b_buffer_pop_front(struct b_buffer *buf, size_t count)
|
||||
{
|
||||
if (count > buf->buf_len) {
|
||||
return B_ERR_OUT_OF_BOUNDS;
|
||||
}
|
||||
|
||||
void *src = b_buffer_get(buf->buf_data, count);
|
||||
void *dest = buf->buf_data;
|
||||
size_t len = (buf->buf_len - count) * buf->buf_itemsz;
|
||||
|
||||
memmove(dest, src, len);
|
||||
buf->buf_len -= count;
|
||||
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
size_t b_buffer_get_size(const struct b_buffer *buf)
|
||||
{
|
||||
return buf->buf_len;
|
||||
}
|
||||
|
||||
size_t b_buffer_get_item_size(const struct b_buffer *buf)
|
||||
{
|
||||
return buf->buf_itemsz;
|
||||
}
|
||||
|
||||
size_t b_buffer_get_capacity(const struct b_buffer *buf)
|
||||
{
|
||||
return buf->buf_cap;
|
||||
}
|
||||
|
||||
b_dsref_type_id b_buffer_type_id(void)
|
||||
{
|
||||
return (b_dsref_type_id)&buffer_type;
|
||||
}
|
||||
17
ds/buffer.h
Normal file
17
ds/buffer.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef _BLUELIB_BUFFER_H_
|
||||
#define _BLUELIB_BUFFER_H_
|
||||
|
||||
#include "../object.h"
|
||||
|
||||
struct b_buffer {
|
||||
struct b_dsref buf_base;
|
||||
/* number of items in buffer */
|
||||
unsigned int buf_len;
|
||||
/* maximum number of items that can currently be stored in array */
|
||||
unsigned int buf_cap;
|
||||
/* the size of each individual item in the buffer */
|
||||
unsigned int buf_itemsz;
|
||||
void *buf_data;
|
||||
};
|
||||
|
||||
#endif
|
||||
483
ds/dict.c
Normal file
483
ds/dict.c
Normal file
@@ -0,0 +1,483 @@
|
||||
#include "dict.h"
|
||||
|
||||
#include <blue/core/status.h>
|
||||
#include <blue/core/stream.h>
|
||||
#include <blue/ds/dict.h>
|
||||
#include <blue/ds/string.h>
|
||||
#include <blue/ds/type.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define HASH_OFFSET_BASIS 0xcbf29ce484222325
|
||||
#define HASH_PRIME 0x100000001b3
|
||||
|
||||
uint64_t b_cstr_hash(const char *s)
|
||||
{
|
||||
uint64_t hash = HASH_OFFSET_BASIS;
|
||||
|
||||
for (size_t i = 0; s[i]; i++) {
|
||||
hash *= HASH_PRIME;
|
||||
hash ^= s[i];
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
static void dict_release(struct b_dsref *obj);
|
||||
static void dict_to_string(struct b_dsref *obj, struct b_stream *out);
|
||||
|
||||
static struct b_dsref_type dict_type = {
|
||||
.t_name = "corelib::dict",
|
||||
.t_flags = B_DSREF_FUNDAMENTAL,
|
||||
.t_id = B_DSREF_TYPE_DICT,
|
||||
.t_instance_size = sizeof(struct b_dict),
|
||||
.t_release = dict_release,
|
||||
.t_to_string = dict_to_string,
|
||||
};
|
||||
|
||||
struct b_dict *b_dict_create(void)
|
||||
{
|
||||
struct b_dict *dict
|
||||
= (struct b_dict *)b_dsref_type_instantiate(&dict_type);
|
||||
if (!dict) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
struct b_dict *b_dict_create_with_items(const b_dict_item *items)
|
||||
{
|
||||
struct b_dict *dict = b_dict_create();
|
||||
if (!dict) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (size_t i = 0; items[i].key; i++) {
|
||||
b_dict_put(dict, items[i].key, items[i].value);
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
static B_BTREE_DEFINE_SIMPLE_GET(
|
||||
struct b_dict_bucket, uint64_t, bk_node, bk_hash,
|
||||
get_bucket) static B_BTREE_DEFINE_SIMPLE_INSERT(struct b_dict_bucket, bk_node, bk_hash, put_bucket)
|
||||
|
||||
static struct b_dict_bucket *create_bucket(void)
|
||||
{
|
||||
struct b_dict_bucket *bucket = malloc(sizeof *bucket);
|
||||
if (!bucket) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(bucket, 0x0, sizeof *bucket);
|
||||
return bucket;
|
||||
}
|
||||
|
||||
static struct b_dict_bucket_item *create_bucket_item(void)
|
||||
{
|
||||
struct b_dict_bucket_item *item = malloc(sizeof *item);
|
||||
if (!item) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(item, 0x0, sizeof *item);
|
||||
return item;
|
||||
}
|
||||
|
||||
b_status b_dict_put(struct b_dict *dict, const char *key, b_dsref *value)
|
||||
{
|
||||
uint64_t hash = b_cstr_hash(key);
|
||||
struct b_dict_bucket *bucket = get_bucket(&dict->d_buckets, hash);
|
||||
if (!bucket) {
|
||||
bucket = create_bucket();
|
||||
if (!bucket) {
|
||||
return B_ERR_NO_MEMORY;
|
||||
}
|
||||
|
||||
bucket->bk_hash = hash;
|
||||
put_bucket(&dict->d_buckets, bucket);
|
||||
}
|
||||
|
||||
struct b_dict_bucket_item *item = create_bucket_item();
|
||||
if (!item) {
|
||||
return B_ERR_NO_MEMORY;
|
||||
}
|
||||
|
||||
item->bi_str = b_strdup(key);
|
||||
item->bi_value = b_retain(value);
|
||||
|
||||
b_queue_push_back(&bucket->bk_items, &item->bi_entry);
|
||||
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
b_dsref *b_dict_at(const struct b_dict *dict, const char *key)
|
||||
{
|
||||
uint64_t hash = b_cstr_hash(key);
|
||||
struct b_dict_bucket *bucket = get_bucket(&dict->d_buckets, hash);
|
||||
if (!bucket) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
b_queue_iterator it;
|
||||
b_queue_foreach (&it, &bucket->bk_items) {
|
||||
struct b_dict_bucket_item *item
|
||||
= b_unbox(struct b_dict_bucket_item, it.entry, bi_entry);
|
||||
|
||||
if (!strcmp(item->bi_str, key)) {
|
||||
return item->bi_value;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
b_dsref *b_dict_get(struct b_dict *dict, const char *key)
|
||||
{
|
||||
b_dsref *value = b_dict_at(dict, key);
|
||||
if (value) {
|
||||
b_retain(value);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
bool b_dict_has_key(const struct b_dict *dict, const char *key)
|
||||
{
|
||||
return b_dict_at(dict, key) != NULL;
|
||||
}
|
||||
|
||||
size_t b_dict_get_size(const struct b_dict *dict)
|
||||
{
|
||||
size_t count = 0;
|
||||
b_btree_iterator it1;
|
||||
b_queue_iterator it2;
|
||||
|
||||
b_btree_foreach (&it1, &dict->d_buckets) {
|
||||
struct b_dict_bucket *bucket
|
||||
= b_unbox(struct b_dict_bucket, it1.node, bk_node);
|
||||
|
||||
b_queue_foreach (&it2, &bucket->bk_items) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
bool b_dict_is_empty(const b_dict *dict)
|
||||
{
|
||||
b_btree_node *first_node = b_btree_first(&dict->d_buckets);
|
||||
struct b_dict_bucket *first_bucket
|
||||
= b_unbox(struct b_dict_bucket, first_node, bk_node);
|
||||
if (!first_bucket) {
|
||||
return true;
|
||||
}
|
||||
|
||||
b_queue_entry *first_entry = b_queue_first(&first_bucket->bk_items);
|
||||
struct b_dict_bucket_item *first_item
|
||||
= b_unbox(struct b_dict_bucket_item, first_entry, bi_entry);
|
||||
if (!first_item) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void dict_to_string(struct b_dsref *obj, struct b_stream *out)
|
||||
{
|
||||
struct b_dict *dict = B_DICT(obj);
|
||||
|
||||
if (b_dict_is_empty(dict)) {
|
||||
b_stream_write_string(out, "{}", NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
b_stream_write_string(out, "{\n", NULL);
|
||||
|
||||
b_stream_push_indent(out, 1);
|
||||
size_t len = b_dict_get_size(dict);
|
||||
|
||||
b_dict_iterator it;
|
||||
b_dict_foreach(&it, dict)
|
||||
{
|
||||
b_stream_write_fmt(out, NULL, "%s: ", it.key);
|
||||
|
||||
bool is_string = b_typeid(it.value) == B_DSREF_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 bool dict_iterator_next(struct b_iterator *it)
|
||||
{
|
||||
return b_dict_iterator_next((struct b_dict_iterator *)it);
|
||||
}
|
||||
|
||||
static b_status dict_iterator_erase(struct b_iterator *it)
|
||||
{
|
||||
return b_dict_iterator_erase((struct b_dict_iterator *)it);
|
||||
}
|
||||
|
||||
static bool dict_iterator_is_valid(const struct b_iterator *it)
|
||||
{
|
||||
return b_dict_iterator_is_valid((struct b_dict_iterator *)it);
|
||||
}
|
||||
|
||||
static struct b_iterator_ops it_ops
|
||||
= {.it_next = dict_iterator_next,
|
||||
.it_close = NULL,
|
||||
.it_erase = dict_iterator_erase,
|
||||
.it_is_valid = dict_iterator_is_valid};
|
||||
|
||||
int b_dict_iterator_begin(struct b_dict *dict, b_dict_iterator *it)
|
||||
{
|
||||
it->_base.it_ops = &it_ops;
|
||||
|
||||
it->i = 0;
|
||||
if (b_dict_is_empty(dict)) {
|
||||
it->key = NULL;
|
||||
it->value = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
b_btree_node *first_node = b_btree_first(&dict->d_buckets);
|
||||
struct b_dict_bucket *first_bucket
|
||||
= b_unbox(struct b_dict_bucket, first_node, bk_node);
|
||||
if (!first_bucket) {
|
||||
it->key = NULL;
|
||||
it->value = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
b_queue_entry *first_entry = b_queue_first(&first_bucket->bk_items);
|
||||
struct b_dict_bucket_item *first_item
|
||||
= b_unbox(struct b_dict_bucket_item, first_entry, bi_entry);
|
||||
if (!first_item) {
|
||||
it->key = NULL;
|
||||
it->value = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
it->key = first_item->bi_str;
|
||||
it->value = first_item->bi_value;
|
||||
|
||||
it->_d = dict;
|
||||
it->_cbn = first_node;
|
||||
it->_cqe = first_entry;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool get_next_node(
|
||||
struct b_btree_node *cur_node, struct b_queue_entry *cur_entry,
|
||||
struct b_btree_node **out_next_node, struct b_queue_entry **out_next_entry)
|
||||
{
|
||||
struct b_dict_bucket *cur_bucket
|
||||
= b_unbox(struct b_dict_bucket, cur_node, bk_node);
|
||||
if (!cur_bucket) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct b_dict_bucket_item *cur_item
|
||||
= b_unbox(struct b_dict_bucket_item, cur_entry, bi_entry);
|
||||
if (!cur_item) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct b_btree_node *next_node = cur_node;
|
||||
struct b_queue_entry *next_entry = b_queue_next(cur_entry);
|
||||
if (!next_entry) {
|
||||
next_node = b_btree_next(cur_node);
|
||||
if (!next_node) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct b_dict_bucket *next_bucket
|
||||
= b_unbox(struct b_dict_bucket, next_node, bk_node);
|
||||
if (!next_bucket) {
|
||||
return false;
|
||||
}
|
||||
|
||||
next_entry = b_queue_first(&next_bucket->bk_items);
|
||||
if (!next_entry) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
struct b_dict_bucket_item *next_item
|
||||
= b_unbox(struct b_dict_bucket_item, next_entry, bi_entry);
|
||||
if (!next_item) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*out_next_node = next_node;
|
||||
*out_next_entry = next_entry;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool b_dict_iterator_next(b_dict_iterator *it)
|
||||
{
|
||||
struct b_btree_node *next_node;
|
||||
struct b_queue_entry *next_entry;
|
||||
if (!get_next_node(it->_cbn, it->_cqe, &next_node, &next_entry)) {
|
||||
it->key = NULL;
|
||||
it->value = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct b_dict_bucket_item *next_item
|
||||
= b_unbox(struct b_dict_bucket_item, next_entry, bi_entry);
|
||||
|
||||
if (!next_item) {
|
||||
it->key = NULL;
|
||||
it->value = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
it->i++;
|
||||
it->key = next_item->bi_str;
|
||||
it->value = next_item->bi_value;
|
||||
|
||||
it->_cbn = next_node;
|
||||
it->_cqe = next_entry;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static b_status delete_item(
|
||||
struct b_dict *dict, struct b_dict_bucket *bucket,
|
||||
struct b_dict_bucket_item *item)
|
||||
{
|
||||
b_queue_delete(&bucket->bk_items, &item->bi_entry);
|
||||
free(item);
|
||||
|
||||
if (b_queue_empty(&bucket->bk_items)) {
|
||||
b_btree_delete(&dict->d_buckets, &bucket->bk_node);
|
||||
free(bucket);
|
||||
}
|
||||
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
b_status b_dict_iterator_erase(struct b_dict_iterator *it)
|
||||
{
|
||||
if ((it->key || it->value) && !(it->_cbn && it->_cqe)) {
|
||||
return B_ERR_BAD_STATE;
|
||||
}
|
||||
|
||||
if (!it->key || !it->_cqe) {
|
||||
return B_ERR_OUT_OF_BOUNDS;
|
||||
}
|
||||
|
||||
struct b_btree_node *next_node;
|
||||
struct b_queue_entry *next_entry;
|
||||
if (!get_next_node(it->_cbn, it->_cqe, &next_node, &next_entry)) {
|
||||
it->key = NULL;
|
||||
it->value = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
struct b_dict_bucket *cur_bucket
|
||||
= b_unbox(struct b_dict_bucket, it->_cbn, bk_node);
|
||||
struct b_dict_bucket_item *cur_item
|
||||
= b_unbox(struct b_dict_bucket_item, it->_cqe, bi_entry);
|
||||
|
||||
struct b_dict_bucket_item *next_item
|
||||
= b_unbox(struct b_dict_bucket_item, next_entry, bi_entry);
|
||||
|
||||
b_status status = delete_item(it->_d, cur_bucket, cur_item);
|
||||
if (B_ERR(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
if (next_item) {
|
||||
it->key = next_item->bi_str;
|
||||
it->value = next_item->bi_value;
|
||||
|
||||
it->_cbn = next_node;
|
||||
it->_cqe = next_entry;
|
||||
} else {
|
||||
it->key = NULL;
|
||||
it->value = NULL;
|
||||
|
||||
it->_cbn = NULL;
|
||||
it->_cqe = NULL;
|
||||
}
|
||||
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
bool b_dict_iterator_is_valid(const struct b_dict_iterator *it)
|
||||
{
|
||||
return it->key != NULL;
|
||||
}
|
||||
|
||||
static void dict_release(struct b_dsref *obj)
|
||||
{
|
||||
struct b_dict *dict = B_DICT(obj);
|
||||
|
||||
b_btree_iterator tree_it;
|
||||
b_btree_iterator_begin(&dict->d_buckets, &tree_it);
|
||||
while (b_btree_iterator_is_valid(&tree_it)) {
|
||||
struct b_dict_bucket *bucket
|
||||
= b_unbox(struct b_dict_bucket, tree_it.node, bk_node);
|
||||
b_btree_iterator_erase(&tree_it);
|
||||
|
||||
if (!bucket) {
|
||||
continue;
|
||||
}
|
||||
|
||||
b_queue_iterator bucket_it;
|
||||
b_queue_iterator_begin(&bucket->bk_items, &bucket_it);
|
||||
while (b_queue_iterator_is_valid(&bucket_it)) {
|
||||
struct b_dict_bucket_item *item = b_unbox(
|
||||
struct b_dict_bucket_item, bucket_it.entry,
|
||||
bi_entry);
|
||||
b_queue_iterator_erase(&bucket_it);
|
||||
|
||||
if (!item) {
|
||||
continue;
|
||||
}
|
||||
|
||||
free(item->bi_str);
|
||||
b_release(item->bi_value);
|
||||
free(item);
|
||||
}
|
||||
|
||||
free(bucket);
|
||||
}
|
||||
|
||||
#if 0
|
||||
b_dict_iterator it;
|
||||
b_dict_iterator_begin(dict, &it);
|
||||
while (b_dict_iterator_is_valid(&it)) {
|
||||
b_dict_iterator_erase(&it);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
b_dsref_type_id b_dict_type_id(void)
|
||||
{
|
||||
return (b_dsref_type_id)&dict_type;
|
||||
}
|
||||
26
ds/dict.h
Normal file
26
ds/dict.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifndef _B_DICT_H_
|
||||
#define _B_DICT_H_
|
||||
|
||||
#include "object.h"
|
||||
|
||||
#include <blue/core/btree.h>
|
||||
#include <blue/core/queue.h>
|
||||
|
||||
struct b_dict_bucket_item {
|
||||
b_queue_entry bi_entry;
|
||||
char *bi_str;
|
||||
struct b_dsref *bi_value;
|
||||
};
|
||||
|
||||
struct b_dict_bucket {
|
||||
b_btree_node bk_node;
|
||||
uint64_t bk_hash;
|
||||
b_queue bk_items;
|
||||
};
|
||||
|
||||
struct b_dict {
|
||||
struct b_dsref d_base;
|
||||
b_btree d_buckets;
|
||||
};
|
||||
|
||||
#endif
|
||||
461
ds/hashmap.c
Normal file
461
ds/hashmap.c
Normal file
@@ -0,0 +1,461 @@
|
||||
#include "hashmap.h"
|
||||
|
||||
#include <blue/core/misc.h>
|
||||
#include <blue/core/status.h>
|
||||
#include <blue/ds/hashmap.h>
|
||||
#include <blue/ds/string.h>
|
||||
#include <blue/ds/type.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define HASH_OFFSET_BASIS 0xcbf29ce484222325
|
||||
#define HASH_PRIME 0x100000001b3
|
||||
|
||||
/* clang-format off */
|
||||
static B_BTREE_DEFINE_SIMPLE_GET(
|
||||
struct b_hashmap_bucket,
|
||||
uint64_t,
|
||||
bk_node,
|
||||
bk_hash,
|
||||
get_bucket)
|
||||
static B_BTREE_DEFINE_SIMPLE_INSERT(
|
||||
struct b_hashmap_bucket,
|
||||
bk_node,
|
||||
bk_hash,
|
||||
put_bucket)
|
||||
|
||||
static uint64_t hash_data(const void *p, size_t size)
|
||||
{
|
||||
/* clang-format on */
|
||||
const unsigned char *s = p;
|
||||
uint64_t hash = HASH_OFFSET_BASIS;
|
||||
|
||||
for (size_t i = 0; s[i]; i++) {
|
||||
hash *= HASH_PRIME;
|
||||
hash ^= s[i];
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
static void hashmap_release(struct b_dsref *obj);
|
||||
|
||||
static struct b_dsref_type hashmap_type = {
|
||||
.t_name = "corelib::hashmap",
|
||||
.t_flags = B_DSREF_FUNDAMENTAL,
|
||||
.t_id = B_DSREF_TYPE_HASHMAP,
|
||||
.t_instance_size = sizeof(struct b_hashmap),
|
||||
.t_release = hashmap_release,
|
||||
};
|
||||
|
||||
struct b_hashmap *b_hashmap_create(
|
||||
b_hashmap_key_destructor key_dtor, b_hashmap_value_destructor value_dtor)
|
||||
{
|
||||
struct b_hashmap *hashmap
|
||||
= (struct b_hashmap *)b_dsref_type_instantiate(&hashmap_type);
|
||||
if (!hashmap) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return hashmap;
|
||||
}
|
||||
|
||||
struct b_hashmap *b_hashmap_create_with_items(const b_hashmap_item *items)
|
||||
{
|
||||
struct b_hashmap *hashmap = b_hashmap_create(NULL, NULL);
|
||||
if (!hashmap) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (size_t i = 0; items[i].key.key_data && items[i].key.key_size; i++) {
|
||||
b_hashmap_put(hashmap, &items[i].key, &items[i].value);
|
||||
}
|
||||
|
||||
return hashmap;
|
||||
}
|
||||
|
||||
static struct b_hashmap_bucket *create_bucket(void)
|
||||
{
|
||||
/* clang-format on */
|
||||
struct b_hashmap_bucket *bucket = malloc(sizeof *bucket);
|
||||
if (!bucket) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(bucket, 0x0, sizeof *bucket);
|
||||
return bucket;
|
||||
}
|
||||
|
||||
static struct b_hashmap_bucket_item *create_bucket_item(void)
|
||||
{
|
||||
struct b_hashmap_bucket_item *item = malloc(sizeof *item);
|
||||
if (!item) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(item, 0x0, sizeof *item);
|
||||
return item;
|
||||
}
|
||||
|
||||
b_status b_hashmap_put(
|
||||
struct b_hashmap *hashmap, const b_hashmap_key *key,
|
||||
const b_hashmap_value *value)
|
||||
{
|
||||
uint64_t hash = hash_data(key->key_data, key->key_size);
|
||||
struct b_hashmap_bucket *bucket = get_bucket(&hashmap->h_buckets, hash);
|
||||
|
||||
if (!bucket) {
|
||||
bucket = create_bucket();
|
||||
if (!bucket) {
|
||||
return B_ERR_NO_MEMORY;
|
||||
}
|
||||
|
||||
bucket->bk_hash = hash;
|
||||
put_bucket(&hashmap->h_buckets, bucket);
|
||||
}
|
||||
|
||||
b_queue_iterator it;
|
||||
b_queue_foreach (&it, &bucket->bk_items) {
|
||||
struct b_hashmap_bucket_item *item = b_unbox(
|
||||
struct b_hashmap_bucket_item, it.entry, bi_entry);
|
||||
|
||||
if (item->bi_key.key_size != key->key_size) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!memcmp(item->bi_key.key_data, key->key_data, key->key_size)) {
|
||||
return B_ERR_NAME_EXISTS;
|
||||
}
|
||||
}
|
||||
|
||||
struct b_hashmap_bucket_item *item = create_bucket_item();
|
||||
if (!item) {
|
||||
return B_ERR_NO_MEMORY;
|
||||
}
|
||||
|
||||
memcpy(&item->bi_key, key, sizeof *key);
|
||||
memcpy(&item->bi_value, value, sizeof *value);
|
||||
|
||||
b_queue_push_back(&bucket->bk_items, &item->bi_entry);
|
||||
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
const struct b_hashmap_value *b_hashmap_get(
|
||||
const struct b_hashmap *hashmap, const struct b_hashmap_key *key)
|
||||
{
|
||||
uint64_t hash = hash_data(key->key_data, key->key_size);
|
||||
struct b_hashmap_bucket *bucket = get_bucket(&hashmap->h_buckets, hash);
|
||||
if (!bucket) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
b_queue_iterator it;
|
||||
b_queue_foreach (&it, &bucket->bk_items) {
|
||||
struct b_hashmap_bucket_item *item = b_unbox(
|
||||
struct b_hashmap_bucket_item, it.entry, bi_entry);
|
||||
|
||||
if (item->bi_key.key_size != key->key_size) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!memcmp(item->bi_key.key_data, key->key_data, key->key_size)) {
|
||||
return &item->bi_value;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool b_hashmap_has_key(const struct b_hashmap *hashmap, const b_hashmap_key *key)
|
||||
{
|
||||
uint64_t hash = hash_data(key->key_data, key->key_size);
|
||||
struct b_hashmap_bucket *bucket = get_bucket(&hashmap->h_buckets, hash);
|
||||
if (!bucket) {
|
||||
return false;
|
||||
}
|
||||
|
||||
b_queue_iterator it;
|
||||
b_queue_foreach (&it, &bucket->bk_items) {
|
||||
struct b_hashmap_bucket_item *item = b_unbox(
|
||||
struct b_hashmap_bucket_item, it.entry, bi_entry);
|
||||
|
||||
if (item->bi_key.key_size != key->key_size) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!memcmp(item->bi_key.key_data, key->key_data, key->key_size)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t b_hashmap_get_size(const struct b_hashmap *hashmap)
|
||||
{
|
||||
size_t count = 0;
|
||||
b_btree_iterator it1;
|
||||
b_queue_iterator it2;
|
||||
b_btree_foreach (&it1, &hashmap->h_buckets) {
|
||||
struct b_hashmap_bucket *bucket
|
||||
= b_unbox(struct b_hashmap_bucket, it1.node, bk_node);
|
||||
|
||||
b_queue_foreach (&it2, &bucket->bk_items) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
bool b_hashmap_is_empty(const b_hashmap *hashmap)
|
||||
{
|
||||
b_btree_node *first_node = b_btree_first(&hashmap->h_buckets);
|
||||
struct b_hashmap_bucket *first_bucket
|
||||
= b_unbox(struct b_hashmap_bucket, first_node, bk_node);
|
||||
if (!first_bucket) {
|
||||
return true;
|
||||
}
|
||||
|
||||
b_queue_entry *first_entry = b_queue_first(&first_bucket->bk_items);
|
||||
struct b_hashmap_bucket_item *first_item
|
||||
= b_unbox(struct b_hashmap_bucket_item, first_entry, bi_entry);
|
||||
if (!first_item) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static b_status delete_item(
|
||||
struct b_hashmap *hashmap, struct b_hashmap_bucket *bucket,
|
||||
struct b_hashmap_bucket_item *item)
|
||||
{
|
||||
b_queue_delete(&bucket->bk_items, &item->bi_entry);
|
||||
|
||||
if (hashmap->h_key_dtor) {
|
||||
hashmap->h_key_dtor((void *)item->bi_key.key_data);
|
||||
}
|
||||
|
||||
if (hashmap->h_value_dtor) {
|
||||
hashmap->h_value_dtor((void *)item->bi_value.value_data);
|
||||
}
|
||||
|
||||
free(item);
|
||||
|
||||
if (b_queue_empty(&bucket->bk_items)) {
|
||||
b_btree_delete(&hashmap->h_buckets, &bucket->bk_node);
|
||||
free(bucket);
|
||||
}
|
||||
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
static bool hashmap_iterator_next(struct b_iterator *it)
|
||||
{
|
||||
return b_hashmap_iterator_next((struct b_hashmap_iterator *)it);
|
||||
}
|
||||
|
||||
static b_status hashmap_iterator_erase(struct b_iterator *it)
|
||||
{
|
||||
return b_hashmap_iterator_erase((struct b_hashmap_iterator *)it);
|
||||
}
|
||||
|
||||
static bool hashmap_iterator_is_valid(const struct b_iterator *it)
|
||||
{
|
||||
return b_hashmap_iterator_is_valid((struct b_hashmap_iterator *)it);
|
||||
}
|
||||
|
||||
static struct b_iterator_ops it_ops = {
|
||||
.it_next = hashmap_iterator_next,
|
||||
.it_erase = hashmap_iterator_erase,
|
||||
.it_close = NULL,
|
||||
.it_is_valid = hashmap_iterator_is_valid,
|
||||
};
|
||||
|
||||
int b_hashmap_iterator_begin(
|
||||
struct b_hashmap *hashmap, struct b_hashmap_iterator *it)
|
||||
{
|
||||
it->_h = hashmap;
|
||||
it->_base.it_ops = &it_ops;
|
||||
|
||||
it->i = 0;
|
||||
if (b_hashmap_is_empty(hashmap)) {
|
||||
it->key = NULL;
|
||||
it->value = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct b_btree_node *first_node = b_btree_first(&hashmap->h_buckets);
|
||||
struct b_hashmap_bucket *first_bucket
|
||||
= b_unbox(struct b_hashmap_bucket, first_node, bk_node);
|
||||
if (!first_bucket) {
|
||||
it->key = NULL;
|
||||
it->value = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct b_queue_entry *first_entry = b_queue_first(&first_bucket->bk_items);
|
||||
struct b_hashmap_bucket_item *first_item
|
||||
= b_unbox(struct b_hashmap_bucket_item, first_entry, bi_entry);
|
||||
if (!first_item) {
|
||||
it->key = NULL;
|
||||
it->value = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
it->key = &first_item->bi_key;
|
||||
it->value = &first_item->bi_value;
|
||||
|
||||
it->_cbn = first_node;
|
||||
it->_cqe = first_entry;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool get_next_node(
|
||||
struct b_btree_node *cur_node, struct b_queue_entry *cur_entry,
|
||||
struct b_btree_node **out_next_node, struct b_queue_entry **out_next_entry)
|
||||
{
|
||||
struct b_hashmap_bucket *cur_bucket
|
||||
= b_unbox(struct b_hashmap_bucket, cur_node, bk_node);
|
||||
if (!cur_bucket) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct b_hashmap_bucket_item *cur_item
|
||||
= b_unbox(struct b_hashmap_bucket_item, cur_entry, bi_entry);
|
||||
if (!cur_item) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct b_btree_node *next_node = cur_node;
|
||||
struct b_queue_entry *next_entry = b_queue_next(cur_entry);
|
||||
if (!next_entry) {
|
||||
next_node = b_btree_next(cur_node);
|
||||
if (!next_node) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct b_hashmap_bucket *next_bucket
|
||||
= b_unbox(struct b_hashmap_bucket, next_node, bk_node);
|
||||
if (!next_bucket) {
|
||||
return false;
|
||||
}
|
||||
|
||||
next_entry = b_queue_first(&next_bucket->bk_items);
|
||||
if (!next_entry) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
struct b_hashmap_bucket_item *next_item
|
||||
= b_unbox(struct b_hashmap_bucket_item, next_entry, bi_entry);
|
||||
if (!next_item) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*out_next_node = next_node;
|
||||
*out_next_entry = next_entry;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool b_hashmap_iterator_next(struct b_hashmap_iterator *it)
|
||||
{
|
||||
struct b_btree_node *next_node;
|
||||
struct b_queue_entry *next_entry;
|
||||
if (!get_next_node(it->_cbn, it->_cqe, &next_node, &next_entry)) {
|
||||
it->key = NULL;
|
||||
it->value = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
struct b_hashmap_bucket_item *next_item
|
||||
= b_unbox(struct b_hashmap_bucket_item, next_entry, bi_entry);
|
||||
|
||||
if (!next_item) {
|
||||
it->key = NULL;
|
||||
it->value = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
it->i++;
|
||||
it->key = &next_item->bi_key;
|
||||
it->value = &next_item->bi_value;
|
||||
|
||||
it->_cbn = next_node;
|
||||
it->_cqe = next_entry;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
b_status b_hashmap_iterator_erase(struct b_hashmap_iterator *it)
|
||||
{
|
||||
if ((it->key || it->value) && !(it->_cbn && it->_cqe)) {
|
||||
return B_ERR_BAD_STATE;
|
||||
}
|
||||
|
||||
if (!it->key || !it->_cqe) {
|
||||
return B_ERR_OUT_OF_BOUNDS;
|
||||
}
|
||||
|
||||
struct b_btree_node *next_node;
|
||||
struct b_queue_entry *next_entry;
|
||||
if (!get_next_node(it->_cbn, it->_cqe, &next_node, &next_entry)) {
|
||||
it->key = NULL;
|
||||
it->value = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
struct b_hashmap_bucket *cur_bucket
|
||||
= b_unbox(struct b_hashmap_bucket, it->_cbn, bk_node);
|
||||
struct b_hashmap_bucket_item *cur_item
|
||||
= b_unbox(struct b_hashmap_bucket_item, it->_cqe, bi_entry);
|
||||
|
||||
struct b_hashmap_bucket_item *next_item
|
||||
= b_unbox(struct b_hashmap_bucket_item, next_entry, bi_entry);
|
||||
|
||||
b_status status = delete_item(it->_h, cur_bucket, cur_item);
|
||||
if (B_ERR(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
if (next_item) {
|
||||
it->key = &next_item->bi_key;
|
||||
it->value = &next_item->bi_value;
|
||||
|
||||
it->_cbn = next_node;
|
||||
it->_cqe = next_entry;
|
||||
} else {
|
||||
it->key = NULL;
|
||||
it->value = NULL;
|
||||
|
||||
it->_cbn = NULL;
|
||||
it->_cqe = NULL;
|
||||
}
|
||||
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
bool b_hashmap_iterator_is_valid(const struct b_hashmap_iterator *it)
|
||||
{
|
||||
return it->key != NULL;
|
||||
}
|
||||
|
||||
static void hashmap_release(struct b_dsref *obj)
|
||||
{
|
||||
struct b_hashmap *map = B_HASHMAP(obj);
|
||||
|
||||
b_hashmap_iterator it;
|
||||
b_hashmap_iterator_begin(map, &it);
|
||||
while (b_hashmap_iterator_is_valid(&it)) {
|
||||
b_hashmap_iterator_erase(&it);
|
||||
}
|
||||
}
|
||||
|
||||
b_dsref_type_id b_hashmap_type_id(void)
|
||||
{
|
||||
return (b_dsref_type_id)&hashmap_type;
|
||||
}
|
||||
27
ds/hashmap.h
Normal file
27
ds/hashmap.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#ifndef _B_HASHMAP_H_
|
||||
#define _B_HASHMAP_H_
|
||||
|
||||
#include <blue/core/btree.h>
|
||||
#include <blue/core/queue.h>
|
||||
#include <blue/ds/hashmap.h>
|
||||
|
||||
struct b_hashmap_bucket_item {
|
||||
struct b_queue_entry bi_entry;
|
||||
struct b_hashmap_key bi_key;
|
||||
struct b_hashmap_value bi_value;
|
||||
};
|
||||
|
||||
struct b_hashmap_bucket {
|
||||
struct b_btree_node bk_node;
|
||||
uint64_t bk_hash;
|
||||
struct b_queue bk_items;
|
||||
};
|
||||
|
||||
struct b_hashmap {
|
||||
struct b_dsref h_base;
|
||||
struct b_btree h_buckets;
|
||||
b_hashmap_key_destructor h_key_dtor;
|
||||
b_hashmap_value_destructor h_value_dtor;
|
||||
};
|
||||
|
||||
#endif
|
||||
0
ds/include/blue/ds.h
Normal file
0
ds/include/blue/ds.h
Normal file
304
ds/include/blue/ds/array.h
Normal file
304
ds/include/blue/ds/array.h
Normal file
@@ -0,0 +1,304 @@
|
||||
#ifndef BLUELIB_ARRAY_H_
|
||||
#define BLUELIB_ARRAY_H_
|
||||
|
||||
#include <blue/core/iterator.h>
|
||||
#include <blue/core/misc.h>
|
||||
#include <blue/core/status.h>
|
||||
#include <blue/ds/object.h>
|
||||
#include <blue/ds/type.h>
|
||||
|
||||
/**
|
||||
* Cast a generic b_dsref pointer to an b_array pointer.
|
||||
*/
|
||||
#define B_ARRAY(p) ((b_array *)(p))
|
||||
|
||||
/**
|
||||
* Iterate through each object in an b_array.
|
||||
*
|
||||
* This should be used for read-only iterations only. Adding or removing objects
|
||||
* while iterating though an array using b_array_foreach is NOT supported.
|
||||
*
|
||||
* @param it A pointer to an b_array_iterator. This iterator will contain the
|
||||
* current object reference for the current loop iteration.
|
||||
* @param array A pointer to the b_array to iterate over.
|
||||
*/
|
||||
#define b_array_foreach(it, array) \
|
||||
for (int z__b_unique_name() = b_array_iterator_begin(array, it); \
|
||||
(it)->value != NULL; b_array_iterator_next(it))
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* A heterogeneous array of objects. b_array only stores references
|
||||
* to the objects that it contains, not the object data itself.
|
||||
*
|
||||
* b_array stores pointers to objects in a single contiguous array,
|
||||
* but this is an implementation detail that may change in the future.
|
||||
* Users of b_array should not rely on this being the case.
|
||||
*/
|
||||
typedef struct b_array b_array;
|
||||
|
||||
/**
|
||||
* Iterator for traversing the contents of an b_array.
|
||||
*
|
||||
* The iterator provides the current b_dsref `value`, as well
|
||||
* as the index `i` of that value within the array.
|
||||
*
|
||||
* Any members whose names begin with _ (underscore) are reserved
|
||||
* and should not be accessed.
|
||||
*/
|
||||
typedef struct b_array_iterator {
|
||||
b_iterator _base;
|
||||
b_array *_a;
|
||||
|
||||
/** The index of the current value */
|
||||
size_t i;
|
||||
/** The current value */
|
||||
b_dsref *value;
|
||||
} b_array_iterator;
|
||||
|
||||
/**
|
||||
* Creates an empty b_array.
|
||||
*
|
||||
* @return A pointer to the new array, or NULL if an error occurred.
|
||||
*/
|
||||
BLUE_API b_array *b_array_create(void);
|
||||
|
||||
/**
|
||||
* Creates an b_array initialised with the contents of the provided b_dsref
|
||||
* pointer array. The b_array will take a reference to each object specified in
|
||||
* `values`, and will increment the reference count. The order of objects in the
|
||||
* new b_array will be the same as the order of objects in `values`. Any NULL
|
||||
* pointers in the `values` array will be ignored, and will not result in gaps
|
||||
* in the created b_array. However, `nr_values` should be large enough to cover
|
||||
* the final non-NULL pointer in `values`, including any NULL pointers
|
||||
* in-between.
|
||||
*
|
||||
* @param values The list of object pointers which should make up the contents
|
||||
* of the new b_array.
|
||||
* @param nr_values The size of the `values` array.
|
||||
* @return A pointer to the new b_array, or NULL if an error occurred.
|
||||
*/
|
||||
BLUE_API b_array *b_array_create_with_values(
|
||||
b_dsref *const *values, size_t nr_values);
|
||||
|
||||
/**
|
||||
* Increment the reference counter of an b_array.
|
||||
*
|
||||
* @param array The b_array to reference.
|
||||
* @return The b_array pointer that was passed to the function.
|
||||
*/
|
||||
static inline b_array *b_array_retain(b_array *array)
|
||||
{
|
||||
return B_ARRAY(b_retain(B_DSREF(array)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrement the reference counter of an b_array, destroying the array if it reaches zero.
|
||||
* @param array The b_array reference to release.
|
||||
*/
|
||||
static inline void b_array_release(b_array *array)
|
||||
{
|
||||
b_release(B_DSREF(array));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all object references from an b_array, resetting the size of the array to zero.
|
||||
* The reference counts of all objects in the array will be decremented.
|
||||
*
|
||||
* @param array The b_array to clear.
|
||||
*/
|
||||
BLUE_API void b_array_clear(b_array *array);
|
||||
|
||||
/**
|
||||
* Inserts an object at the end of an b_array. The reference count of the object
|
||||
* will be incremented.
|
||||
*
|
||||
* @param array The b_array to append the object to.
|
||||
* @param value The object to append.
|
||||
* @return B_SUCCESS if the object was appended successfully, or an error code if an error occurred.
|
||||
*/
|
||||
BLUE_API b_status b_array_append(b_array *array, b_dsref *value);
|
||||
|
||||
/**
|
||||
* Inserts an object at the beginning of an b_array. The reference count of the object
|
||||
* will be incremented. All other objects in the array will be moved to make space
|
||||
* for the object being pre-pended.
|
||||
*
|
||||
* @param array The b_array to prepend the object to.
|
||||
* @param value The object to prepend.
|
||||
* @return B_SUCCESS if the object was prepended successfully, or an error code if an error occurred.
|
||||
*/
|
||||
BLUE_API b_status b_array_prepend(b_array *array, b_dsref *value);
|
||||
|
||||
/**
|
||||
* Inserts an object into an b_array at a given index. The reference count of the object
|
||||
* will be incremented. If the specified index is at the beginning or mid-way through
|
||||
* the array (i.e. not at the end), some or all of the objects already in the array will
|
||||
* be moved to make space for the object being inserted.
|
||||
*
|
||||
* @param array The b_array to insert the object into.
|
||||
* @param value The object to insert.
|
||||
* @param at The index to insert the object at. If the index is `B_NPOS`, the object will
|
||||
* be inserted at the end of the b_array.
|
||||
* @return B_SUCCESS if the object was inserted, or a status code describing any error that occurred.
|
||||
*/
|
||||
BLUE_API b_status b_array_insert(b_array *array, b_dsref *value, size_t at);
|
||||
|
||||
/**
|
||||
* Removes the object at the specified index from an b_array. The reference count
|
||||
* of the removed object will be decremented. If the specified index is at the beginning
|
||||
* or mid-way through the array (i.e. not at the end), the remaining objects will be moved
|
||||
* to fill the empty space created by the object's removal.
|
||||
*
|
||||
* @param array The b_array to remove the object from.
|
||||
* @param at The index of the object to be removed.
|
||||
* @return B_SUCCESS if the object was removed, or a status code describing any error that occurred.
|
||||
*/
|
||||
BLUE_API b_status b_array_remove(b_array *array, size_t at);
|
||||
|
||||
/**
|
||||
* Removes the object at the beginning of an b_array. The reference count
|
||||
* of the removed object will be decremented. The remaining objects will be moved
|
||||
* to fill the empty space created by the object's removal.
|
||||
*
|
||||
* @param array The b_array to remove the object from.
|
||||
* @return B_SUCCESS if the object was removed, or a status code describing any error that occurred.
|
||||
*/
|
||||
BLUE_API b_status b_array_remove_front(b_array *array);
|
||||
|
||||
/**
|
||||
* Removes the object at the end of an b_array. The reference count
|
||||
* of the removed object will be decremented.
|
||||
*
|
||||
* @param array The b_array to remove the object from.
|
||||
* @return B_SUCCESS if the object was removed, or a status code describing any error that occurred.
|
||||
*/
|
||||
BLUE_API b_status b_array_remove_back(b_array *array);
|
||||
|
||||
/**
|
||||
* Removes the object at the specified index of an b_array, and returns a
|
||||
* pointer to it. The reference count of the removed object will NOT be
|
||||
* decremented. The caller becomes the owner of the array's reference to the
|
||||
* object. If the specified index is at the beginning or mid-way through the
|
||||
* array (i.e. not at the end), the remaining objects will be moved to fill the
|
||||
* empty space created by the object's removal.
|
||||
*
|
||||
* @param array The b_array to remove the object from.
|
||||
* @param at The index of the object to be removed.
|
||||
* @return An pointer to the removed object. This pointer is owned by the
|
||||
* caller. Returns NULL if an error occurred.
|
||||
*/
|
||||
BLUE_API b_dsref *b_array_pop(b_array *array, size_t at);
|
||||
|
||||
/**
|
||||
* Removes the object at the beginning of an b_array, and returns a pointer to
|
||||
* it. The reference count of the removed object will NOT be decremented. The
|
||||
* caller becomes the owner of the array's reference to the object. The
|
||||
* remaining objects in the b_array will be moved to fill the empty space left
|
||||
* by the removed object.
|
||||
*
|
||||
* @param array The b_array to remove the object from.
|
||||
* @return An pointer to the removed object. This pointer is owned by the
|
||||
* caller. Returns NULL if an error occurred.
|
||||
*/
|
||||
BLUE_API b_dsref *b_array_pop_front(b_array *array);
|
||||
|
||||
/**
|
||||
* Removes the object at the end of an b_array, and returns a pointer to it. The
|
||||
* reference count of the removed object will NOT be decremented. The caller
|
||||
* becomes the owner of the array's reference to the object.
|
||||
*
|
||||
* @param array The b_array to remove the object from.
|
||||
* @return An pointer to the removed object. This pointer is owned by the
|
||||
* caller. Returns NULL if an error occurred.
|
||||
*/
|
||||
BLUE_API b_dsref *b_array_pop_back(b_array *array);
|
||||
|
||||
/**
|
||||
* Returns an unowned pointer to the object at the given index of an b_array.
|
||||
* The caller does not own the returned pointer, and MUST NOT release it.
|
||||
*
|
||||
* @param array The b_array.
|
||||
* @param at The index of the object to return.
|
||||
* @return A pointer to the object at the given index. This pointer is NOT owned
|
||||
* by the caller. Returns NULL if an error occurred.
|
||||
*/
|
||||
BLUE_API b_dsref *b_array_at(const b_array *array, size_t at);
|
||||
|
||||
/**
|
||||
* Returns an owned pointer to the object at the given index of an b_array. The caller owns
|
||||
* the returned pointer, and must release it when they are finished with it.
|
||||
*
|
||||
* @param array The b_array.
|
||||
* @param at The index of the object to return.
|
||||
* @return A pointer to the object at the given index. This pointer is owned by the caller.
|
||||
* Returns NULL if an error occurred.
|
||||
*/
|
||||
BLUE_API b_dsref *b_array_get(b_array *array, size_t at);
|
||||
|
||||
/**
|
||||
* Returns the number of objects contained in an b_array.
|
||||
*
|
||||
* @param array The b_array.
|
||||
* @return The number of objects contained in the b_array.
|
||||
*/
|
||||
BLUE_API size_t b_array_size(const b_array *array);
|
||||
|
||||
/**
|
||||
* Returns the current maximum capacity of an b_array. This represents the
|
||||
* number of objects that can be stored in an b_array before its internal buffer
|
||||
* would need to be re-sized.
|
||||
*
|
||||
* @param array The b_array.
|
||||
* @return The maximum capacity of the b_array.
|
||||
*/
|
||||
BLUE_API size_t b_array_capacity(const b_array *array);
|
||||
|
||||
/**
|
||||
* Initialise an b_array_iterator to pointer to the first object in an b_array.
|
||||
* If the b_array is empty, then `it` will be an invalid iterator.
|
||||
*
|
||||
* @param array
|
||||
* @param it
|
||||
* @return Always returns 0.
|
||||
*/
|
||||
BLUE_API int b_array_iterator_begin(b_array *array, b_array_iterator *it);
|
||||
|
||||
/**
|
||||
* Advances an b_array_iterator to pointer to the next object in an b_array.
|
||||
* @param it The iterator to advance.
|
||||
* @return True if the iterator contains a valid reference to an object, or
|
||||
* False if the iterator has gone past the end of the array.
|
||||
*/
|
||||
BLUE_API bool b_array_iterator_next(b_array_iterator *it);
|
||||
|
||||
/**
|
||||
* Removes the object pointed to by an b_array_iterator from its container
|
||||
* b_array, and advances the iterator to the next object in the b_array.
|
||||
*
|
||||
* The reference count of the removed object will be decremented.
|
||||
*
|
||||
* @param it The iterator whose object should be removed.
|
||||
* @return B_SUCCESS if the object was removed, or a status code describing the error that occurred.
|
||||
*/
|
||||
BLUE_API b_status b_array_iterator_erase(b_array_iterator *it);
|
||||
|
||||
/**
|
||||
* Checks whether or not an iterator contains a valid reference to an object.
|
||||
* An iterator will become invalid if it has moved past the end of the b_array
|
||||
* it was iterating across, or if b_array_iterator_erase() was called on an
|
||||
* iterator pointing to the last object in an b_array.
|
||||
*
|
||||
* @param it The iterator to check.
|
||||
* @return True if the iterator is valid. False otherwise.
|
||||
*/
|
||||
BLUE_API bool b_array_iterator_is_valid(const b_array_iterator *it);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
30
ds/include/blue/ds/bitbuffer.h
Normal file
30
ds/include/blue/ds/bitbuffer.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#ifndef BLUE_OBJECT_BITBUFFER_H_
|
||||
#define BLUE_OBJECT_BITBUFFER_H_
|
||||
|
||||
#include <blue/ds/object.h>
|
||||
|
||||
#define B_BITBUFFER(p) ((b_bitbuffer *)(p))
|
||||
|
||||
typedef struct b_bitbuffer b_bitbuffer;
|
||||
|
||||
BLUE_API b_bitbuffer *b_bitbuffer_create(void);
|
||||
|
||||
static inline b_bitbuffer *b_bitbuffer_retain(b_bitbuffer *buf)
|
||||
{
|
||||
return B_BITBUFFER(b_retain(B_DSREF(buf)));
|
||||
}
|
||||
static inline void b_bitbuffer_release(b_bitbuffer *buf)
|
||||
{
|
||||
b_release(B_DSREF(buf));
|
||||
}
|
||||
|
||||
BLUE_API b_status b_bitbuffer_put_bit(b_bitbuffer *buf, int bit);
|
||||
BLUE_API b_status b_bitbuffer_put_bool(b_bitbuffer *buf, bool b);
|
||||
BLUE_API b_status b_bitbuffer_put_int(
|
||||
b_bitbuffer *buf, uint64_t v, unsigned int nr_bits);
|
||||
BLUE_API b_status b_bitbuffer_put_bytes(
|
||||
b_bitbuffer *buf, const void *p, size_t len, size_t bits_per_byte);
|
||||
BLUE_API b_status b_bitbuffer_put_string(
|
||||
b_bitbuffer *buf, const char *p, size_t len, size_t bits_per_char);
|
||||
|
||||
#endif
|
||||
35
ds/include/blue/ds/bitmap.h
Normal file
35
ds/include/blue/ds/bitmap.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#ifndef BLUELIB_BITMAP_H_
|
||||
#define BLUELIB_BITMAP_H_
|
||||
|
||||
#include <blue/core/misc.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef unsigned long b_bitmap_word;
|
||||
|
||||
#define Z__B_BITS_PER_WORD (8 * sizeof(b_bitmap_word))
|
||||
#define Z__B_DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
|
||||
#define B_BITMAP_WORDS(nbits) Z__B_DIV_ROUND_UP(nbits, Z__B_BITS_PER_WORD)
|
||||
#define B_BITMAP_NPOS ((unsigned int)-1)
|
||||
|
||||
#define B_DECLARE_BITMAP(name, nbits) b_bitmap_word name[B_BITMAP_WORDS(nbits)]
|
||||
|
||||
BLUE_API void b_bitmap_zero(b_bitmap_word *map, unsigned long nbits);
|
||||
BLUE_API void b_bitmap_fill(b_bitmap_word *map, unsigned long nbits);
|
||||
BLUE_API void b_bitmap_set(b_bitmap_word *map, unsigned long bit);
|
||||
BLUE_API void b_bitmap_clear(b_bitmap_word *map, unsigned long bit);
|
||||
BLUE_API bool b_bitmap_check(const b_bitmap_word *map, unsigned long bit);
|
||||
|
||||
BLUE_API unsigned int b_bitmap_count_set(const b_bitmap_word *map, unsigned long nbits);
|
||||
BLUE_API unsigned int b_bitmap_count_clear(const b_bitmap_word *map, unsigned long nbits);
|
||||
|
||||
BLUE_API unsigned int b_bitmap_highest_set(const b_bitmap_word *map, unsigned long nbits);
|
||||
BLUE_API unsigned int b_bitmap_highest_clear(const b_bitmap_word *map, unsigned long nbits);
|
||||
BLUE_API unsigned int b_bitmap_lowest_set(const b_bitmap_word *map, unsigned long nbits);
|
||||
BLUE_API unsigned int b_bitmap_lowest_clear(const b_bitmap_word *map, unsigned long nbits);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
49
ds/include/blue/ds/buffer.h
Normal file
49
ds/include/blue/ds/buffer.h
Normal file
@@ -0,0 +1,49 @@
|
||||
#ifndef BLUELIB_BUFFER_H_
|
||||
#define BLUELIB_BUFFER_H_
|
||||
|
||||
#include <blue/ds/object.h>
|
||||
#include <blue/ds/type.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#define B_BUFFER(p) ((b_buffer *)(p))
|
||||
|
||||
typedef struct b_buffer b_buffer;
|
||||
|
||||
BLUE_API b_buffer *b_buffer_create(size_t item_sz);
|
||||
BLUE_API b_buffer *b_buffer_create_from_bytes(const void *p, size_t len);
|
||||
BLUE_API b_buffer *b_buffer_create_from_array(
|
||||
const void *p, size_t item_sz, size_t len);
|
||||
|
||||
static inline b_buffer *b_buffer_retain(b_buffer *buf)
|
||||
{
|
||||
return B_BUFFER(b_retain(B_DSREF(buf)));
|
||||
}
|
||||
static inline void b_buffer_release(b_buffer *buf)
|
||||
{
|
||||
b_release(B_DSREF(buf));
|
||||
}
|
||||
BLUE_API void *b_buffer_steal(b_buffer *buf);
|
||||
BLUE_API b_status b_buffer_reserve(b_buffer *buf, size_t capacity);
|
||||
BLUE_API b_status b_buffer_resize(b_buffer *buf, size_t length);
|
||||
|
||||
BLUE_API b_status b_buffer_append(b_buffer *dest, const void *p, size_t count);
|
||||
BLUE_API b_status b_buffer_prepend(b_buffer *dest, const void *p, size_t count);
|
||||
BLUE_API b_status b_buffer_insert(
|
||||
b_buffer *dest, const void *p, size_t count, size_t at);
|
||||
BLUE_API b_status b_buffer_remove(b_buffer *dest, size_t at, size_t count);
|
||||
BLUE_API b_status b_buffer_clear(b_buffer *buf);
|
||||
|
||||
BLUE_API b_status b_buffer_push_back(b_buffer *buf, size_t count, void **p);
|
||||
BLUE_API b_status b_buffer_push_front(b_buffer *buf, size_t count, void **p);
|
||||
|
||||
BLUE_API b_status b_buffer_pop_back(b_buffer *buf, size_t count);
|
||||
BLUE_API b_status b_buffer_pop_front(b_buffer *buf, size_t count);
|
||||
|
||||
BLUE_API size_t b_buffer_get_size(const b_buffer *buf);
|
||||
BLUE_API size_t b_buffer_get_item_size(const b_buffer *buf);
|
||||
BLUE_API size_t b_buffer_get_capacity(const b_buffer *buf);
|
||||
|
||||
BLUE_API void *b_buffer_ptr(const b_buffer *buf);
|
||||
BLUE_API void *b_buffer_get(const b_buffer *buf, size_t at);
|
||||
|
||||
#endif
|
||||
69
ds/include/blue/ds/dict.h
Normal file
69
ds/include/blue/ds/dict.h
Normal file
@@ -0,0 +1,69 @@
|
||||
#ifndef BLUELIB_DICT_H_
|
||||
#define BLUELIB_DICT_H_
|
||||
|
||||
#include <blue/core/btree.h>
|
||||
#include <blue/core/misc.h>
|
||||
#include <blue/core/queue.h>
|
||||
#include <blue/core/status.h>
|
||||
#include <blue/ds/object.h>
|
||||
#include <blue/ds/type.h>
|
||||
|
||||
#define B_DICT(p) ((b_dict *)(p))
|
||||
|
||||
#define B_DICT_ITEM(k, v) \
|
||||
{ \
|
||||
.key = (k), .value = (v) \
|
||||
}
|
||||
#define B_DICT_ITEM_END \
|
||||
{ \
|
||||
.key = NULL, .value = NULL \
|
||||
}
|
||||
|
||||
#define b_dict_foreach(it, dict) \
|
||||
for (int z__b_unique_name() = b_dict_iterator_begin(dict, it); \
|
||||
(it)->key != NULL; b_dict_iterator_next(it))
|
||||
|
||||
typedef struct b_dict b_dict;
|
||||
|
||||
typedef struct b_dict_iterator {
|
||||
b_iterator _base;
|
||||
size_t i;
|
||||
const char *key;
|
||||
b_dsref *value;
|
||||
|
||||
b_dict *_d;
|
||||
b_btree_node *_cbn;
|
||||
b_queue_entry *_cqe;
|
||||
} b_dict_iterator;
|
||||
|
||||
typedef struct b_dict_item {
|
||||
const char *key;
|
||||
b_dsref *value;
|
||||
} b_dict_item;
|
||||
|
||||
BLUE_API b_dict *b_dict_create(void);
|
||||
BLUE_API b_dict *b_dict_create_with_items(const b_dict_item *items);
|
||||
|
||||
static inline b_dict *b_dict_retain(b_dict *dict)
|
||||
{
|
||||
return B_DICT(b_retain(B_DSREF(dict)));
|
||||
}
|
||||
static inline void b_dict_release(b_dict *dict)
|
||||
{
|
||||
b_release(B_DSREF(dict));
|
||||
}
|
||||
|
||||
BLUE_API b_status b_dict_put(b_dict *dict, const char *key, b_dsref *value);
|
||||
BLUE_API b_dsref *b_dict_at(const b_dict *dict, const char *key);
|
||||
BLUE_API b_dsref *b_dict_get(b_dict *dict, const char *key);
|
||||
|
||||
BLUE_API bool b_dict_has_key(const b_dict *dict, const char *key);
|
||||
BLUE_API size_t b_dict_get_size(const b_dict *dict);
|
||||
BLUE_API bool b_dict_is_empty(const b_dict *dict);
|
||||
|
||||
BLUE_API int b_dict_iterator_begin(b_dict *dict, b_dict_iterator *it);
|
||||
BLUE_API bool b_dict_iterator_next(b_dict_iterator *it);
|
||||
BLUE_API b_status b_dict_iterator_erase(b_dict_iterator *it);
|
||||
BLUE_API bool b_dict_iterator_is_valid(const b_dict_iterator *it);
|
||||
|
||||
#endif
|
||||
94
ds/include/blue/ds/hashmap.h
Normal file
94
ds/include/blue/ds/hashmap.h
Normal file
@@ -0,0 +1,94 @@
|
||||
#ifndef BLUELIB_HASHMAP_H_
|
||||
#define BLUELIB_HASHMAP_H_
|
||||
|
||||
#include <blue/core/btree.h>
|
||||
#include <blue/core/misc.h>
|
||||
#include <blue/core/queue.h>
|
||||
#include <blue/core/status.h>
|
||||
#include <blue/ds/object.h>
|
||||
#include <blue/ds/type.h>
|
||||
|
||||
#define B_HASHMAP(p) ((b_hashmap *)(p))
|
||||
|
||||
#define B_HASHMAP_KEY(k, ks) \
|
||||
{ \
|
||||
.key_data = (k), .key_size = (ks) \
|
||||
}
|
||||
#define B_HASHMAP_VALUE(v, vs) \
|
||||
{ \
|
||||
.value_data = (v), .value_size = (vs) \
|
||||
}
|
||||
|
||||
#define B_HASHMAP_ITEM(k, ks, v, vs) \
|
||||
{ \
|
||||
.key = B_HASHMAP_KEY(k, ks), .value = B_HASHMAP_VALUE(v, vs) \
|
||||
}
|
||||
|
||||
#define B_HASHMAP_ITEM_END \
|
||||
{ \
|
||||
.key = {0}, .value = { 0 } \
|
||||
}
|
||||
|
||||
#define b_hashmap_foreach(it, hashmap) \
|
||||
for (int z__b_unique_name() = b_hashmap_iterator_begin(hashmap, it); \
|
||||
(it)->key != NULL; b_hashmap_iterator_next(it))
|
||||
|
||||
typedef struct b_hashmap b_hashmap;
|
||||
|
||||
typedef void (*b_hashmap_key_destructor)(void *);
|
||||
typedef void (*b_hashmap_value_destructor)(void *);
|
||||
|
||||
typedef struct b_hashmap_key {
|
||||
const void *key_data;
|
||||
size_t key_size;
|
||||
} b_hashmap_key;
|
||||
|
||||
typedef struct b_hashmap_value {
|
||||
void *value_data;
|
||||
size_t value_size;
|
||||
} b_hashmap_value;
|
||||
|
||||
typedef struct b_hashmap_item {
|
||||
b_hashmap_key key;
|
||||
b_hashmap_value value;
|
||||
} b_hashmap_item;
|
||||
|
||||
typedef struct b_hashmap_iterator {
|
||||
b_iterator _base;
|
||||
size_t i;
|
||||
const b_hashmap_key *key;
|
||||
const b_hashmap_value *value;
|
||||
|
||||
b_hashmap *_h;
|
||||
b_btree_node *_cbn;
|
||||
b_queue_entry *_cqe;
|
||||
} b_hashmap_iterator;
|
||||
|
||||
BLUE_API b_hashmap *b_hashmap_create(
|
||||
b_hashmap_key_destructor key_dtor, b_hashmap_value_destructor value_dtor);
|
||||
BLUE_API b_hashmap *b_hashmap_create_with_items(const b_hashmap_item *items);
|
||||
|
||||
static inline b_hashmap *b_hashmap_retain(b_hashmap *hashmap)
|
||||
{
|
||||
return B_HASHMAP(b_retain(B_DSREF(hashmap)));
|
||||
}
|
||||
static inline void b_hashmap_release(b_hashmap *hashmap)
|
||||
{
|
||||
b_release(B_DSREF(hashmap));
|
||||
}
|
||||
|
||||
BLUE_API b_status b_hashmap_put(
|
||||
b_hashmap *hashmap, const b_hashmap_key *key, const b_hashmap_value *value);
|
||||
BLUE_API const b_hashmap_value *b_hashmap_get(
|
||||
const b_hashmap *hashmap, const b_hashmap_key *key);
|
||||
|
||||
BLUE_API bool b_hashmap_has_key(const b_hashmap *hashmap, const b_hashmap_key *key);
|
||||
BLUE_API size_t b_hashmap_get_size(const b_hashmap *hashmap);
|
||||
BLUE_API bool b_hashmap_is_empty(const b_hashmap *hashmap);
|
||||
|
||||
BLUE_API int b_hashmap_iterator_begin(b_hashmap *hashmap, b_hashmap_iterator *it);
|
||||
BLUE_API bool b_hashmap_iterator_next(b_hashmap_iterator *it);
|
||||
BLUE_API b_status b_hashmap_iterator_erase(b_hashmap_iterator *it);
|
||||
BLUE_API bool b_hashmap_iterator_is_valid(const b_hashmap_iterator *it);
|
||||
|
||||
#endif
|
||||
67
ds/include/blue/ds/list.h
Normal file
67
ds/include/blue/ds/list.h
Normal file
@@ -0,0 +1,67 @@
|
||||
#ifndef BLUE_OBJECT_LIST_H_
|
||||
#define BLUE_OBJECT_LIST_H_
|
||||
|
||||
#include <blue/core/status.h>
|
||||
#include <blue/ds/object.h>
|
||||
|
||||
#define B_LIST(p) ((b_list *)(p))
|
||||
|
||||
typedef struct b_list b_list;
|
||||
typedef struct b_list_entry b_list_entry;
|
||||
|
||||
#define b_list_foreach(it, q) \
|
||||
for (int z__b_unique_name() = b_list_iterator_begin(q, it); \
|
||||
(it)->entry != NULL; b_list_iterator_next(it))
|
||||
|
||||
typedef struct b_list_iterator {
|
||||
b_queue_iterator _base;
|
||||
const b_list *_q;
|
||||
size_t i;
|
||||
void *item;
|
||||
b_list_entry *entry;
|
||||
} b_list_iterator;
|
||||
|
||||
BLUE_API b_list *b_list_create(void);
|
||||
|
||||
static inline b_list *b_list_retain(b_list *str)
|
||||
{
|
||||
return B_LIST(b_retain(B_DSREF(str)));
|
||||
}
|
||||
static inline void b_list_release(b_list *str)
|
||||
{
|
||||
b_release(B_DSREF(str));
|
||||
}
|
||||
|
||||
BLUE_API bool b_list_empty(b_list *q);
|
||||
BLUE_API void *b_list_first_item(const b_list *q);
|
||||
BLUE_API void *b_list_last_item(const b_list *q);
|
||||
BLUE_API b_list_entry *b_list_first_entry(const b_list *q);
|
||||
BLUE_API b_list_entry *b_list_last_entry(const b_list *q);
|
||||
BLUE_API b_list_entry *b_list_next(const b_list_entry *entry);
|
||||
BLUE_API b_list_entry *b_list_prev(const b_list_entry *entry);
|
||||
|
||||
BLUE_API size_t b_list_length(const b_list *q);
|
||||
|
||||
BLUE_API b_list_entry *b_list_insert_before(
|
||||
b_list *q, void *ptr, b_list_entry *before);
|
||||
BLUE_API b_list_entry *b_list_insert_after(
|
||||
b_list *q, void *ptr, b_list_entry *after);
|
||||
|
||||
BLUE_API b_list_entry *b_list_push_front(b_list *q, void *ptr);
|
||||
BLUE_API b_list_entry *b_list_push_back(b_list *q, void *ptr);
|
||||
|
||||
BLUE_API void *b_list_pop_front(b_list *q);
|
||||
BLUE_API void *b_list_pop_back(b_list *q);
|
||||
|
||||
BLUE_API b_status b_list_delete_item(b_list *q, void *ptr);
|
||||
BLUE_API b_status b_list_delete_entry(b_list *q, b_list_entry *entry);
|
||||
BLUE_API void b_list_delete_all(b_list *q);
|
||||
|
||||
BLUE_API int b_list_iterator_begin(const b_list *q, b_list_iterator *it);
|
||||
BLUE_API bool b_list_iterator_next(b_list_iterator *it);
|
||||
BLUE_API b_status b_list_iterator_erase(b_list_iterator *it);
|
||||
BLUE_API bool b_list_iterator_is_valid(const b_list_iterator *it);
|
||||
|
||||
BLUE_API void *b_list_entry_value(const b_list_entry *entry);
|
||||
|
||||
#endif
|
||||
243
ds/include/blue/ds/number.h
Normal file
243
ds/include/blue/ds/number.h
Normal file
@@ -0,0 +1,243 @@
|
||||
#ifndef BLUELIB_NUMBER_H
|
||||
#define BLUELIB_NUMBER_H
|
||||
|
||||
#include <blue/ds/object.h>
|
||||
#include <blue/ds/type.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define B_NUMBER(p) ((b_number *)(p))
|
||||
|
||||
#define B_INT8(v) (b_number_create_int8(v))
|
||||
#define B_INT16(v) (b_number_create_int16(v))
|
||||
#define B_INT32(v) (b_number_create_int32(v))
|
||||
#define B_INT64(v) (b_number_create_int64(v))
|
||||
#define B_FLOAT32(v) (b_number_create_float32(v))
|
||||
#define B_FLOAT64(v) (b_number_create_float64(v))
|
||||
#define B_CHAR(v) (b_number_create_char(v))
|
||||
#define B_SHORT(v) (b_number_create_short(v))
|
||||
#define B_INT(v) (b_number_create_int(v))
|
||||
#define B_LONG(v) (b_number_create_long(v))
|
||||
#define B_LONGLONG(v) (b_number_create_longlong(v))
|
||||
#define B_FLOAT(v) (b_number_create_float(v))
|
||||
#define B_DOUBLE(v) (b_number_create_double(v))
|
||||
#define B_SIZE_T(v) (b_number_create_size_t(v))
|
||||
|
||||
#define B_RV_INT8(v) B_RV(b_number_create_int8(v))
|
||||
#define B_RV_INT16(v) B_RV(b_number_create_int16(v))
|
||||
#define B_RV_INT32(v) B_RV(b_number_create_int32(v))
|
||||
#define B_RV_INT64(v) B_RV(b_number_create_int64(v))
|
||||
#define B_RV_FLOAT32(v) B_RV(b_number_create_float32(v))
|
||||
#define B_RV_FLOAT64(v) B_RV(b_number_create_float64(v))
|
||||
#define B_RV_CHAR(v) B_RV(b_number_create_char(v))
|
||||
#define B_RV_SHORT(v) B_RV(b_number_create_short(v))
|
||||
#define B_RV_INT(v) B_RV(b_number_create_int(v))
|
||||
#define B_RV_LONG(v) B_RV(b_number_create_long(v))
|
||||
#define B_RV_LONGLONG(v) B_RV(b_number_create_longlong(v))
|
||||
#define B_RV_FLOAT(v) B_RV(b_number_create_float(v))
|
||||
#define B_RV_DOUBLE(v) B_RV(b_number_create_double(v))
|
||||
#define B_RV_SIZE_T(v) B_RV(b_number_create_size_t(v))
|
||||
|
||||
#define B_NUMBER_IVAL(p) (b_number_get_size_t(p))
|
||||
#define B_NUMBER_FVAL(p) (b_number_get_double(p))
|
||||
|
||||
typedef struct b_number b_number;
|
||||
|
||||
typedef enum b_number_type {
|
||||
B_NUMBER_INT8,
|
||||
B_NUMBER_INT16,
|
||||
B_NUMBER_INT32,
|
||||
B_NUMBER_INT64,
|
||||
B_NUMBER_FLOAT32,
|
||||
B_NUMBER_FLOAT64,
|
||||
B_NUMBER_CHAR,
|
||||
B_NUMBER_SHORT,
|
||||
B_NUMBER_INT,
|
||||
B_NUMBER_LONG,
|
||||
B_NUMBER_LONGLONG,
|
||||
B_NUMBER_FLOAT,
|
||||
B_NUMBER_DOUBLE,
|
||||
B_NUMBER_SIZE_T,
|
||||
B_NUMBER_HANDLE,
|
||||
B_NUMBER_TYPE_COUNT,
|
||||
|
||||
B_NUMBER_BYTE = B_NUMBER_INT8,
|
||||
B_NUMBER_WORD = B_NUMBER_INT16,
|
||||
B_NUMBER_DWORD = B_NUMBER_INT32,
|
||||
B_NUMBER_QWORD = B_NUMBER_INT64,
|
||||
} b_number_type;
|
||||
|
||||
BLUE_API b_number *b_number_create(b_number_type type, void *value_ptr);
|
||||
static inline b_number *b_number_retain(b_number *number)
|
||||
{
|
||||
return B_NUMBER(b_retain(B_DSREF(number)));
|
||||
}
|
||||
static inline void b_number_release(b_number *number)
|
||||
{
|
||||
b_release(B_DSREF(number));
|
||||
}
|
||||
|
||||
static inline b_number *b_number_create_int8(int8_t value)
|
||||
{
|
||||
return b_number_create(B_NUMBER_INT8, &value);
|
||||
}
|
||||
static inline b_number *b_number_create_int16(int16_t value)
|
||||
{
|
||||
return b_number_create(B_NUMBER_INT16, &value);
|
||||
}
|
||||
static inline b_number *b_number_create_int32(int32_t value)
|
||||
{
|
||||
return b_number_create(B_NUMBER_INT32, &value);
|
||||
}
|
||||
static inline b_number *b_number_create_int64(int64_t value)
|
||||
{
|
||||
return b_number_create(B_NUMBER_INT64, &value);
|
||||
}
|
||||
static inline b_number *b_number_create_float32(float value)
|
||||
{
|
||||
return b_number_create(B_NUMBER_FLOAT32, &value);
|
||||
}
|
||||
static inline b_number *b_number_create_float64(double value)
|
||||
{
|
||||
return b_number_create(B_NUMBER_FLOAT64, &value);
|
||||
}
|
||||
static inline b_number *b_number_create_char(char value)
|
||||
{
|
||||
return b_number_create(B_NUMBER_CHAR, &value);
|
||||
}
|
||||
static inline b_number *b_number_create_short(short value)
|
||||
{
|
||||
return b_number_create(B_NUMBER_SHORT, &value);
|
||||
}
|
||||
static inline b_number *b_number_create_int(int value)
|
||||
{
|
||||
return b_number_create(B_NUMBER_INT, &value);
|
||||
}
|
||||
static inline b_number *b_number_create_long(long value)
|
||||
{
|
||||
return b_number_create(B_NUMBER_LONG, &value);
|
||||
}
|
||||
static inline b_number *b_number_create_longlong(long long value)
|
||||
{
|
||||
return b_number_create(B_NUMBER_LONGLONG, &value);
|
||||
}
|
||||
static inline b_number *b_number_create_float(float value)
|
||||
{
|
||||
return b_number_create(B_NUMBER_FLOAT, &value);
|
||||
}
|
||||
static inline b_number *b_number_create_double(double value)
|
||||
{
|
||||
return b_number_create(B_NUMBER_DOUBLE, &value);
|
||||
}
|
||||
static inline b_number *b_number_create_size_t(size_t value)
|
||||
{
|
||||
return b_number_create(B_NUMBER_SIZE_T, &value);
|
||||
}
|
||||
|
||||
BLUE_API b_number_type b_number_get_type(const b_number *number);
|
||||
BLUE_API int b_number_get_value(
|
||||
const b_number *number, b_number_type type, void *value_ptr);
|
||||
|
||||
static inline int8_t b_number_get_int8(const b_number *number)
|
||||
{
|
||||
int8_t v;
|
||||
b_number_get_value(number, B_NUMBER_INT8, &v);
|
||||
return v;
|
||||
}
|
||||
|
||||
static inline int16_t b_number_get_int16(const b_number *number)
|
||||
{
|
||||
int16_t v;
|
||||
b_number_get_value(number, B_NUMBER_INT16, &v);
|
||||
return v;
|
||||
}
|
||||
|
||||
static inline int32_t b_number_get_int32(const b_number *number)
|
||||
{
|
||||
int32_t v;
|
||||
b_number_get_value(number, B_NUMBER_INT32, &v);
|
||||
return v;
|
||||
}
|
||||
|
||||
static inline int64_t b_number_get_int64(const b_number *number)
|
||||
{
|
||||
int64_t v;
|
||||
b_number_get_value(number, B_NUMBER_INT64, &v);
|
||||
return v;
|
||||
}
|
||||
|
||||
static inline float b_number_get_float32(const b_number *number)
|
||||
{
|
||||
float v;
|
||||
b_number_get_value(number, B_NUMBER_FLOAT32, &v);
|
||||
return v;
|
||||
}
|
||||
|
||||
static inline double b_number_get_float64(const b_number *number)
|
||||
{
|
||||
double v;
|
||||
b_number_get_value(number, B_NUMBER_FLOAT64, &v);
|
||||
return v;
|
||||
}
|
||||
|
||||
static inline char b_number_get_char(const b_number *number)
|
||||
{
|
||||
char v;
|
||||
b_number_get_value(number, B_NUMBER_CHAR, &v);
|
||||
return v;
|
||||
}
|
||||
|
||||
static inline short b_number_get_short(const b_number *number)
|
||||
{
|
||||
short v;
|
||||
b_number_get_value(number, B_NUMBER_SHORT, &v);
|
||||
return v;
|
||||
}
|
||||
|
||||
static inline int b_number_get_int(const b_number *number)
|
||||
{
|
||||
int v;
|
||||
b_number_get_value(number, B_NUMBER_INT, &v);
|
||||
return v;
|
||||
}
|
||||
|
||||
static inline long b_number_get_long(const b_number *number)
|
||||
{
|
||||
long v;
|
||||
b_number_get_value(number, B_NUMBER_LONG, &v);
|
||||
return v;
|
||||
}
|
||||
|
||||
static inline long long b_number_get_longlong(const b_number *number)
|
||||
{
|
||||
long long v;
|
||||
b_number_get_value(number, B_NUMBER_LONGLONG, &v);
|
||||
return v;
|
||||
}
|
||||
|
||||
static inline float b_number_get_float(const b_number *number)
|
||||
{
|
||||
float v;
|
||||
b_number_get_value(number, B_NUMBER_FLOAT, &v);
|
||||
return v;
|
||||
}
|
||||
|
||||
static inline double b_number_get_double(const b_number *number)
|
||||
{
|
||||
double v;
|
||||
b_number_get_value(number, B_NUMBER_DOUBLE, &v);
|
||||
return v;
|
||||
}
|
||||
|
||||
static inline size_t b_number_get_size_t(const b_number *number)
|
||||
{
|
||||
size_t v;
|
||||
b_number_get_value(number, B_NUMBER_SIZE_T, &v);
|
||||
return v;
|
||||
}
|
||||
|
||||
BLUE_API bool b_number_is_integer(const b_number *number);
|
||||
BLUE_API bool b_number_is_float(const b_number *number);
|
||||
|
||||
BLUE_API size_t b_number_data_size(const b_number *number);
|
||||
|
||||
#endif
|
||||
40
ds/include/blue/ds/object.h
Normal file
40
ds/include/blue/ds/object.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#ifndef BLUELIB_DSREF_H_
|
||||
#define BLUELIB_DSREF_H_
|
||||
|
||||
#include <blue/ds/type.h>
|
||||
|
||||
#define B_DSREF(p) ((b_dsref *)(p))
|
||||
|
||||
#define B_TYPEOF(object) ((struct b_dsref *)(object)->ob_type)
|
||||
#define B_TYPEID(object) (b_typeid(B_DSREF(object)))
|
||||
|
||||
#define B_RV(p) (b_make_rvalue(B_DSREF(p)))
|
||||
#define B_RVT(t, p) ((t *)(b_make_rvalue(B_DSREF(p))))
|
||||
|
||||
#define B_DSREF_IS(object, type) (B_TYPEID(object) == B_DSREF_TYPE_##type)
|
||||
|
||||
struct b_string;
|
||||
struct b_stream;
|
||||
|
||||
typedef enum b_comparison_result {
|
||||
B_LESS = -1,
|
||||
B_EQUAL = 0,
|
||||
B_GREATER = 1,
|
||||
} b_comparison_result_t;
|
||||
|
||||
typedef struct b_dsref {
|
||||
unsigned int ob_ref;
|
||||
const struct b_dsref_type *ob_type;
|
||||
} b_dsref;
|
||||
|
||||
BLUE_API b_dsref *b_make_rvalue(b_dsref *obj);
|
||||
|
||||
BLUE_API b_dsref *b_retain(b_dsref *obj);
|
||||
BLUE_API void b_release(b_dsref *obj);
|
||||
|
||||
BLUE_API void b_to_string(b_dsref *obj, struct b_stream *out);
|
||||
BLUE_API b_dsref_type_id b_typeid(const b_dsref *obj);
|
||||
|
||||
BLUE_API b_comparison_result_t b_compare(const b_dsref *a, const b_dsref *b);
|
||||
|
||||
#endif
|
||||
83
ds/include/blue/ds/string.h
Normal file
83
ds/include/blue/ds/string.h
Normal file
@@ -0,0 +1,83 @@
|
||||
#ifndef BLUELIB_STRING_H_
|
||||
#define BLUELIB_STRING_H_
|
||||
|
||||
#include <blue/core/status.h>
|
||||
#include <blue/ds/object.h>
|
||||
#include <blue/ds/type.h>
|
||||
#include <ctype.h>
|
||||
|
||||
struct b_stream;
|
||||
|
||||
#define B_STRING(p) ((b_string *)(p))
|
||||
|
||||
#define B_CSTR(s) (b_string_create_from_cstr(s))
|
||||
#define B_RV_CSTR(s) (B_RV(b_string_create_from_cstr(s)))
|
||||
|
||||
typedef struct b_string b_string;
|
||||
|
||||
typedef enum b_strlen_flags {
|
||||
B_STRLEN_NORMAL = 0,
|
||||
B_STRLEN_IGNORE_ESC = 0x01u,
|
||||
B_STRLEN_IGNORE_MOD = 0x02u,
|
||||
} b_strlen_flags;
|
||||
|
||||
BLUE_API b_string *b_string_create(void);
|
||||
BLUE_API b_string *b_string_create_from_cstr(const char *s);
|
||||
BLUE_API b_string *b_string_create_from_c(char c, size_t count);
|
||||
BLUE_API b_string *b_string_duplicate(const b_string *str);
|
||||
|
||||
static inline b_string *b_string_retain(b_string *str)
|
||||
{
|
||||
return B_STRING(b_retain(B_DSREF(str)));
|
||||
}
|
||||
static inline void b_string_release(b_string *str)
|
||||
{
|
||||
b_release(B_DSREF(str));
|
||||
}
|
||||
BLUE_API char *b_string_steal(b_string *str);
|
||||
BLUE_API b_status b_string_reserve(b_string *str, size_t capacity);
|
||||
BLUE_API b_status b_string_replace(
|
||||
b_string *str, size_t start, size_t length, const char *new_data);
|
||||
BLUE_API b_status b_string_replace_all(b_string *str, const char *new_data);
|
||||
BLUE_API b_status b_string_remove(b_string *str, size_t start, size_t length);
|
||||
BLUE_API b_status b_string_transform(b_string *str, int (*transformer)(int));
|
||||
static inline b_status b_string_toupper(b_string *str)
|
||||
{
|
||||
return b_string_transform(str, toupper);
|
||||
}
|
||||
static inline b_status b_string_tolower(b_string *str)
|
||||
{
|
||||
return b_string_transform(str, tolower);
|
||||
}
|
||||
BLUE_API b_status b_string_open_stream(b_string *str, struct b_stream **out);
|
||||
|
||||
BLUE_API void b_string_append_s(b_string *dest, const b_string *src);
|
||||
BLUE_API void b_string_append_cstr(b_string *dest, const char *src);
|
||||
BLUE_API void b_string_append_cstrf(b_string *dest, const char *format, ...);
|
||||
BLUE_API void b_string_prepend_cstr(b_string *dest, const char *src);
|
||||
BLUE_API void b_string_prepend_cstrf(b_string *dest, const char *format, ...);
|
||||
BLUE_API void b_string_insert_s(b_string *dest, const b_string *src, size_t at);
|
||||
BLUE_API void b_string_insert_cstr(b_string *dest, const char *src, size_t at);
|
||||
BLUE_API void b_string_insert_cstrn(
|
||||
b_string *dest, const char *src, size_t len, size_t at);
|
||||
BLUE_API void b_string_insert_cstrf(
|
||||
b_string *dest, size_t at, const char *format, ...);
|
||||
BLUE_API void b_string_clear(b_string *str);
|
||||
|
||||
BLUE_API size_t b_string_get_size(const b_string *str, b_strlen_flags flags);
|
||||
BLUE_API size_t b_string_get_capacity(const b_string *str);
|
||||
|
||||
BLUE_API char b_string_front(const b_string *str);
|
||||
BLUE_API char b_string_back(const b_string *str);
|
||||
|
||||
BLUE_API void b_string_pop_back(b_string *str);
|
||||
|
||||
BLUE_API const char *b_string_ptr(const b_string *str);
|
||||
BLUE_API b_string *b_string_substr(const b_string *str, size_t start, size_t len);
|
||||
|
||||
BLUE_API char *b_strdup(const char *s);
|
||||
BLUE_API size_t b_strlen(const char *s, b_strlen_flags flags);
|
||||
|
||||
BLUE_API uint64_t b_cstr_hash(const char *s);
|
||||
|
||||
#endif
|
||||
72
ds/include/blue/ds/tree.h
Normal file
72
ds/include/blue/ds/tree.h
Normal file
@@ -0,0 +1,72 @@
|
||||
#ifndef BLUELIB_TREE_H_
|
||||
#define BLUELIB_TREE_H_
|
||||
|
||||
#include <blue/core/misc.h>
|
||||
#include <blue/core/queue.h>
|
||||
#include <blue/ds/string.h>
|
||||
|
||||
#define B_TREE(p) ((b_tree *)(p))
|
||||
#define B_TREE_NODE_INIT ((b_tree_node){0})
|
||||
|
||||
#define B_TREE_CONTAINER(t, m, v) \
|
||||
((void *)((v) ? (uintptr_t)(v) - (offsetof(t, m)) : 0))
|
||||
|
||||
typedef struct b_tree b_tree;
|
||||
|
||||
#define b_tree_node_foreach(it, node) \
|
||||
for (int z__b_unique_name() = b_tree_iterator_begin_at_node(node, it); \
|
||||
(it)->node != NULL; b_tree_iterator_next(it))
|
||||
|
||||
#define b_tree_node_foreach_recursive(it, node) \
|
||||
for (int z__b_unique_name() \
|
||||
= b_tree_iterator_begin_at_node_recursive(node, it); \
|
||||
(it)->node != NULL; b_tree_iterator_next(it))
|
||||
|
||||
#define b_tree_foreach(it, tree) \
|
||||
for (int z__b_unique_name() = b_tree_iterator_begin(tree, it); \
|
||||
(it)->node != NULL; b_tree_iterator_next(it))
|
||||
|
||||
typedef struct b_tree_node {
|
||||
struct b_tree_node *__p01, *__p02, *__p03;
|
||||
struct b_queue_entry __q01;
|
||||
// struct b_tree_node *parent;
|
||||
// struct b_tree_node *first_child, *next_sibling;
|
||||
} b_tree_node;
|
||||
|
||||
typedef struct b_tree_iterator {
|
||||
b_iterator _base;
|
||||
size_t i, depth;
|
||||
b_tree_node *node;
|
||||
|
||||
unsigned char _f01;
|
||||
} b_tree_iterator;
|
||||
|
||||
BLUE_API b_tree *b_tree_create(void);
|
||||
|
||||
static inline b_tree *b_tree_retain(b_tree *tree)
|
||||
{
|
||||
return B_TREE(b_retain(B_DSREF(tree)));
|
||||
}
|
||||
static inline void b_tree_release(b_tree *tree)
|
||||
{
|
||||
b_release(B_DSREF(tree));
|
||||
}
|
||||
|
||||
BLUE_API void b_tree_set_root(b_tree *tree, struct b_tree_node *node);
|
||||
|
||||
BLUE_API void b_tree_node_add_child(b_tree_node *parent, b_tree_node *child);
|
||||
BLUE_API void b_tree_node_add_sibling(b_tree_node *node, b_tree_node *to_add);
|
||||
|
||||
BLUE_API b_tree_node *b_tree_node_get_child(b_tree_node *node, size_t at);
|
||||
BLUE_API b_tree_node *b_tree_node_get_parent(b_tree_node *node);
|
||||
|
||||
BLUE_API int b_tree_iterator_begin(b_tree *tree, b_tree_iterator *it);
|
||||
BLUE_API int b_tree_iterator_begin_at_node(b_tree_node *node, b_tree_iterator *it);
|
||||
BLUE_API int b_tree_iterator_begin_at_node_recursive(
|
||||
b_tree_node *node, b_tree_iterator *it);
|
||||
|
||||
BLUE_API bool b_tree_iterator_next(b_tree_iterator *it);
|
||||
BLUE_API b_status b_tree_iterator_erase(b_tree_iterator *it);
|
||||
BLUE_API bool b_tree_iterator_is_valid(const b_tree_iterator *it);
|
||||
|
||||
#endif
|
||||
53
ds/include/blue/ds/type.h
Normal file
53
ds/include/blue/ds/type.h
Normal file
@@ -0,0 +1,53 @@
|
||||
#ifndef BLUELIB_TYPE_H_
|
||||
#define BLUELIB_TYPE_H_
|
||||
|
||||
#include <blue/core/queue.h>
|
||||
#include <blue/core/status.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define B_NPOS ((size_t)INTPTR_MAX)
|
||||
|
||||
struct b_dsref;
|
||||
struct b_stream;
|
||||
|
||||
typedef uintptr_t b_dsref_type_id;
|
||||
|
||||
typedef enum b_fundamental_type_id {
|
||||
B_DSREF_TYPE_NONE = 0,
|
||||
B_DSREF_TYPE_ANY,
|
||||
B_DSREF_TYPE_LIST,
|
||||
B_DSREF_TYPE_ARRAY,
|
||||
B_DSREF_TYPE_BUFFER,
|
||||
B_DSREF_TYPE_DICT,
|
||||
B_DSREF_TYPE_ERROR,
|
||||
B_DSREF_TYPE_HASHMAP,
|
||||
B_DSREF_TYPE_NUMBER,
|
||||
B_DSREF_TYPE_STRING,
|
||||
B_DSREF_TYPE_TREE,
|
||||
B_DSREF_TYPE_UUID,
|
||||
B_DSREF_TYPE_PATH,
|
||||
B_DSREF_TYPE_FILE,
|
||||
B_DSREF_TYPE_DIRECTORY,
|
||||
} b_fundamental_type_id;
|
||||
|
||||
typedef enum b_dsref_type_flags {
|
||||
B_DSREF_FUNDAMENTAL = 0x01u,
|
||||
} b_dsref_type_flags;
|
||||
|
||||
typedef struct b_dsref_type {
|
||||
b_dsref_type_flags t_flags;
|
||||
char t_name[64];
|
||||
size_t t_instance_size;
|
||||
b_dsref_type_id t_id;
|
||||
b_queue_entry t_entry;
|
||||
void (*t_init)(struct b_dsref *);
|
||||
void (*t_release)(struct b_dsref *);
|
||||
void (*t_to_string)(struct b_dsref *, struct b_stream *);
|
||||
} b_dsref_type;
|
||||
|
||||
BLUE_API b_status b_dsref_type_register(b_dsref_type *type);
|
||||
BLUE_API struct b_dsref *b_dsref_type_instantiate(const b_dsref_type *type);
|
||||
|
||||
#endif
|
||||
52
ds/include/blue/ds/uuid.h
Normal file
52
ds/include/blue/ds/uuid.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#ifndef BLUELIB_UUID_H_
|
||||
#define BLUELIB_UUID_H_
|
||||
|
||||
#include <blue/core/status.h>
|
||||
#include <blue/ds/object.h>
|
||||
#include <blue/ds/type.h>
|
||||
|
||||
#define B_UUID(p) ((b_uuid *)(p))
|
||||
|
||||
#define B_UUID_NBYTES 16
|
||||
#define B_UUID_STRING_MAX 37
|
||||
|
||||
struct b_string;
|
||||
struct b_stringstream;
|
||||
|
||||
typedef struct b_uuid b_uuid;
|
||||
|
||||
typedef struct b_uuid_bytes {
|
||||
unsigned char uuid_bytes[B_UUID_NBYTES];
|
||||
} b_uuid_bytes;
|
||||
|
||||
BLUE_API b_uuid *b_uuid_create(void);
|
||||
BLUE_API b_uuid *b_uuid_create_from_bytes(
|
||||
unsigned char u00, unsigned char u01, unsigned char u02,
|
||||
unsigned char u03, unsigned char u04, unsigned char u05,
|
||||
unsigned char u06, unsigned char u07, unsigned char u08,
|
||||
unsigned char u09, unsigned char u10, unsigned char u11, unsigned char u12,
|
||||
unsigned char u13, unsigned char u14, unsigned char u15);
|
||||
BLUE_API b_uuid *b_uuid_create_from_bytev(const unsigned char bytes[B_UUID_NBYTES]);
|
||||
BLUE_API b_uuid *b_uuid_create_from_uuid_bytes(const b_uuid_bytes *bytes);
|
||||
BLUE_API b_uuid *b_uuid_create_from_string(const struct b_string *string);
|
||||
BLUE_API b_uuid *b_uuid_create_from_cstr(const char *s);
|
||||
|
||||
static inline b_uuid *b_uuid_retain(b_uuid *uuid)
|
||||
{
|
||||
return B_UUID(b_retain(B_DSREF(uuid)));
|
||||
}
|
||||
static inline void b_uuid_release(b_uuid *uuid)
|
||||
{
|
||||
b_release(B_DSREF(uuid));
|
||||
}
|
||||
|
||||
BLUE_API b_status b_uuid_to_string(const b_uuid *uuid, struct b_string *out);
|
||||
BLUE_API b_status b_uuid_to_cstr(const b_uuid *uuid, char out[B_UUID_STRING_MAX]);
|
||||
BLUE_API b_status b_uuid_to_stringstream(
|
||||
const b_uuid *uuid, struct b_stringstream *out);
|
||||
BLUE_API void b_uuid_get_bytes(
|
||||
const b_uuid *uuid, unsigned char bytes[B_UUID_NBYTES]);
|
||||
BLUE_API void b_uuid_get_uuid_bytes(const b_uuid *uuid, b_uuid_bytes *bytes);
|
||||
BLUE_API b_uuid_bytes *b_uuid_ptr(b_uuid *uuid);
|
||||
|
||||
#endif
|
||||
342
ds/list.c
Normal file
342
ds/list.c
Normal file
@@ -0,0 +1,342 @@
|
||||
#include "list.h"
|
||||
|
||||
#include <blue/ds/list.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static void list_release(struct b_dsref *obj);
|
||||
|
||||
static struct b_dsref_type list_type = {
|
||||
.t_name = "corelib::list",
|
||||
.t_flags = B_DSREF_FUNDAMENTAL,
|
||||
.t_id = B_DSREF_TYPE_LIST,
|
||||
.t_instance_size = sizeof(struct b_list),
|
||||
.t_release = list_release,
|
||||
};
|
||||
|
||||
struct b_list *b_list_create(void)
|
||||
{
|
||||
struct b_list *list
|
||||
= (struct b_list *)b_dsref_type_instantiate(&list_type);
|
||||
if (!list) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
bool b_list_empty(struct b_list *q)
|
||||
{
|
||||
return q->l_len == 0;
|
||||
}
|
||||
|
||||
void *b_list_first_item(const struct b_list *q)
|
||||
{
|
||||
struct b_queue_entry *entry = b_queue_first(&q->l_queue);
|
||||
|
||||
if (!entry) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct b_list_entry *list_entry
|
||||
= b_unbox(struct b_list_entry, entry, e_entry);
|
||||
|
||||
return list_entry->e_data;
|
||||
}
|
||||
|
||||
void *b_list_last_item(const struct b_list *q)
|
||||
{
|
||||
struct b_queue_entry *entry = b_queue_last(&q->l_queue);
|
||||
|
||||
if (!entry) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct b_list_entry *list_entry
|
||||
= b_unbox(struct b_list_entry, entry, e_entry);
|
||||
|
||||
return list_entry->e_data;
|
||||
}
|
||||
|
||||
struct b_list_entry *b_list_first_entry(const struct b_list *q)
|
||||
{
|
||||
struct b_queue_entry *entry = b_queue_first(&q->l_queue);
|
||||
|
||||
if (!entry) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct b_list_entry *list_entry
|
||||
= b_unbox(struct b_list_entry, entry, e_entry);
|
||||
|
||||
return list_entry;
|
||||
}
|
||||
|
||||
struct b_list_entry *b_list_last_entry(const struct b_list *q)
|
||||
{
|
||||
struct b_queue_entry *entry = b_queue_last(&q->l_queue);
|
||||
|
||||
if (!entry) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct b_list_entry *list_entry
|
||||
= b_unbox(struct b_list_entry, entry, e_entry);
|
||||
|
||||
return list_entry;
|
||||
}
|
||||
|
||||
struct b_list_entry *b_list_next(const struct b_list_entry *entry)
|
||||
{
|
||||
if (!entry) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct b_queue_entry *next = b_queue_next(&entry->e_entry);
|
||||
|
||||
if (!next) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return b_unbox(struct b_list_entry, next, e_entry);
|
||||
}
|
||||
|
||||
struct b_list_entry *b_list_prev(const struct b_list_entry *entry)
|
||||
{
|
||||
if (!entry) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct b_queue_entry *next = b_queue_prev(&entry->e_entry);
|
||||
|
||||
if (!next) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return b_unbox(struct b_list_entry, next, e_entry);
|
||||
}
|
||||
|
||||
size_t b_list_length(const struct b_list *q)
|
||||
{
|
||||
return q->l_len;
|
||||
}
|
||||
|
||||
static struct b_list_entry *make_entry(void *item)
|
||||
{
|
||||
struct b_list_entry *entry = malloc(sizeof *entry);
|
||||
if (!entry) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(entry, 0x0, sizeof *entry);
|
||||
|
||||
entry->e_data = item;
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
struct b_list_entry *b_list_insert_before(
|
||||
struct b_list *q, void *ptr, struct b_list_entry *before)
|
||||
{
|
||||
struct b_list_entry *entry = make_entry(ptr);
|
||||
if (!entry) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
b_queue_insert_before(&q->l_queue, &entry->e_entry, &before->e_entry);
|
||||
q->l_len++;
|
||||
return entry;
|
||||
}
|
||||
|
||||
struct b_list_entry *b_list_insert_after(
|
||||
struct b_list *q, void *ptr, struct b_list_entry *after)
|
||||
{
|
||||
struct b_list_entry *entry = make_entry(ptr);
|
||||
if (!entry) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
b_queue_insert_after(&q->l_queue, &entry->e_entry, &after->e_entry);
|
||||
q->l_len++;
|
||||
return entry;
|
||||
}
|
||||
|
||||
struct b_list_entry *b_list_push_front(struct b_list *q, void *ptr)
|
||||
{
|
||||
struct b_list_entry *entry = make_entry(ptr);
|
||||
if (!entry) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
b_queue_push_front(&q->l_queue, &entry->e_entry);
|
||||
q->l_len++;
|
||||
return entry;
|
||||
}
|
||||
|
||||
struct b_list_entry *b_list_push_back(struct b_list *q, void *ptr)
|
||||
{
|
||||
struct b_list_entry *entry = make_entry(ptr);
|
||||
if (!entry) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
b_queue_push_back(&q->l_queue, &entry->e_entry);
|
||||
q->l_len++;
|
||||
return entry;
|
||||
}
|
||||
|
||||
void *b_list_pop_front(struct b_list *q)
|
||||
{
|
||||
struct b_queue_entry *entry = b_queue_pop_front(&q->l_queue);
|
||||
if (!entry) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct b_list_entry *list_entry
|
||||
= b_unbox(struct b_list_entry, entry, e_entry);
|
||||
|
||||
void *item = list_entry->e_data;
|
||||
free(list_entry);
|
||||
|
||||
q->l_len--;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
void *b_list_pop_back(struct b_list *q)
|
||||
{
|
||||
struct b_queue_entry *entry = b_queue_pop_back(&q->l_queue);
|
||||
if (!entry) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct b_list_entry *list_entry
|
||||
= b_unbox(struct b_list_entry, entry, e_entry);
|
||||
|
||||
void *item = list_entry->e_data;
|
||||
free(list_entry);
|
||||
|
||||
q->l_len--;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
static struct b_list_entry *find_item(struct b_list *list, void *item)
|
||||
{
|
||||
struct b_queue_iterator it;
|
||||
b_queue_foreach (&it, &list->l_queue) {
|
||||
struct b_list_entry *entry
|
||||
= b_unbox(struct b_list_entry, it.entry, e_entry);
|
||||
|
||||
if (entry->e_data == item) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
b_status b_list_delete_item(struct b_list *q, void *ptr)
|
||||
{
|
||||
struct b_list_entry *entry = find_item(q, ptr);
|
||||
if (!entry) {
|
||||
return B_ERR_NO_ENTRY;
|
||||
}
|
||||
|
||||
b_queue_delete(&q->l_queue, &entry->e_entry);
|
||||
q->l_len--;
|
||||
|
||||
free(entry);
|
||||
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
b_status b_list_delete_entry(struct b_list *q, struct b_list_entry *entry)
|
||||
{
|
||||
b_queue_delete(&q->l_queue, &entry->e_entry);
|
||||
q->l_len--;
|
||||
|
||||
free(entry);
|
||||
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
void b_list_delete_all(struct b_list *q)
|
||||
{
|
||||
struct b_queue_iterator it;
|
||||
b_queue_iterator_begin(&q->l_queue, &it);
|
||||
|
||||
while (b_queue_iterator_is_valid(&it)) {
|
||||
struct b_list_entry *entry
|
||||
= b_unbox(struct b_list_entry, it.entry, e_entry);
|
||||
b_queue_iterator_erase(&it);
|
||||
|
||||
free(entry);
|
||||
}
|
||||
|
||||
q->l_len = 0;
|
||||
}
|
||||
|
||||
int b_list_iterator_begin(const struct b_list *q, struct b_list_iterator *it)
|
||||
{
|
||||
b_queue_iterator_begin(&q->l_queue, &it->_base);
|
||||
it->_q = q;
|
||||
it->i = 0;
|
||||
it->entry = b_unbox(struct b_list_entry, it->_base.entry, e_entry);
|
||||
if (it->entry) {
|
||||
it->item = it->entry->e_data;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool b_list_iterator_next(struct b_list_iterator *it)
|
||||
{
|
||||
bool ok = b_queue_iterator_next(&it->_base);
|
||||
if (!ok) {
|
||||
it->entry = NULL;
|
||||
it->item = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
it->i++;
|
||||
it->entry = b_unbox(struct b_list_entry, it->_base.entry, e_entry);
|
||||
if (it->entry) {
|
||||
it->item = it->entry->e_data;
|
||||
}
|
||||
|
||||
return it->entry != NULL;
|
||||
}
|
||||
|
||||
b_status b_list_iterator_erase(struct b_list_iterator *it)
|
||||
{
|
||||
if (!it->entry) {
|
||||
return B_ERR_OUT_OF_BOUNDS;
|
||||
}
|
||||
|
||||
b_queue_iterator_erase(&it->_base);
|
||||
free(it->entry);
|
||||
|
||||
it->entry = b_unbox(struct b_list_entry, it->_base.entry, e_entry);
|
||||
if (it->entry) {
|
||||
it->item = it->entry->e_data;
|
||||
}
|
||||
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
bool b_list_iterator_is_valid(const struct b_list_iterator *it)
|
||||
{
|
||||
return it->entry != NULL;
|
||||
}
|
||||
|
||||
void *b_list_entry_value(const struct b_list_entry *entry)
|
||||
{
|
||||
return entry ? entry->e_data : NULL;
|
||||
}
|
||||
|
||||
static void list_release(struct b_dsref *obj)
|
||||
{
|
||||
struct b_list *list = B_LIST(obj);
|
||||
b_list_delete_all(list);
|
||||
}
|
||||
19
ds/list.h
Normal file
19
ds/list.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#ifndef _BLUELIB_LIST_H_
|
||||
#define _BLUELIB_LIST_H_
|
||||
|
||||
#include "object.h"
|
||||
|
||||
#include <blue/core/queue.h>
|
||||
|
||||
struct b_list {
|
||||
struct b_dsref l_base;
|
||||
struct b_queue l_queue;
|
||||
size_t l_len;
|
||||
};
|
||||
|
||||
struct b_list_entry {
|
||||
struct b_queue_entry e_entry;
|
||||
void *e_data;
|
||||
};
|
||||
|
||||
#endif
|
||||
1681
ds/number.c
Normal file
1681
ds/number.c
Normal file
File diff suppressed because it is too large
Load Diff
29
ds/number.h
Normal file
29
ds/number.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifndef _BLUELIB_NUMBER_H_
|
||||
#define _BLUELIB_NUMBER_H_
|
||||
|
||||
#include "object.h"
|
||||
|
||||
#include <blue/ds/number.h>
|
||||
|
||||
struct b_number {
|
||||
struct b_dsref n_base;
|
||||
b_number_type n_type;
|
||||
union {
|
||||
int8_t v_int8;
|
||||
int16_t v_int16;
|
||||
int32_t v_int32;
|
||||
int64_t v_int64;
|
||||
float v_float32;
|
||||
double v_float64;
|
||||
char v_char;
|
||||
short v_short;
|
||||
int v_int;
|
||||
long v_long;
|
||||
long long v_longlong;
|
||||
float v_float;
|
||||
double v_double;
|
||||
size_t v_size_t;
|
||||
} n_value;
|
||||
};
|
||||
|
||||
#endif
|
||||
66
ds/object.c
Normal file
66
ds/object.c
Normal file
@@ -0,0 +1,66 @@
|
||||
#include "object.h"
|
||||
|
||||
#include <blue/core/stream.h>
|
||||
#include <blue/ds/object.h>
|
||||
#include <blue/ds/string.h>
|
||||
#include <blue/ds/type.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
void b_dsref_init(struct b_dsref *obj, struct b_dsref_type *type)
|
||||
{
|
||||
obj->ob_ref = 1;
|
||||
obj->ob_type = type;
|
||||
}
|
||||
|
||||
struct b_dsref *b_make_rvalue(struct b_dsref *obj)
|
||||
{
|
||||
obj->ob_ref = 0;
|
||||
return obj;
|
||||
}
|
||||
|
||||
struct b_dsref *b_retain(struct b_dsref *obj)
|
||||
{
|
||||
obj->ob_ref++;
|
||||
return obj;
|
||||
}
|
||||
|
||||
void b_release(struct b_dsref *obj)
|
||||
{
|
||||
if (obj->ob_ref > 1) {
|
||||
obj->ob_ref--;
|
||||
return;
|
||||
}
|
||||
|
||||
obj->ob_ref = 0;
|
||||
|
||||
if (obj->ob_type && obj->ob_type->t_release) {
|
||||
obj->ob_type->t_release(obj);
|
||||
}
|
||||
|
||||
free(obj);
|
||||
}
|
||||
|
||||
void b_to_string(struct b_dsref *obj, struct b_stream *out)
|
||||
{
|
||||
if (obj->ob_type->t_to_string) {
|
||||
obj->ob_type->t_to_string(obj, out);
|
||||
return;
|
||||
}
|
||||
|
||||
const char *name = "corelib::object";
|
||||
if (obj->ob_type) {
|
||||
name = obj->ob_type->t_name;
|
||||
}
|
||||
|
||||
b_stream_write_fmt(out, NULL, "<%s@%p>", name, obj);
|
||||
}
|
||||
|
||||
b_dsref_type_id b_typeid(const struct b_dsref *obj)
|
||||
{
|
||||
if (obj->ob_type->t_flags & B_DSREF_FUNDAMENTAL) {
|
||||
return obj->ob_type->t_id;
|
||||
}
|
||||
|
||||
return (b_dsref_type_id)obj->ob_type;
|
||||
}
|
||||
6
ds/object.h
Normal file
6
ds/object.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#ifndef _BLUELIB_DSREF_H_
|
||||
#define _BLUELIB_DSREF_H_
|
||||
|
||||
#include <blue/ds/object.h>
|
||||
|
||||
#endif
|
||||
683
ds/string.c
Normal file
683
ds/string.c
Normal file
@@ -0,0 +1,683 @@
|
||||
#include "string.h"
|
||||
|
||||
#include <blue/core/stream.h>
|
||||
#include <blue/core/stringstream.h>
|
||||
#include <blue/ds/string.h>
|
||||
#include <blue/ds/type.h>
|
||||
#include <ctype.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static void string_release(struct b_dsref *obj);
|
||||
static void string_to_string(struct b_dsref *obj, struct b_stream *out);
|
||||
|
||||
static struct b_dsref_type string_type = {
|
||||
.t_name = "corelib::string",
|
||||
.t_flags = B_DSREF_FUNDAMENTAL,
|
||||
.t_id = B_DSREF_TYPE_STRING,
|
||||
.t_instance_size = sizeof(struct b_string),
|
||||
.t_release = string_release,
|
||||
.t_to_string = string_to_string,
|
||||
};
|
||||
|
||||
struct b_string *b_string_create(void)
|
||||
{
|
||||
struct b_string *str
|
||||
= (struct b_string *)b_dsref_type_instantiate(&string_type);
|
||||
if (!str) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
str->s_len = 0;
|
||||
str->s_max = STRING_INLINE_CAPACITY;
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
static bool string_is_inline(const struct b_string *str)
|
||||
{
|
||||
/* strings cannot go below STRING_INLINE_CAPACITY capacity */
|
||||
return str->s_max == STRING_INLINE_CAPACITY;
|
||||
}
|
||||
|
||||
static char *string_ptr(struct b_string *str)
|
||||
{
|
||||
if (string_is_inline(str)) {
|
||||
return str->s_data.d_inline;
|
||||
}
|
||||
|
||||
return str->s_data.d_external;
|
||||
}
|
||||
|
||||
static int string_make_inline(struct b_string *str)
|
||||
{
|
||||
char *buffer = string_ptr(str);
|
||||
memcpy(str->s_data.d_inline, buffer, sizeof str->s_data.d_inline);
|
||||
str->s_data.d_inline[sizeof str->s_data.d_inline - 1] = '\0';
|
||||
|
||||
str->s_max = STRING_INLINE_CAPACITY;
|
||||
|
||||
if (str->s_len >= str->s_max) {
|
||||
str->s_len = str->s_max;
|
||||
}
|
||||
|
||||
free(buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int string_resize_large(struct b_string *str, size_t capacity)
|
||||
{
|
||||
char *buffer = string_ptr(str);
|
||||
char *new_buffer = realloc(buffer, capacity + 1);
|
||||
if (!new_buffer) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
str->s_max = capacity;
|
||||
str->s_data.d_external = new_buffer;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int string_make_large(struct b_string *str, size_t capacity)
|
||||
{
|
||||
const char *old_buffer = string_ptr(str);
|
||||
char *buffer = malloc(capacity + 1);
|
||||
if (!buffer) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(buffer, old_buffer, sizeof str->s_data.d_inline);
|
||||
buffer[str->s_len] = '\0';
|
||||
|
||||
str->s_max = capacity;
|
||||
str->s_data.d_external = buffer;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int string_change_capacity(struct b_string *str, size_t capacity)
|
||||
{
|
||||
size_t old_capacity = str->s_max;
|
||||
|
||||
if (capacity < STRING_INLINE_CAPACITY) {
|
||||
capacity = STRING_INLINE_CAPACITY;
|
||||
}
|
||||
|
||||
bool was_inline = string_is_inline(str);
|
||||
bool is_now_inline = capacity == STRING_INLINE_CAPACITY;
|
||||
|
||||
if (capacity == old_capacity) {
|
||||
/* this also handles the case where the old and new capacity both fit into the inline buffer. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!was_inline && is_now_inline) {
|
||||
/* string was large, is now small enough to fit inline. */
|
||||
return string_make_inline(str);
|
||||
}
|
||||
|
||||
if (!was_inline) {
|
||||
/* string was large, and is still large. */
|
||||
return string_resize_large(str, capacity);
|
||||
}
|
||||
|
||||
if (!is_now_inline) {
|
||||
/* string was inline, and now large enough to require a buffer. */
|
||||
return string_make_large(str, capacity);
|
||||
}
|
||||
|
||||
/* nothing to do */
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct b_string *b_string_create_from_cstr(const char *s)
|
||||
{
|
||||
struct b_string *str = b_string_create();
|
||||
if (!str) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!s) {
|
||||
return str;
|
||||
}
|
||||
|
||||
str->s_len = strlen(s);
|
||||
string_change_capacity(str, str->s_len);
|
||||
|
||||
char *dest = string_ptr(str);
|
||||
memcpy(dest, s, str->s_len);
|
||||
dest[str->s_len] = 0;
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
struct b_string *b_string_create_from_c(char c, size_t count)
|
||||
{
|
||||
struct b_string *str = b_string_create();
|
||||
if (!str) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
string_change_capacity(str, count);
|
||||
char *s = string_ptr(str);
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
s[i] = c;
|
||||
}
|
||||
|
||||
str->s_len = count;
|
||||
return str;
|
||||
}
|
||||
|
||||
struct b_string *b_string_duplicate(const struct b_string *str)
|
||||
{
|
||||
struct b_string *new_str = b_string_create();
|
||||
if (!str) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
string_change_capacity(new_str, str->s_len);
|
||||
const char *src = b_string_ptr(str);
|
||||
char *dst = string_ptr(new_str);
|
||||
|
||||
memcpy(dst, src, str->s_len);
|
||||
new_str->s_len = str->s_len;
|
||||
|
||||
return new_str;
|
||||
}
|
||||
|
||||
char *b_string_steal(struct b_string *str)
|
||||
{
|
||||
char *dest = NULL;
|
||||
char *src = string_ptr(str);
|
||||
|
||||
if (string_is_inline(str)) {
|
||||
dest = b_strdup(src);
|
||||
src[0] = 0;
|
||||
} else {
|
||||
dest = src;
|
||||
str->s_data.d_external = NULL;
|
||||
str->s_max = STRING_INLINE_CAPACITY;
|
||||
}
|
||||
|
||||
str->s_len = 0;
|
||||
return dest;
|
||||
}
|
||||
|
||||
b_status b_string_reserve(struct b_string *str, size_t capacity)
|
||||
{
|
||||
if (str->s_max >= capacity) {
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
int err = string_change_capacity(str, capacity);
|
||||
|
||||
return err == 0 ? B_SUCCESS : B_ERR_NO_MEMORY;
|
||||
}
|
||||
|
||||
b_status b_string_replace(
|
||||
struct b_string *str, size_t start, size_t length, const char *new_data)
|
||||
{
|
||||
b_status status = B_SUCCESS;
|
||||
size_t new_data_len = strlen(new_data);
|
||||
|
||||
if (start >= str->s_len) {
|
||||
return B_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (start + length >= str->s_len) {
|
||||
length = str->s_len - start;
|
||||
}
|
||||
|
||||
size_t new_str_len = str->s_len - length + new_data_len;
|
||||
if (new_str_len > str->s_max) {
|
||||
status = b_string_reserve(str, new_str_len);
|
||||
}
|
||||
|
||||
if (!B_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
char *s = string_ptr(str);
|
||||
|
||||
char *substitution_start = s + start;
|
||||
char *excess_src = s + start + length;
|
||||
size_t excess_length = str->s_len - start - length;
|
||||
char *excess_dest = substitution_start + new_data_len;
|
||||
|
||||
memmove(excess_dest, excess_src, excess_length);
|
||||
memmove(substitution_start, new_data, new_data_len);
|
||||
s[new_str_len] = '\0';
|
||||
|
||||
str->s_len = new_str_len;
|
||||
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
b_status b_string_replace_all(b_string *str, const char *new_data)
|
||||
{
|
||||
size_t new_len = strlen(new_data);
|
||||
b_string_reserve(str, new_len);
|
||||
char *dest = (char *)b_string_ptr(str);
|
||||
memcpy(dest, new_data, new_len);
|
||||
dest[new_len] = '\0';
|
||||
str->s_len = new_len;
|
||||
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
b_status b_string_remove(b_string *str, size_t start, size_t length)
|
||||
{
|
||||
b_status status = B_SUCCESS;
|
||||
|
||||
if (start >= str->s_len) {
|
||||
return B_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (start + length >= str->s_len) {
|
||||
length = str->s_len - start;
|
||||
}
|
||||
|
||||
size_t new_str_len = str->s_len - length;
|
||||
|
||||
char *s = string_ptr(str);
|
||||
|
||||
char *removal_start = s + start;
|
||||
char *excess_src = s + start + length;
|
||||
size_t excess_length = str->s_len - start - length;
|
||||
|
||||
memmove(removal_start, excess_src, excess_length);
|
||||
s[new_str_len] = '\0';
|
||||
|
||||
str->s_len = new_str_len;
|
||||
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
b_status b_string_transform(b_string *str, int (*transformer)(int))
|
||||
{
|
||||
char *s = string_ptr(str);
|
||||
for (size_t i = 0; i < str->s_len; i++) {
|
||||
int c = transformer(s[i]);
|
||||
|
||||
if (c != 0) {
|
||||
s[i] = c;
|
||||
}
|
||||
}
|
||||
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
static enum b_status stream_close(struct b_stream *stream)
|
||||
{
|
||||
struct b_string *str = stream->s_ptr;
|
||||
b_string_release(str);
|
||||
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
static enum b_status stream_getc(struct b_stream *stream, int *out)
|
||||
{
|
||||
struct b_string *str = stream->s_ptr;
|
||||
if (stream->s_cursor >= str->s_len) {
|
||||
return B_ERR_NO_DATA;
|
||||
}
|
||||
|
||||
char *s = string_ptr(str);
|
||||
*out = s[stream->s_cursor];
|
||||
stream->s_cursor++;
|
||||
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
static enum b_status stream_read(
|
||||
struct b_stream *stream, unsigned char *buf, size_t count, size_t *nr_read)
|
||||
{
|
||||
struct b_string *str = stream->s_ptr;
|
||||
if (stream->s_cursor >= str->s_len) {
|
||||
*nr_read = 0;
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
size_t available = str->s_len - stream->s_cursor;
|
||||
size_t to_read = b_min(size_t, count, available);
|
||||
|
||||
char *s = string_ptr(str) + stream->s_cursor;
|
||||
|
||||
memcpy(buf, s, to_read);
|
||||
|
||||
*nr_read = to_read;
|
||||
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
static enum b_status stream_write(
|
||||
struct b_stream *stream, const unsigned char *buf, size_t count,
|
||||
size_t *nr_written)
|
||||
{
|
||||
struct b_string *str = stream->s_ptr;
|
||||
enum b_status status = B_SUCCESS;
|
||||
|
||||
if (stream->s_cursor + count > str->s_max) {
|
||||
status = b_string_reserve(str, stream->s_cursor + count);
|
||||
}
|
||||
|
||||
if (!B_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
char *s = string_ptr(str) + stream->s_cursor;
|
||||
memcpy(s, buf, count);
|
||||
s[str->s_max] = '\0';
|
||||
stream->s_cursor += count;
|
||||
str->s_len = b_max(size_t, str->s_len, stream->s_cursor + count);
|
||||
|
||||
*nr_written = count;
|
||||
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
static enum b_status stream_seek(
|
||||
struct b_stream *stream, long long offset, b_stream_seek_origin origin)
|
||||
{
|
||||
struct b_string *str = stream->s_ptr;
|
||||
|
||||
size_t abs_offset;
|
||||
switch (origin) {
|
||||
case B_STREAM_SEEK_START:
|
||||
abs_offset = offset;
|
||||
break;
|
||||
case B_STREAM_SEEK_CURRENT:
|
||||
abs_offset = stream->s_cursor + offset;
|
||||
break;
|
||||
case B_STREAM_SEEK_END:
|
||||
abs_offset = str->s_len + offset;
|
||||
break;
|
||||
default:
|
||||
return B_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
stream->s_cursor = abs_offset;
|
||||
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
static enum b_status stream_reserve(struct b_stream *stream, size_t len)
|
||||
{
|
||||
struct b_string *str = stream->s_ptr;
|
||||
|
||||
size_t new_capacity = str->s_len + len;
|
||||
return b_string_reserve(str, new_capacity);
|
||||
}
|
||||
|
||||
enum b_status b_string_open_stream(struct b_string *str, struct b_stream **out)
|
||||
{
|
||||
struct b_stream *stream = malloc(sizeof *stream);
|
||||
if (!stream) {
|
||||
return B_ERR_NO_MEMORY;
|
||||
}
|
||||
|
||||
memset(stream, 0x0, sizeof *stream);
|
||||
|
||||
stream->s_mode |= B_STREAM_READ | B_STREAM_WRITE;
|
||||
|
||||
stream->s_ptr = b_string_retain(str);
|
||||
stream->s_close = stream_close;
|
||||
stream->s_getc = stream_getc;
|
||||
stream->s_read = stream_read;
|
||||
stream->s_write = stream_write;
|
||||
stream->s_seek = stream_seek;
|
||||
stream->s_reserve = stream_reserve;
|
||||
|
||||
*out = stream;
|
||||
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
static void string_insert(
|
||||
struct b_string *dest, const char *src, size_t len, size_t at)
|
||||
{
|
||||
if (at >= dest->s_len) {
|
||||
at = dest->s_len;
|
||||
}
|
||||
|
||||
size_t new_size = dest->s_len + len;
|
||||
if (dest->s_max < new_size) {
|
||||
string_change_capacity(dest, new_size);
|
||||
}
|
||||
|
||||
char *dest_buf = string_ptr(dest);
|
||||
char *from = dest_buf + at;
|
||||
char *to = dest_buf + at + len;
|
||||
|
||||
memmove(to, from, dest->s_len - at);
|
||||
memcpy(from, src, len);
|
||||
dest_buf[new_size] = '\0';
|
||||
|
||||
dest->s_len = new_size;
|
||||
}
|
||||
|
||||
static void string_insertf(
|
||||
struct b_string *dest, size_t at, const char *format, va_list arg)
|
||||
{
|
||||
char buf[1024];
|
||||
size_t len = vsnprintf(buf, sizeof buf, format, arg);
|
||||
string_insert(dest, buf, len, at);
|
||||
}
|
||||
|
||||
void b_string_insert_s(struct b_string *dest, const struct b_string *src, size_t at)
|
||||
{
|
||||
string_insert(dest, b_string_ptr(src), src->s_len, at);
|
||||
}
|
||||
|
||||
void b_string_insert_cstr(struct b_string *dest, const char *src, size_t at)
|
||||
{
|
||||
string_insert(dest, src, strlen(src), at);
|
||||
}
|
||||
|
||||
void b_string_insert_cstrf(struct b_string *dest, size_t at, const char *format, ...)
|
||||
{
|
||||
va_list arg;
|
||||
va_start(arg, format);
|
||||
string_insertf(dest, at, format, arg);
|
||||
va_end(arg);
|
||||
}
|
||||
|
||||
void b_string_insert_cstrn(b_string *dest, const char *src, size_t len, size_t at)
|
||||
{
|
||||
string_insert(dest, src, len, at);
|
||||
}
|
||||
|
||||
void b_string_append_s(struct b_string *dest, const struct b_string *src)
|
||||
{
|
||||
b_string_insert_s(dest, src, SIZE_MAX);
|
||||
}
|
||||
|
||||
void b_string_append_cstr(struct b_string *dest, const char *src)
|
||||
{
|
||||
b_string_insert_cstr(dest, src, SIZE_MAX);
|
||||
}
|
||||
|
||||
void b_string_append_cstrf(struct b_string *dest, const char *format, ...)
|
||||
{
|
||||
va_list arg;
|
||||
va_start(arg, format);
|
||||
string_insertf(dest, SIZE_MAX, format, arg);
|
||||
va_end(arg);
|
||||
}
|
||||
|
||||
void b_string_prepend_s(struct b_string *dest, const struct b_string *src)
|
||||
{
|
||||
b_string_insert_s(dest, src, 0);
|
||||
}
|
||||
|
||||
void b_string_prepend_cstr(struct b_string *dest, const char *src)
|
||||
{
|
||||
b_string_insert_cstr(dest, src, 0);
|
||||
}
|
||||
|
||||
void b_string_prepend_cstrf(struct b_string *dest, const char *format, ...)
|
||||
{
|
||||
va_list arg;
|
||||
va_start(arg, format);
|
||||
string_insertf(dest, 0, format, arg);
|
||||
va_end(arg);
|
||||
}
|
||||
|
||||
void b_string_clear(struct b_string *str)
|
||||
{
|
||||
if (str->s_len == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
char *s = string_ptr(str);
|
||||
*s = '\0';
|
||||
str->s_len = 0;
|
||||
}
|
||||
|
||||
size_t b_string_get_size(const struct b_string *str, b_strlen_flags flags)
|
||||
{
|
||||
if (flags != B_STRLEN_NORMAL) {
|
||||
return b_strlen(b_string_ptr(str), flags);
|
||||
}
|
||||
|
||||
return str->s_len;
|
||||
}
|
||||
|
||||
size_t b_string_get_capacity(const struct b_string *str)
|
||||
{
|
||||
return str->s_max;
|
||||
}
|
||||
|
||||
char b_string_front(const struct b_string *str)
|
||||
{
|
||||
if (str->s_len == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *s = b_string_ptr(str);
|
||||
return s[0];
|
||||
}
|
||||
|
||||
char b_string_back(const struct b_string *str)
|
||||
{
|
||||
if (str->s_len == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *s = b_string_ptr(str);
|
||||
return s[str->s_len - 1];
|
||||
}
|
||||
|
||||
void b_string_pop_back(struct b_string *str)
|
||||
{
|
||||
if (str->s_len == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
char *s = string_ptr(str);
|
||||
|
||||
s[str->s_len - 1] = '\0';
|
||||
str->s_len--;
|
||||
}
|
||||
|
||||
const char *b_string_ptr(const struct b_string *str)
|
||||
{
|
||||
if (string_is_inline(str)) {
|
||||
return str->s_data.d_inline;
|
||||
}
|
||||
|
||||
return str->s_data.d_external;
|
||||
}
|
||||
|
||||
struct b_string *b_string_substr(const struct b_string *str, size_t start, size_t len)
|
||||
{
|
||||
if (start > b_string_get_size(str, B_STRLEN_NORMAL)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (start + len > b_string_get_size(str, B_STRLEN_NORMAL)) {
|
||||
len = b_string_get_size(str, B_STRLEN_NORMAL) - start;
|
||||
}
|
||||
|
||||
struct b_string *newstr = b_string_create();
|
||||
b_string_reserve(newstr, len);
|
||||
|
||||
const char *src = b_string_ptr(str) + start;
|
||||
char *dest = string_ptr(newstr);
|
||||
|
||||
memcpy(dest, src, len);
|
||||
newstr->s_len = len;
|
||||
|
||||
return newstr;
|
||||
}
|
||||
|
||||
static void string_release(struct b_dsref *obj)
|
||||
{
|
||||
struct b_string *str = B_STRING(obj);
|
||||
if (!string_is_inline(str)) {
|
||||
free(string_ptr(str));
|
||||
}
|
||||
}
|
||||
|
||||
static void string_to_string(struct b_dsref *obj, struct b_stream *out)
|
||||
{
|
||||
b_string *str = B_STRING(obj);
|
||||
b_stream_write_fmt(out, NULL, "%s", b_string_ptr(str));
|
||||
}
|
||||
|
||||
char *b_strdup(const char *s)
|
||||
{
|
||||
size_t len = strlen(s);
|
||||
char *p = malloc(len + 1);
|
||||
if (!p) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(p, s, len);
|
||||
p[len] = '\0';
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
size_t b_strlen(const char *s, b_strlen_flags flags)
|
||||
{
|
||||
if (!(flags & (B_STRLEN_IGNORE_ESC | B_STRLEN_IGNORE_MOD))) {
|
||||
return strlen(s);
|
||||
}
|
||||
|
||||
size_t out = 0;
|
||||
for (size_t i = 0; s[i]; i++) {
|
||||
if (s[i] == '\033' && (flags & B_STRLEN_IGNORE_ESC)) {
|
||||
while (!isalpha(s[i]) && s[i]) {
|
||||
i++;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (s[i] == '[' && (flags & B_STRLEN_IGNORE_MOD)) {
|
||||
i++;
|
||||
if (s[i] == '[') {
|
||||
out++;
|
||||
continue;
|
||||
}
|
||||
|
||||
while (s[i] != ']' && s[i]) {
|
||||
i++;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
out++;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
b_dsref_type_id b_string_type_id(void)
|
||||
{
|
||||
return (b_dsref_type_id)&string_type;
|
||||
}
|
||||
21
ds/string.h
Normal file
21
ds/string.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifndef _BLUELIB_STRING_H_
|
||||
#define _BLUELIB_STRING_H_
|
||||
|
||||
#include "object.h"
|
||||
|
||||
/* maximum length of string that can be stored inline, not including null-terminator */
|
||||
#define STRING_INLINE_CAPACITY 15
|
||||
|
||||
struct b_string {
|
||||
struct b_dsref s_base;
|
||||
/* length of string, not including null-terminator */
|
||||
unsigned int s_len;
|
||||
/* maximum length of string storable in the currently-allocated buffer, not including null terminator */
|
||||
unsigned int s_max;
|
||||
union {
|
||||
char d_inline[STRING_INLINE_CAPACITY + 1];
|
||||
char *d_external;
|
||||
} s_data;
|
||||
};
|
||||
|
||||
#endif
|
||||
300
ds/tree.c
Normal file
300
ds/tree.c
Normal file
@@ -0,0 +1,300 @@
|
||||
#include "tree.h"
|
||||
|
||||
#include <blue/ds/object.h>
|
||||
#include <blue/ds/tree.h>
|
||||
#include <blue/ds/type.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define ITERATOR_RECURSIVE 0x01u
|
||||
|
||||
#define ITERATOR_IS_RECURSIVE(it) (((it)->_f01 & ITERATOR_RECURSIVE) != 0)
|
||||
#define ITERATOR_UNSET_RECURSIVE(it) ((it)->_f01 &= ~ITERATOR_RECURSIVE)
|
||||
#define ITERATOR_SET_RECURSIVE(it) ((it)->_f01 |= ITERATOR_RECURSIVE)
|
||||
|
||||
#define NODE_PARENT(n) ((n)->__p01)
|
||||
#define NODE_FIRST_CHILD(n) ((n)->__p02)
|
||||
#define NODE_NEXT_SIBLING(n) ((n)->__p03)
|
||||
|
||||
static struct b_dsref_type tree_type = {
|
||||
.t_name = "corelib::tree",
|
||||
.t_flags = B_DSREF_FUNDAMENTAL,
|
||||
.t_id = B_DSREF_TYPE_TREE,
|
||||
.t_instance_size = sizeof(struct b_tree),
|
||||
};
|
||||
|
||||
struct b_tree *b_tree_create(void)
|
||||
{
|
||||
struct b_tree *tree
|
||||
= (struct b_tree *)b_dsref_type_instantiate(&tree_type);
|
||||
if (!tree) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
void b_tree_set_root(struct b_tree *tree, struct b_tree_node *node)
|
||||
{
|
||||
tree->t_root = node;
|
||||
}
|
||||
|
||||
void b_tree_node_add_child(struct b_tree_node *parent, struct b_tree_node *child)
|
||||
{
|
||||
if (NODE_PARENT(child)) {
|
||||
return;
|
||||
}
|
||||
|
||||
NODE_PARENT(child) = parent;
|
||||
if (!NODE_FIRST_CHILD(parent)) {
|
||||
NODE_FIRST_CHILD(parent) = child;
|
||||
return;
|
||||
}
|
||||
|
||||
struct b_tree_node *cur = NODE_FIRST_CHILD(parent);
|
||||
while (NODE_NEXT_SIBLING(cur)) {
|
||||
cur = NODE_NEXT_SIBLING(cur);
|
||||
}
|
||||
|
||||
NODE_NEXT_SIBLING(cur) = child;
|
||||
}
|
||||
|
||||
void b_tree_node_add_sibling(struct b_tree_node *node, struct b_tree_node *to_add)
|
||||
{
|
||||
if (NODE_PARENT(to_add) || !NODE_PARENT(node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
b_tree_node_add_child(NODE_PARENT(node), to_add);
|
||||
}
|
||||
|
||||
struct b_tree_node *b_tree_node_get_child(struct b_tree_node *node, size_t at)
|
||||
{
|
||||
size_t i = 0;
|
||||
struct b_tree_node *cur = NODE_FIRST_CHILD(node);
|
||||
|
||||
while (i < at) {
|
||||
if (!cur) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cur = NODE_NEXT_SIBLING(cur);
|
||||
i++;
|
||||
}
|
||||
|
||||
return cur;
|
||||
}
|
||||
|
||||
static bool tree_iterator_next(struct b_iterator *it)
|
||||
{
|
||||
return b_tree_iterator_next((struct b_tree_iterator *)it);
|
||||
}
|
||||
|
||||
static b_status tree_iterator_erase(struct b_iterator *it)
|
||||
{
|
||||
return b_tree_iterator_erase((struct b_tree_iterator *)it);
|
||||
}
|
||||
|
||||
static bool tree_iterator_is_valid(const struct b_iterator *it)
|
||||
{
|
||||
return b_tree_iterator_is_valid((const struct b_tree_iterator *)it);
|
||||
}
|
||||
|
||||
struct b_tree_node *b_tree_node_get_parent(struct b_tree_node *node)
|
||||
{
|
||||
return NODE_PARENT(node);
|
||||
}
|
||||
|
||||
int b_tree_iterator_begin(struct b_tree *tree, b_tree_iterator *it)
|
||||
{
|
||||
return b_tree_iterator_begin_at_node_recursive(tree->t_root, it);
|
||||
}
|
||||
|
||||
static b_iterator_ops it_ops = {
|
||||
.it_next = tree_iterator_next,
|
||||
.it_erase = tree_iterator_erase,
|
||||
.it_close = NULL,
|
||||
.it_is_valid = tree_iterator_is_valid,
|
||||
};
|
||||
|
||||
int b_tree_iterator_begin_at_node(struct b_tree_node *node, b_tree_iterator *it)
|
||||
{
|
||||
it->node = NODE_FIRST_CHILD(node);
|
||||
it->i = 0;
|
||||
it->depth = 0;
|
||||
|
||||
it->_base.it_ops = &it_ops;
|
||||
|
||||
ITERATOR_UNSET_RECURSIVE(it);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int b_tree_iterator_begin_at_node_recursive(
|
||||
struct b_tree_node *node, b_tree_iterator *it)
|
||||
{
|
||||
it->node = node;
|
||||
it->i = 0;
|
||||
it->depth = 0;
|
||||
it->_base.it_ops = &it_ops;
|
||||
|
||||
ITERATOR_SET_RECURSIVE(it);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct b_tree_node *next_node(
|
||||
const struct b_tree_node *node, bool recursive, int *depth_diff)
|
||||
{
|
||||
if (!node) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!recursive) {
|
||||
node = NODE_NEXT_SIBLING(node);
|
||||
return node;
|
||||
}
|
||||
|
||||
int d = 0;
|
||||
struct b_tree_node *next = NODE_FIRST_CHILD(node);
|
||||
if (next) {
|
||||
d = 1;
|
||||
*depth_diff = d;
|
||||
return next;
|
||||
}
|
||||
|
||||
const struct b_tree_node *n = node;
|
||||
next = NODE_NEXT_SIBLING(n);
|
||||
while (!next) {
|
||||
n = NODE_PARENT(n);
|
||||
if (!n) {
|
||||
break;
|
||||
}
|
||||
|
||||
d--;
|
||||
next = NODE_NEXT_SIBLING(n);
|
||||
}
|
||||
|
||||
*depth_diff = d;
|
||||
return next;
|
||||
}
|
||||
|
||||
bool b_tree_iterator_next(struct b_tree_iterator *it)
|
||||
{
|
||||
int depth_diff = 0;
|
||||
const struct b_tree_node *next
|
||||
= next_node(it->node, ITERATOR_IS_RECURSIVE(it), &depth_diff);
|
||||
|
||||
if (next) {
|
||||
it->depth += depth_diff;
|
||||
it->i++;
|
||||
} else {
|
||||
it->depth = 0;
|
||||
it->i = 0;
|
||||
}
|
||||
|
||||
it->node = (struct b_tree_node *)next;
|
||||
return it->node != NULL;
|
||||
}
|
||||
|
||||
static void remove_node(struct b_tree_node *node)
|
||||
{
|
||||
struct b_tree_node *parent = NODE_PARENT(node);
|
||||
if (!parent) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct b_tree_node *n0 = NULL, *n1 = NULL;
|
||||
n0 = NODE_FIRST_CHILD(parent);
|
||||
|
||||
while (n0) {
|
||||
if (n0 == node) {
|
||||
break;
|
||||
}
|
||||
|
||||
n1 = n0;
|
||||
n0 = NODE_NEXT_SIBLING(n0);
|
||||
}
|
||||
|
||||
if (!n0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (n1) {
|
||||
NODE_NEXT_SIBLING(n1) = NODE_NEXT_SIBLING(n0);
|
||||
} else {
|
||||
NODE_FIRST_CHILD(parent) = NODE_NEXT_SIBLING(n0);
|
||||
}
|
||||
|
||||
NODE_PARENT(n0) = NODE_NEXT_SIBLING(n0) = NULL;
|
||||
}
|
||||
|
||||
static void reparent_children(
|
||||
struct b_tree_node *old_parent, struct b_tree_node *new_parent)
|
||||
{
|
||||
struct b_tree_node *last = NODE_FIRST_CHILD(new_parent);
|
||||
while (last && NODE_NEXT_SIBLING(last)) {
|
||||
last = NODE_NEXT_SIBLING(last);
|
||||
}
|
||||
|
||||
struct b_tree_node *cur = NODE_FIRST_CHILD(old_parent);
|
||||
while (cur) {
|
||||
struct b_tree_node *next = NODE_NEXT_SIBLING(cur);
|
||||
NODE_PARENT(cur) = new_parent;
|
||||
NODE_NEXT_SIBLING(cur) = NULL;
|
||||
|
||||
if (last) {
|
||||
NODE_NEXT_SIBLING(last) = cur;
|
||||
} else {
|
||||
NODE_FIRST_CHILD(new_parent) = cur;
|
||||
}
|
||||
|
||||
last = cur;
|
||||
cur = next;
|
||||
}
|
||||
}
|
||||
|
||||
b_status b_tree_iterator_erase(struct b_tree_iterator *it)
|
||||
{
|
||||
if (!it->node) {
|
||||
return B_ERR_OUT_OF_BOUNDS;
|
||||
}
|
||||
|
||||
struct b_tree_node *parent = NODE_PARENT(it->node);
|
||||
if (!parent) {
|
||||
return B_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
int d = 0;
|
||||
struct b_tree_node *n = it->node;
|
||||
struct b_tree_node *next = NODE_NEXT_SIBLING(n);
|
||||
|
||||
if (!next) {
|
||||
next = NODE_FIRST_CHILD(n);
|
||||
}
|
||||
|
||||
while (!next) {
|
||||
n = NODE_PARENT(n);
|
||||
if (!n) {
|
||||
break;
|
||||
}
|
||||
|
||||
d--;
|
||||
next = NODE_NEXT_SIBLING(n);
|
||||
}
|
||||
|
||||
remove_node(it->node);
|
||||
reparent_children(it->node, parent);
|
||||
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
bool b_tree_iterator_is_valid(const struct b_tree_iterator *it)
|
||||
{
|
||||
return it->node != NULL;
|
||||
}
|
||||
|
||||
b_dsref_type_id b_tree_type_id(void)
|
||||
{
|
||||
return (b_dsref_type_id)&tree_type;
|
||||
}
|
||||
14
ds/tree.h
Normal file
14
ds/tree.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef _B_TREE_H_
|
||||
#define _B_TREE_H_
|
||||
|
||||
#include "../object.h"
|
||||
|
||||
#include <blue/core/queue.h>
|
||||
#include <blue/ds/tree.h>
|
||||
|
||||
struct b_tree {
|
||||
struct b_dsref t_base;
|
||||
struct b_tree_node *t_root;
|
||||
};
|
||||
|
||||
#endif
|
||||
29
ds/type.c
Normal file
29
ds/type.c
Normal file
@@ -0,0 +1,29 @@
|
||||
#include "object.h"
|
||||
|
||||
#include <blue/core/queue.h>
|
||||
#include <blue/ds/type.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
struct b_dsref *b_dsref_type_instantiate(const b_dsref_type *type)
|
||||
{
|
||||
if (!type || type->t_instance_size < sizeof(struct b_dsref)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct b_dsref *out = malloc(type->t_instance_size);
|
||||
if (!out) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(out, 0x0, type->t_instance_size);
|
||||
|
||||
out->ob_ref = 1;
|
||||
out->ob_type = type;
|
||||
|
||||
if (type->t_init) {
|
||||
type->t_init(out);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
198
ds/uuid.c
Normal file
198
ds/uuid.c
Normal file
@@ -0,0 +1,198 @@
|
||||
#include "uuid.h"
|
||||
|
||||
#include <blue/core/stringstream.h>
|
||||
#include <blue/ds/string.h>
|
||||
#include <blue/ds/type.h>
|
||||
#include <blue/ds/uuid.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static struct b_dsref_type uuid_type = {
|
||||
.t_name = "corelib::uuid",
|
||||
.t_flags = B_DSREF_FUNDAMENTAL,
|
||||
.t_id = B_DSREF_TYPE_UUID,
|
||||
.t_instance_size = sizeof(struct b_uuid),
|
||||
};
|
||||
|
||||
struct b_uuid *b_uuid_create(void)
|
||||
{
|
||||
struct b_uuid *out
|
||||
= (struct b_uuid *)b_dsref_type_instantiate(&uuid_type);
|
||||
if (!out) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
struct b_uuid *b_uuid_create_from_bytes(
|
||||
unsigned char u00, unsigned char u01, unsigned char u02,
|
||||
unsigned char u03, unsigned char u04, unsigned char u05,
|
||||
unsigned char u06, unsigned char u07, unsigned char u08,
|
||||
unsigned char u09, unsigned char u10, unsigned char u11,
|
||||
unsigned char u12, unsigned char u13, unsigned char u14, unsigned char u15)
|
||||
{
|
||||
struct b_uuid *uuid = b_uuid_create();
|
||||
if (!uuid) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uuid->uuid_bytes.uuid_bytes[0] = u00;
|
||||
uuid->uuid_bytes.uuid_bytes[1] = u01;
|
||||
uuid->uuid_bytes.uuid_bytes[2] = u02;
|
||||
uuid->uuid_bytes.uuid_bytes[3] = u03;
|
||||
uuid->uuid_bytes.uuid_bytes[4] = u04;
|
||||
uuid->uuid_bytes.uuid_bytes[5] = u05;
|
||||
uuid->uuid_bytes.uuid_bytes[6] = u06;
|
||||
uuid->uuid_bytes.uuid_bytes[7] = u07;
|
||||
uuid->uuid_bytes.uuid_bytes[8] = u08;
|
||||
uuid->uuid_bytes.uuid_bytes[9] = u09;
|
||||
uuid->uuid_bytes.uuid_bytes[10] = u10;
|
||||
uuid->uuid_bytes.uuid_bytes[11] = u11;
|
||||
uuid->uuid_bytes.uuid_bytes[12] = u12;
|
||||
uuid->uuid_bytes.uuid_bytes[13] = u13;
|
||||
uuid->uuid_bytes.uuid_bytes[14] = u14;
|
||||
uuid->uuid_bytes.uuid_bytes[15] = u15;
|
||||
|
||||
return uuid;
|
||||
}
|
||||
|
||||
struct b_uuid *b_uuid_create_from_bytev(const unsigned char bytes[B_UUID_NBYTES])
|
||||
{
|
||||
struct b_uuid *uuid = b_uuid_create();
|
||||
if (!uuid) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(uuid->uuid_bytes.uuid_bytes, bytes, B_UUID_NBYTES);
|
||||
|
||||
return uuid;
|
||||
}
|
||||
|
||||
struct b_uuid *b_uuid_create_from_uuid_bytes(const struct b_uuid_bytes *bytes)
|
||||
{
|
||||
return b_uuid_create_from_bytev(bytes->uuid_bytes);
|
||||
}
|
||||
|
||||
struct b_uuid *b_uuid_create_from_string(const struct b_string *string)
|
||||
{
|
||||
return b_uuid_create_from_cstr(b_string_ptr(string));
|
||||
}
|
||||
|
||||
struct b_uuid *b_uuid_create_from_cstr(const char *str)
|
||||
{
|
||||
struct b_uuid_bytes bytes;
|
||||
|
||||
bool valid = true;
|
||||
bool is_guid = false;
|
||||
if (*str == '{') {
|
||||
is_guid = true;
|
||||
str++;
|
||||
}
|
||||
|
||||
size_t i, byte = 0;
|
||||
for (i = 0; str[i] && byte < B_UUID_NBYTES;) {
|
||||
if (i == 8 || i == 13 || i == 18 || i == 23) {
|
||||
if (str[i] != '-') {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
char n[3];
|
||||
n[0] = str[i];
|
||||
n[1] = str[i + 1];
|
||||
n[2] = '\0';
|
||||
|
||||
if (!isxdigit(n[0]) || !isxdigit(n[1])) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
|
||||
char *p;
|
||||
unsigned long v = strtoul(n, &p, 16);
|
||||
bytes.uuid_bytes[byte] = v;
|
||||
|
||||
byte++;
|
||||
i += 2;
|
||||
}
|
||||
|
||||
if (str[i] == '}') {
|
||||
if (is_guid) {
|
||||
i++;
|
||||
} else {
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (str[i] != '\0' || byte != B_UUID_NBYTES) {
|
||||
valid = false;
|
||||
}
|
||||
|
||||
if (!valid) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return b_uuid_create_from_uuid_bytes(&bytes);
|
||||
}
|
||||
|
||||
b_status b_uuid_to_string(const struct b_uuid *uuid, struct b_string *out)
|
||||
{
|
||||
char str[B_UUID_STRING_MAX];
|
||||
b_uuid_to_cstr(uuid, str);
|
||||
b_string_append_cstr(out, str);
|
||||
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
b_status b_uuid_to_cstr(const struct b_uuid *uuid, char out[B_UUID_STRING_MAX])
|
||||
{
|
||||
snprintf(
|
||||
out, B_UUID_STRING_MAX,
|
||||
"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%"
|
||||
"02x%02x",
|
||||
uuid->uuid_bytes.uuid_bytes[0], uuid->uuid_bytes.uuid_bytes[1],
|
||||
uuid->uuid_bytes.uuid_bytes[2], uuid->uuid_bytes.uuid_bytes[3],
|
||||
uuid->uuid_bytes.uuid_bytes[4], uuid->uuid_bytes.uuid_bytes[5],
|
||||
uuid->uuid_bytes.uuid_bytes[6], uuid->uuid_bytes.uuid_bytes[7],
|
||||
uuid->uuid_bytes.uuid_bytes[8], uuid->uuid_bytes.uuid_bytes[9],
|
||||
uuid->uuid_bytes.uuid_bytes[10], uuid->uuid_bytes.uuid_bytes[11],
|
||||
uuid->uuid_bytes.uuid_bytes[12], uuid->uuid_bytes.uuid_bytes[13],
|
||||
uuid->uuid_bytes.uuid_bytes[14], uuid->uuid_bytes.uuid_bytes[15]);
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
b_status b_uuid_to_stringstream(
|
||||
const struct b_uuid *uuid, struct b_stringstream *out)
|
||||
{
|
||||
char str[B_UUID_STRING_MAX];
|
||||
b_uuid_to_cstr(uuid, str);
|
||||
b_stringstream_add(out, str);
|
||||
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
void b_uuid_get_bytes(const struct b_uuid *uuid, unsigned char bytes[B_UUID_NBYTES])
|
||||
{
|
||||
memcpy(bytes, uuid->uuid_bytes.uuid_bytes, B_UUID_NBYTES);
|
||||
}
|
||||
|
||||
void b_uuid_get_uuid_bytes(const struct b_uuid *uuid, struct b_uuid_bytes *bytes)
|
||||
{
|
||||
memcpy(bytes, &uuid->uuid_bytes, sizeof *bytes);
|
||||
}
|
||||
|
||||
struct b_uuid_bytes *b_uuid_ptr(struct b_uuid *uuid)
|
||||
{
|
||||
return &uuid->uuid_bytes;
|
||||
}
|
||||
|
||||
b_dsref_type_id b_uuid_type_id(void)
|
||||
{
|
||||
return (b_dsref_type_id)&uuid_type;
|
||||
}
|
||||
Reference in New Issue
Block a user