add object module from corelib
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
cmake_minimum_required(VERSION 3.25)
|
cmake_minimum_required(VERSION 3.25)
|
||||||
project(bluelib C)
|
project(bluelib C)
|
||||||
|
|
||||||
set(b_modules core)
|
set(b_modules core object)
|
||||||
|
|
||||||
set(b_system_name ${CMAKE_SYSTEM_NAME})
|
set(b_system_name ${CMAKE_SYSTEM_NAME})
|
||||||
string(TOLOWER ${b_system_name} b_system_name)
|
string(TOLOWER ${b_system_name} b_system_name)
|
||||||
|
|||||||
@@ -1,4 +1,11 @@
|
|||||||
macro(add_bluelib_module module_name)
|
function(add_bluelib_module)
|
||||||
|
set(options)
|
||||||
|
set(one_value_args NAME)
|
||||||
|
set(multi_value_args DEPENDENCIES)
|
||||||
|
cmake_parse_arguments(PARSE_ARGV 0 arg "${options}" "${one_value_args}" "${multi_value_args}")
|
||||||
|
|
||||||
|
set(module_name ${arg_NAME})
|
||||||
|
|
||||||
file(GLOB sources *.c *.h)
|
file(GLOB sources *.c *.h)
|
||||||
file(GLOB sys_sources sys/${b_system_name}/*.c sys/${b_system_name}/*.h)
|
file(GLOB sys_sources sys/${b_system_name}/*.c sys/${b_system_name}/*.h)
|
||||||
set(root_header include/blue/${module_name}.h)
|
set(root_header include/blue/${module_name}.h)
|
||||||
@@ -16,6 +23,10 @@ macro(add_bluelib_module module_name)
|
|||||||
target_include_directories(blue-${module_name}-obj PUBLIC include/)
|
target_include_directories(blue-${module_name}-obj PUBLIC include/)
|
||||||
target_compile_definitions(blue-${module_name}-obj PUBLIC ${module_preproc_token})
|
target_compile_definitions(blue-${module_name}-obj PUBLIC ${module_preproc_token})
|
||||||
|
|
||||||
|
foreach (dep ${arg_DEPENDENCIES})
|
||||||
|
target_link_libraries(blue-${module_name}-obj blue-${dep}-obj)
|
||||||
|
endforeach (dep)
|
||||||
|
|
||||||
message(STATUS "Building module ${module_name} (shared)")
|
message(STATUS "Building module ${module_name} (shared)")
|
||||||
add_library(blue-${module_name} SHARED $<TARGET_OBJECTS:blue-${module_name}-obj>)
|
add_library(blue-${module_name} SHARED $<TARGET_OBJECTS:blue-${module_name}-obj>)
|
||||||
message(STATUS "Building module ${module_name} (static)")
|
message(STATUS "Building module ${module_name} (static)")
|
||||||
@@ -24,7 +35,12 @@ macro(add_bluelib_module module_name)
|
|||||||
target_include_directories(blue-${module_name} PUBLIC include/)
|
target_include_directories(blue-${module_name} PUBLIC include/)
|
||||||
target_include_directories(blue-${module_name}-s PUBLIC include/)
|
target_include_directories(blue-${module_name}-s PUBLIC include/)
|
||||||
|
|
||||||
|
foreach (dep ${arg_DEPENDENCIES})
|
||||||
|
target_link_libraries(blue-${module_name} blue-${dep})
|
||||||
|
target_link_libraries(blue-${module_name}-s blue-${dep}-s)
|
||||||
|
endforeach (dep)
|
||||||
|
|
||||||
install(TARGETS blue-${module_name} blue-${module_name}-s)
|
install(TARGETS blue-${module_name} blue-${module_name}-s)
|
||||||
install(FILES ${root_header} DESTINATION include/blue)
|
install(FILES ${root_header} DESTINATION include/blue)
|
||||||
install(FILES ${headers} DESTINATION include/blue/${module_name})
|
install(FILES ${headers} DESTINATION include/blue/${module_name})
|
||||||
endmacro(add_bluelib_module)
|
endfunction(add_bluelib_module)
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
include(../cmake/Templates.cmake)
|
include(../cmake/Templates.cmake)
|
||||||
|
|
||||||
add_bluelib_module(core)
|
add_bluelib_module(NAME core)
|
||||||
|
|||||||
@@ -9,21 +9,22 @@ typedef struct b_stringstream {
|
|||||||
size_t ss_len;
|
size_t ss_len;
|
||||||
size_t ss_max;
|
size_t ss_max;
|
||||||
unsigned char ss_alloc;
|
unsigned char ss_alloc;
|
||||||
|
int *ss_istack;
|
||||||
|
int ss_add_indent;
|
||||||
|
size_t ss_istack_ptr, ss_istack_size;
|
||||||
} b_stringstream;
|
} b_stringstream;
|
||||||
|
|
||||||
extern void b_stringstream_begin(b_stringstream *strv, char *buf, size_t max);
|
extern void b_stringstream_begin(b_stringstream *strv, char *buf, size_t max);
|
||||||
extern void b_stringstream_begin_dynamic(b_stringstream *strv);
|
extern void b_stringstream_begin_dynamic(b_stringstream *strv);
|
||||||
|
|
||||||
|
extern void b_stringstream_push_indent(b_stringstream *strv, int indent);
|
||||||
|
extern void b_stringstream_pop_indent(b_stringstream *strv);
|
||||||
|
|
||||||
extern b_status b_stringstream_add(b_stringstream *strv, const char *str);
|
extern b_status b_stringstream_add(b_stringstream *strv, const char *str);
|
||||||
extern b_status b_stringstream_addf(
|
extern b_status b_stringstream_addf(b_stringstream *strv, const char *format, ...);
|
||||||
b_stringstream *strv,
|
|
||||||
const char *format,
|
|
||||||
...);
|
|
||||||
extern b_status b_stringstream_addv(b_stringstream *strv, const char **strs);
|
extern b_status b_stringstream_addv(b_stringstream *strv, const char **strs);
|
||||||
extern b_status b_stringstream_addvl(
|
extern b_status b_stringstream_addvl(
|
||||||
b_stringstream *strv,
|
b_stringstream *strv, const char **strs, size_t count);
|
||||||
const char **strs,
|
|
||||||
size_t count);
|
|
||||||
extern b_status b_stringstream_add_many(b_stringstream *strv, ...);
|
extern b_status b_stringstream_add_many(b_stringstream *strv, ...);
|
||||||
extern char *b_stringstream_end(b_stringstream *strv);
|
extern char *b_stringstream_end(b_stringstream *strv);
|
||||||
|
|
||||||
|
|||||||
@@ -20,28 +20,91 @@ void b_stringstream_begin_dynamic(b_stringstream *ss)
|
|||||||
ss->ss_alloc = 1;
|
ss->ss_alloc = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static b_status ss_builder_push_string(b_stringstream *ss, const char *s, size_t len)
|
static int current_indent(struct b_stringstream *ss)
|
||||||
{
|
{
|
||||||
if (ss->ss_len + len >= ss->ss_max && ss->ss_alloc == 1) {
|
if (!ss->ss_istack || !ss->ss_istack_size) {
|
||||||
char *new_buf = realloc(ss->ss_buf, ss->ss_len + len + 1);
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ss->ss_istack[ss->ss_istack_ptr];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __formatter_putchar(struct b_stringstream *ss, char c)
|
||||||
|
{
|
||||||
|
if (ss->ss_len + 1 >= ss->ss_max && ss->ss_alloc == 1) {
|
||||||
|
char *new_buf = realloc(ss->ss_buf, ss->ss_len + 8);
|
||||||
if (!new_buf) {
|
if (!new_buf) {
|
||||||
return B_ERR_NO_MEMORY;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ss->ss_buf = new_buf;
|
ss->ss_buf = new_buf;
|
||||||
ss->ss_max = ss->ss_len + len + 1;
|
ss->ss_max = ss->ss_len + 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < len; i++) {
|
ss->ss_buf[ss->ss_len++] = c;
|
||||||
if (ss->ss_len < ss->ss_max) {
|
ss->ss_buf[ss->ss_len] = '\0';
|
||||||
ss->ss_buf[ss->ss_len++] = s[i];
|
}
|
||||||
ss->ss_buf[ss->ss_len] = 0;
|
|
||||||
|
static void formatter_putchar(struct b_stringstream *f, char c)
|
||||||
|
{
|
||||||
|
if (f->ss_add_indent && c != '\n') {
|
||||||
|
int indent = current_indent(f);
|
||||||
|
for (int i = 0; i < indent; i++) {
|
||||||
|
__formatter_putchar(f, ' ');
|
||||||
|
__formatter_putchar(f, ' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
f->ss_add_indent = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
__formatter_putchar(f, c);
|
||||||
|
|
||||||
|
if (c == '\n') {
|
||||||
|
f->ss_add_indent = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static b_status ss_builder_push_string(b_stringstream *ss, const char *s, size_t len)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < len; i++) {
|
||||||
|
formatter_putchar(ss, s[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return B_SUCCESS;
|
return B_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void b_stringstream_push_indent(struct b_stringstream *ss, int indent)
|
||||||
|
{
|
||||||
|
if (!ss->ss_istack) {
|
||||||
|
ss->ss_istack = calloc(4, sizeof(int));
|
||||||
|
ss->ss_istack_size = 4;
|
||||||
|
ss->ss_istack_ptr = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ss->ss_istack_ptr + 1 > ss->ss_istack_size) {
|
||||||
|
int *buf = realloc(
|
||||||
|
ss->ss_istack, (ss->ss_istack_size + 4) * sizeof(int));
|
||||||
|
if (!buf) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ss->ss_istack = buf;
|
||||||
|
ss->ss_istack_size += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cur_indent = ss->ss_istack[ss->ss_istack_ptr];
|
||||||
|
ss->ss_istack[++ss->ss_istack_ptr] = cur_indent + indent;
|
||||||
|
}
|
||||||
|
|
||||||
|
void b_stringstream_pop_indent(b_stringstream *strv)
|
||||||
|
{
|
||||||
|
if (!strv->ss_istack || !strv->ss_istack_size || !strv->ss_istack_ptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
strv->ss_istack_ptr--;
|
||||||
|
}
|
||||||
|
|
||||||
b_status b_stringstream_add(struct b_stringstream *ss, const char *str)
|
b_status b_stringstream_add(struct b_stringstream *ss, const char *str)
|
||||||
{
|
{
|
||||||
return ss_builder_push_string(ss, str, strlen(str));
|
return ss_builder_push_string(ss, str, strlen(str));
|
||||||
@@ -109,10 +172,11 @@ char *b_stringstream_end(b_stringstream *ss)
|
|||||||
{
|
{
|
||||||
char *out = ss->ss_buf;
|
char *out = ss->ss_buf;
|
||||||
|
|
||||||
ss->ss_alloc = 0;
|
if (ss->ss_istack) {
|
||||||
ss->ss_len = 0;
|
free(ss->ss_istack);
|
||||||
ss->ss_max = 0;
|
}
|
||||||
ss->ss_buf = NULL;
|
|
||||||
|
memset(ss, 0x0, sizeof *ss);
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|||||||
50
object-test/object-test.c
Normal file
50
object-test/object-test.c
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
#include <CuTest.h>
|
||||||
|
#include <blue/object/string.h>
|
||||||
|
|
||||||
|
static void test_string_create(CuTest *tc)
|
||||||
|
{
|
||||||
|
b_string *str = b_string_create();
|
||||||
|
|
||||||
|
CuAssertPtrNotNull(tc, str);
|
||||||
|
CuAssertIntEquals(tc, 0, b_string_get_size(str, B_STRLEN_NORMAL));
|
||||||
|
CuAssertStrEquals(tc, "", b_string_ptr(str));
|
||||||
|
|
||||||
|
b_string_release(str);
|
||||||
|
|
||||||
|
str = b_string_create_from_c('A', 8);
|
||||||
|
|
||||||
|
CuAssertPtrNotNull(tc, str);
|
||||||
|
CuAssertIntEquals(tc, 8, b_string_get_size(str, B_STRLEN_NORMAL));
|
||||||
|
CuAssertStrEquals(tc, "AAAAAAAA", b_string_ptr(str));
|
||||||
|
|
||||||
|
b_string_release(str);
|
||||||
|
|
||||||
|
str = b_string_create_from_cstr("Hello, world!");
|
||||||
|
|
||||||
|
CuAssertPtrNotNull(tc, str);
|
||||||
|
CuAssertIntEquals(tc, 13, b_string_get_size(str, B_STRLEN_NORMAL));
|
||||||
|
CuAssertStrEquals(tc, "Hello, world!", b_string_ptr(str));
|
||||||
|
|
||||||
|
b_string_release(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_string_length(CuTest *tc)
|
||||||
|
{
|
||||||
|
const char *cstr = "Hello, \033[91;1mworld!";
|
||||||
|
b_string *s = b_string_create_from_cstr(cstr);
|
||||||
|
|
||||||
|
CuAssertIntEquals(tc, 13, b_string_get_size(s, B_STRLEN_IGNORE_ESC));
|
||||||
|
CuAssertIntEquals(tc, 20, b_string_get_size(s, B_STRLEN_NORMAL));
|
||||||
|
|
||||||
|
b_string_release(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
CuSuite *get_all_tests(void)
|
||||||
|
{
|
||||||
|
CuSuite *suite = CuSuiteNew();
|
||||||
|
|
||||||
|
SUITE_ADD_TEST(suite, test_string_create);
|
||||||
|
SUITE_ADD_TEST(suite, test_string_length);
|
||||||
|
|
||||||
|
return suite;
|
||||||
|
}
|
||||||
3
object/CMakeLists.txt
Normal file
3
object/CMakeLists.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
include(../cmake/Templates.cmake)
|
||||||
|
|
||||||
|
add_bluelib_module(NAME object DEPENDENCIES core)
|
||||||
337
object/array.c
Normal file
337
object/array.c
Normal file
@@ -0,0 +1,337 @@
|
|||||||
|
#include "array.h"
|
||||||
|
|
||||||
|
#include <blue/core/iterator.h>
|
||||||
|
#include <blue/object/array.h>
|
||||||
|
#include <blue/object/type.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static void array_release(struct b_object *obj);
|
||||||
|
|
||||||
|
static struct b_object_type array_type = {
|
||||||
|
.t_flags = B_OBJECT_FUNDAMENTAL,
|
||||||
|
.t_id = B_OBJECT_TYPE_ARRAY,
|
||||||
|
.t_name = "corelib::array",
|
||||||
|
.t_instance_size = sizeof(struct b_array),
|
||||||
|
.t_release = array_release,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct b_array *b_array_create(void)
|
||||||
|
{
|
||||||
|
struct b_array *array
|
||||||
|
= (struct b_array *)b_object_type_instantiate(&array_type);
|
||||||
|
if (!array) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct b_array *b_array_create_with_values(
|
||||||
|
struct b_object *const *values, size_t nr_values)
|
||||||
|
{
|
||||||
|
struct b_array *array = b_array_create();
|
||||||
|
if (!array) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t real_nr_values = 0;
|
||||||
|
for (size_t i = 0; i < nr_values; i++) {
|
||||||
|
if (values[i]) {
|
||||||
|
real_nr_values++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
array->ar_len = real_nr_values;
|
||||||
|
array->ar_cap = real_nr_values;
|
||||||
|
array->ar_data = calloc(real_nr_values, sizeof(struct b_object *));
|
||||||
|
if (!array->ar_data) {
|
||||||
|
b_array_release(array);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t index = 0;
|
||||||
|
for (size_t i = 0; i < nr_values; i++) {
|
||||||
|
array->ar_data[index++] = b_retain(values[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
static b_status resize_array(struct b_array *array, size_t new_capacity)
|
||||||
|
{
|
||||||
|
if (array->ar_cap < new_capacity) {
|
||||||
|
void *new_data = realloc(
|
||||||
|
array->ar_data, new_capacity * sizeof(struct b_object *));
|
||||||
|
if (!new_data) {
|
||||||
|
return B_ERR_NO_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
array->ar_data = new_data;
|
||||||
|
} else {
|
||||||
|
for (size_t i = new_capacity; i < array->ar_len; i++) {
|
||||||
|
b_release(array->ar_data[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *new_data = realloc(
|
||||||
|
array->ar_data, new_capacity * sizeof(struct b_object *));
|
||||||
|
if (!new_data) {
|
||||||
|
return B_ERR_NO_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
array->ar_data = new_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
array->ar_cap = new_capacity;
|
||||||
|
if (array->ar_len > new_capacity) {
|
||||||
|
array->ar_len = new_capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
b_status b_array_append(struct b_array *array, struct b_object *value)
|
||||||
|
{
|
||||||
|
return b_array_insert(array, value, B_NPOS);
|
||||||
|
}
|
||||||
|
|
||||||
|
b_status b_array_prepend(struct b_array *array, struct b_object *value)
|
||||||
|
{
|
||||||
|
return b_array_insert(array, value, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
b_status b_array_insert(struct b_array *array, struct b_object *value, size_t at)
|
||||||
|
{
|
||||||
|
if (at == B_NPOS) {
|
||||||
|
at = array->ar_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (at > array->ar_len) {
|
||||||
|
return B_ERR_OUT_OF_BOUNDS;
|
||||||
|
}
|
||||||
|
|
||||||
|
b_status status = B_SUCCESS;
|
||||||
|
|
||||||
|
if (array->ar_len + 1 > array->ar_cap) {
|
||||||
|
status = resize_array(array, array->ar_cap + 8);
|
||||||
|
|
||||||
|
if (status != B_SUCCESS) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct b_object **src = array->ar_data + at;
|
||||||
|
struct b_object **dest = src + 1;
|
||||||
|
size_t move_len = (array->ar_len - at) * sizeof(struct b_object *);
|
||||||
|
|
||||||
|
memmove(dest, src, move_len);
|
||||||
|
|
||||||
|
array->ar_data[at] = b_retain(value);
|
||||||
|
array->ar_len++;
|
||||||
|
|
||||||
|
return B_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
b_status b_array_remove(struct b_array *array, size_t at)
|
||||||
|
{
|
||||||
|
if (at >= array->ar_len) {
|
||||||
|
return B_ERR_OUT_OF_BOUNDS;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct b_object **src = array->ar_data + at;
|
||||||
|
struct b_object **dest = src + 1;
|
||||||
|
size_t move_len = array->ar_len * sizeof(struct b_object *);
|
||||||
|
|
||||||
|
b_release(array->ar_data[at]);
|
||||||
|
|
||||||
|
memmove(dest, src, move_len);
|
||||||
|
|
||||||
|
array->ar_len--;
|
||||||
|
|
||||||
|
return B_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
b_status b_array_remove_front(struct b_array *array)
|
||||||
|
{
|
||||||
|
return b_array_remove(array, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
b_status b_array_remove_back(struct b_array *array)
|
||||||
|
{
|
||||||
|
return b_array_remove(array, array->ar_len - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct b_object *b_array_pop(b_array *array, size_t at)
|
||||||
|
{
|
||||||
|
if (at >= array->ar_len) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct b_object **src = array->ar_data + at;
|
||||||
|
struct b_object **dest = src + 1;
|
||||||
|
size_t move_len = array->ar_len * sizeof(struct b_object *);
|
||||||
|
|
||||||
|
struct b_object *out = array->ar_data[at];
|
||||||
|
|
||||||
|
memmove(dest, src, move_len);
|
||||||
|
|
||||||
|
array->ar_len--;
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct b_object *b_array_pop_front(struct b_array *array)
|
||||||
|
{
|
||||||
|
return b_array_pop(array, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct b_object *b_array_pop_back(struct b_array *array)
|
||||||
|
{
|
||||||
|
return b_array_pop(array, array->ar_len - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct b_object *b_array_at(const struct b_array *array, size_t at)
|
||||||
|
{
|
||||||
|
if (at >= array->ar_len) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return array->ar_data[at];
|
||||||
|
}
|
||||||
|
|
||||||
|
struct b_object *b_array_get(struct b_array *array, size_t at)
|
||||||
|
{
|
||||||
|
if (at >= array->ar_len) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return b_retain(array->ar_data[at]);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t b_array_size(const struct b_array *array)
|
||||||
|
{
|
||||||
|
return array->ar_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t b_array_capacity(const struct b_array *array)
|
||||||
|
{
|
||||||
|
return array->ar_cap;
|
||||||
|
}
|
||||||
|
|
||||||
|
void array_release(struct b_object *obj)
|
||||||
|
{
|
||||||
|
struct b_array *array = B_ARRAY(obj);
|
||||||
|
|
||||||
|
if (array->ar_data) {
|
||||||
|
for (size_t i = 0; i < array->ar_len; i++) {
|
||||||
|
b_release(array->ar_data[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(array->ar_data);
|
||||||
|
array->ar_data = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void b_array_clear(struct b_array *array)
|
||||||
|
{
|
||||||
|
while (array->ar_len) {
|
||||||
|
b_array_remove_back(array);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool array_iterator_next(struct b_iterator *it)
|
||||||
|
{
|
||||||
|
return b_array_iterator_next((struct b_array_iterator *)it);
|
||||||
|
}
|
||||||
|
|
||||||
|
static b_status array_iterator_erase(struct b_iterator *it)
|
||||||
|
{
|
||||||
|
return b_array_iterator_erase((struct b_array_iterator *)it);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool array_iterator_is_valid(const struct b_iterator *it)
|
||||||
|
{
|
||||||
|
return b_array_iterator_is_valid((const struct b_array_iterator *)it);
|
||||||
|
}
|
||||||
|
|
||||||
|
static b_iterator_ops it_ops = {
|
||||||
|
.it_next = array_iterator_next,
|
||||||
|
.it_close = NULL,
|
||||||
|
.it_erase = array_iterator_erase,
|
||||||
|
.it_is_valid = array_iterator_is_valid,
|
||||||
|
};
|
||||||
|
|
||||||
|
int b_array_iterator_begin(struct b_array *array, struct b_array_iterator *it)
|
||||||
|
{
|
||||||
|
it->_a = array;
|
||||||
|
it->i = 0;
|
||||||
|
it->_base.it_ops = &it_ops;
|
||||||
|
|
||||||
|
if (array->ar_len > 0) {
|
||||||
|
it->value = array->ar_data[0];
|
||||||
|
} else {
|
||||||
|
it->value = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool b_array_iterator_next(struct b_array_iterator *it)
|
||||||
|
{
|
||||||
|
struct b_array *array = it->_a;
|
||||||
|
|
||||||
|
if (it->value == NULL || it->i >= array->ar_len) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
it->i++;
|
||||||
|
|
||||||
|
if (it->i >= array->ar_len) {
|
||||||
|
it->value = NULL;
|
||||||
|
} else {
|
||||||
|
it->value = array->ar_data[it->i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return it->value != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
b_status b_array_iterator_erase(struct b_array_iterator *it)
|
||||||
|
{
|
||||||
|
struct b_array *array = it->_a;
|
||||||
|
if (it->i >= array->ar_len) {
|
||||||
|
return B_ERR_OUT_OF_BOUNDS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (array->ar_data[it->i] != it->value) {
|
||||||
|
return B_ERR_BAD_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
b_array_remove(array, it->i);
|
||||||
|
|
||||||
|
if (it->i < array->ar_len) {
|
||||||
|
it->value = array->ar_data[it->i];
|
||||||
|
} else {
|
||||||
|
it->value = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool b_array_iterator_is_valid(const struct b_array_iterator *it)
|
||||||
|
{
|
||||||
|
struct b_array *array = it->_a;
|
||||||
|
if (it->i >= array->ar_len) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (array->ar_data[it->i] != it->value) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return it->value != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
b_object_type_id b_array_type_id(void)
|
||||||
|
{
|
||||||
|
return (b_object_type_id)&array_type;
|
||||||
|
}
|
||||||
15
object/array.h
Normal file
15
object/array.h
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
#ifndef _BLUELIB_ARRAY_H_
|
||||||
|
#define _BLUELIB_ARRAY_H_
|
||||||
|
|
||||||
|
#include "../object.h"
|
||||||
|
|
||||||
|
struct b_array {
|
||||||
|
struct b_object 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_object **ar_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
167
object/bitmap.c
Normal file
167
object/bitmap.c
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
#include <string.h>
|
||||||
|
#include <blue/object/bitmap.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 += __builtin_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 += __builtin_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 - __builtin_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 - __builtin_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 + __builtin_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 + __builtin_clzl(~last_word);
|
||||||
|
}
|
||||||
472
object/dict.c
Normal file
472
object/dict.c
Normal file
@@ -0,0 +1,472 @@
|
|||||||
|
#include "dict.h"
|
||||||
|
|
||||||
|
#include <blue/core/status.h>
|
||||||
|
#include <blue/core/stringstream.h>
|
||||||
|
#include <blue/object/dict.h>
|
||||||
|
#include <blue/object/string.h>
|
||||||
|
#include <blue/object/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_object *obj);
|
||||||
|
static void dict_to_string(struct b_object *obj, struct b_stringstream *out);
|
||||||
|
|
||||||
|
static struct b_object_type dict_type = {
|
||||||
|
.t_name = "corelib::dict",
|
||||||
|
.t_flags = B_OBJECT_FUNDAMENTAL,
|
||||||
|
.t_id = B_OBJECT_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_object_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_object *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_object *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_object *b_dict_get(struct b_dict *dict, const char *key)
|
||||||
|
{
|
||||||
|
b_object *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_object *obj, struct b_stringstream *out)
|
||||||
|
{
|
||||||
|
struct b_dict *dict = B_DICT(obj);
|
||||||
|
|
||||||
|
if (b_dict_is_empty(dict)) {
|
||||||
|
b_stringstream_add(out, "{}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
b_stringstream_add(out, "{\n");
|
||||||
|
|
||||||
|
b_stringstream_push_indent(out, 1);
|
||||||
|
size_t len = b_dict_get_size(dict);
|
||||||
|
|
||||||
|
b_dict_iterator it;
|
||||||
|
b_dict_foreach(&it, dict)
|
||||||
|
{
|
||||||
|
b_stringstream_addf(out, "%s: ", it.key);
|
||||||
|
b_to_string(it.value, out);
|
||||||
|
|
||||||
|
if (it.i < len - 1) {
|
||||||
|
b_stringstream_add(out, ",");
|
||||||
|
}
|
||||||
|
|
||||||
|
b_stringstream_add(out, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
b_stringstream_pop_indent(out);
|
||||||
|
b_stringstream_add(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_object *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_object_type_id b_dict_type_id(void)
|
||||||
|
{
|
||||||
|
return (b_object_type_id)&dict_type;
|
||||||
|
}
|
||||||
26
object/dict.h
Normal file
26
object/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_object *bi_value;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct b_dict_bucket {
|
||||||
|
b_btree_node bk_node;
|
||||||
|
uint64_t bk_hash;
|
||||||
|
b_queue bk_items;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct b_dict {
|
||||||
|
struct b_object d_base;
|
||||||
|
b_btree d_buckets;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
434
object/hashmap.c
Normal file
434
object/hashmap.c
Normal file
@@ -0,0 +1,434 @@
|
|||||||
|
#include "hashmap.h"
|
||||||
|
|
||||||
|
#include <blue/core/misc.h>
|
||||||
|
#include <blue/core/status.h>
|
||||||
|
#include <blue/object/hashmap.h>
|
||||||
|
#include <blue/object/string.h>
|
||||||
|
#include <blue/object/type.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#define HASH_OFFSET_BASIS 0xcbf29ce484222325
|
||||||
|
#define HASH_PRIME 0x100000001b3
|
||||||
|
|
||||||
|
static uint64_t hash_data(const void *p, size_t size)
|
||||||
|
{
|
||||||
|
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_object *obj);
|
||||||
|
|
||||||
|
static struct b_object_type hashmap_type = {
|
||||||
|
.t_name = "corelib::hashmap",
|
||||||
|
.t_flags = B_OBJECT_FUNDAMENTAL,
|
||||||
|
.t_id = B_OBJECT_TYPE_HASHMAP,
|
||||||
|
.t_instance_size = sizeof(struct b_hashmap),
|
||||||
|
.t_release = hashmap_release,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct b_hashmap *b_hashmap_create(void)
|
||||||
|
{
|
||||||
|
struct b_hashmap *hashmap
|
||||||
|
= (struct b_hashmap *)b_object_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();
|
||||||
|
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 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 struct b_hashmap_bucket *create_bucket(void)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
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_object *obj)
|
||||||
|
{
|
||||||
|
struct b_hashmap *map = B_HASHMAP(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
b_object_type_id b_hashmap_type_id(void)
|
||||||
|
{
|
||||||
|
return (b_object_type_id)&hashmap_type;
|
||||||
|
}
|
||||||
25
object/hashmap.h
Normal file
25
object/hashmap.h
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
#ifndef _B_HASHMAP_H_
|
||||||
|
#define _B_HASHMAP_H_
|
||||||
|
|
||||||
|
#include <blue/core/btree.h>
|
||||||
|
#include <blue/core/queue.h>
|
||||||
|
#include <blue/object/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_object h_base;
|
||||||
|
struct b_btree h_buckets;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
0
object/include/blue/object.h
Normal file
0
object/include/blue/object.h
Normal file
304
object/include/blue/object/array.h
Normal file
304
object/include/blue/object/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/object/object.h>
|
||||||
|
#include <blue/object/type.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cast a generic b_object 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_object `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_object *value;
|
||||||
|
} b_array_iterator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an empty b_array.
|
||||||
|
*
|
||||||
|
* @return A pointer to the new array, or NULL if an error occurred.
|
||||||
|
*/
|
||||||
|
extern b_array *b_array_create(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an b_array initialised with the contents of the provided b_object
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
extern b_array *b_array_create_with_values(
|
||||||
|
b_object *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_OBJECT(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_OBJECT(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.
|
||||||
|
*/
|
||||||
|
extern 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.
|
||||||
|
*/
|
||||||
|
extern b_status b_array_append(b_array *array, b_object *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.
|
||||||
|
*/
|
||||||
|
extern b_status b_array_prepend(b_array *array, b_object *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.
|
||||||
|
*/
|
||||||
|
extern b_status b_array_insert(b_array *array, b_object *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.
|
||||||
|
*/
|
||||||
|
extern 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.
|
||||||
|
*/
|
||||||
|
extern 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.
|
||||||
|
*/
|
||||||
|
extern 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.
|
||||||
|
*/
|
||||||
|
extern b_object *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.
|
||||||
|
*/
|
||||||
|
extern b_object *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.
|
||||||
|
*/
|
||||||
|
extern b_object *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.
|
||||||
|
*/
|
||||||
|
extern b_object *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.
|
||||||
|
*/
|
||||||
|
extern b_object *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.
|
||||||
|
*/
|
||||||
|
extern 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.
|
||||||
|
*/
|
||||||
|
extern 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.
|
||||||
|
*/
|
||||||
|
extern 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.
|
||||||
|
*/
|
||||||
|
extern 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.
|
||||||
|
*/
|
||||||
|
extern 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.
|
||||||
|
*/
|
||||||
|
extern bool b_array_iterator_is_valid(const b_array_iterator *it);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
34
object/include/blue/object/bitmap.h
Normal file
34
object/include/blue/object/bitmap.h
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
#ifndef BLUELIB_BITMAP_H_
|
||||||
|
#define BLUELIB_BITMAP_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)]
|
||||||
|
|
||||||
|
extern void b_bitmap_zero(b_bitmap_word *map, unsigned long nbits);
|
||||||
|
extern void b_bitmap_fill(b_bitmap_word *map, unsigned long nbits);
|
||||||
|
extern void b_bitmap_set(b_bitmap_word *map, unsigned long bit);
|
||||||
|
extern void b_bitmap_clear(b_bitmap_word *map, unsigned long bit);
|
||||||
|
extern bool b_bitmap_check(const b_bitmap_word *map, unsigned long bit);
|
||||||
|
|
||||||
|
extern unsigned int b_bitmap_count_set(const b_bitmap_word *map, unsigned long nbits);
|
||||||
|
extern unsigned int b_bitmap_count_clear(const b_bitmap_word *map, unsigned long nbits);
|
||||||
|
|
||||||
|
extern unsigned int b_bitmap_highest_set(const b_bitmap_word *map, unsigned long nbits);
|
||||||
|
extern unsigned int b_bitmap_highest_clear(const b_bitmap_word *map, unsigned long nbits);
|
||||||
|
extern unsigned int b_bitmap_lowest_set(const b_bitmap_word *map, unsigned long nbits);
|
||||||
|
extern unsigned int b_bitmap_lowest_clear(const b_bitmap_word *map, unsigned long nbits);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
33
object/include/blue/object/buffer.h
Normal file
33
object/include/blue/object/buffer.h
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
#ifndef BLUELIB_BUFFER_H_
|
||||||
|
#define BLUELIB_BUFFER_H_
|
||||||
|
|
||||||
|
#include <blue/object/type.h>
|
||||||
|
#include <blue/object/object.h>
|
||||||
|
#include <blue/object/status.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#define B_BUFFER(p) ((b_buffer *)(p))
|
||||||
|
|
||||||
|
typedef struct b_buffer b_buffer;
|
||||||
|
|
||||||
|
extern b_buffer *b_buffer_create(size_t item_sz);
|
||||||
|
extern b_buffer *b_buffer_create_from_bytes(const void *p, size_t len);
|
||||||
|
extern 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_OBJECT(buf))); }
|
||||||
|
static inline void b_buffer_release(b_buffer *buf) { b_release(B_OBJECT(buf)); }
|
||||||
|
extern void *b_buffer_steal(b_buffer *buf);
|
||||||
|
extern b_status b_buffer_reserve(b_buffer *buf, size_t capacity);
|
||||||
|
|
||||||
|
extern b_status b_buffer_append(b_buffer *dest, const void *p, size_t count);
|
||||||
|
extern b_status b_buffer_prepend(b_buffer *dest, const void *p, size_t count);
|
||||||
|
extern b_status b_buffer_insert(b_buffer *dest, const void *p, size_t count, size_t at);
|
||||||
|
extern b_status b_buffer_clear(b_buffer *str);
|
||||||
|
|
||||||
|
extern size_t b_buffer_get_size(const b_buffer *str);
|
||||||
|
extern size_t b_buffer_get_item_size(const b_buffer *str);
|
||||||
|
extern size_t b_buffer_get_capacity(const b_buffer *str);
|
||||||
|
|
||||||
|
extern void *b_buffer_ptr(const b_buffer *str);
|
||||||
|
|
||||||
|
#endif
|
||||||
69
object/include/blue/object/dict.h
Normal file
69
object/include/blue/object/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/object/object.h>
|
||||||
|
#include <blue/object/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_object *value;
|
||||||
|
|
||||||
|
b_dict *_d;
|
||||||
|
b_btree_node *_cbn;
|
||||||
|
b_queue_entry *_cqe;
|
||||||
|
} b_dict_iterator;
|
||||||
|
|
||||||
|
typedef struct b_dict_item {
|
||||||
|
const char *key;
|
||||||
|
b_object *value;
|
||||||
|
} b_dict_item;
|
||||||
|
|
||||||
|
extern b_dict *b_dict_create(void);
|
||||||
|
extern 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_OBJECT(dict)));
|
||||||
|
}
|
||||||
|
static inline void b_dict_release(b_dict *dict)
|
||||||
|
{
|
||||||
|
b_release(B_OBJECT(dict));
|
||||||
|
}
|
||||||
|
|
||||||
|
extern b_status b_dict_put(b_dict *dict, const char *key, b_object *value);
|
||||||
|
extern b_object *b_dict_at(const b_dict *dict, const char *key);
|
||||||
|
extern b_object *b_dict_get(b_dict *dict, const char *key);
|
||||||
|
|
||||||
|
extern bool b_dict_has_key(const b_dict *dict, const char *key);
|
||||||
|
extern size_t b_dict_get_size(const b_dict *dict);
|
||||||
|
extern bool b_dict_is_empty(const b_dict *dict);
|
||||||
|
|
||||||
|
extern int b_dict_iterator_begin(b_dict *dict, b_dict_iterator *it);
|
||||||
|
extern bool b_dict_iterator_next(b_dict_iterator *it);
|
||||||
|
extern b_status b_dict_iterator_erase(b_dict_iterator *it);
|
||||||
|
extern bool b_dict_iterator_is_valid(const b_dict_iterator *it);
|
||||||
|
|
||||||
|
#endif
|
||||||
92
object/include/blue/object/hashmap.h
Normal file
92
object/include/blue/object/hashmap.h
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
#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/object/object.h>
|
||||||
|
#include <blue/object/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 = {}, .value = {} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#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 struct b_hashmap_key {
|
||||||
|
const void *key_data;
|
||||||
|
size_t key_size;
|
||||||
|
bool key_owned;
|
||||||
|
} b_hashmap_key;
|
||||||
|
|
||||||
|
typedef struct b_hashmap_value {
|
||||||
|
void *value_data;
|
||||||
|
size_t value_size;
|
||||||
|
bool value_owned;
|
||||||
|
} 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;
|
||||||
|
|
||||||
|
extern b_hashmap *b_hashmap_create(void);
|
||||||
|
extern 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_OBJECT(hashmap)));
|
||||||
|
}
|
||||||
|
static inline void b_hashmap_release(b_hashmap *hashmap)
|
||||||
|
{
|
||||||
|
b_release(B_OBJECT(hashmap));
|
||||||
|
}
|
||||||
|
|
||||||
|
extern b_status b_hashmap_put(
|
||||||
|
b_hashmap *hashmap, const b_hashmap_key *key, const b_hashmap_value *value);
|
||||||
|
extern const b_hashmap_value *b_hashmap_get(
|
||||||
|
const b_hashmap *hashmap, const b_hashmap_key *key);
|
||||||
|
|
||||||
|
extern bool b_hashmap_has_key(const b_hashmap *hashmap, const b_hashmap_key *key);
|
||||||
|
extern size_t b_hashmap_get_size(const b_hashmap *hashmap);
|
||||||
|
extern bool b_hashmap_is_empty(const b_hashmap *hashmap);
|
||||||
|
|
||||||
|
extern int b_hashmap_iterator_begin(b_hashmap *hashmap, b_hashmap_iterator *it);
|
||||||
|
extern bool b_hashmap_iterator_next(b_hashmap_iterator *it);
|
||||||
|
extern b_status b_hashmap_iterator_erase(b_hashmap_iterator *it);
|
||||||
|
extern bool b_hashmap_iterator_is_valid(const b_hashmap_iterator *it);
|
||||||
|
|
||||||
|
#endif
|
||||||
285
object/include/blue/object/number.h
Normal file
285
object/include/blue/object/number.h
Normal file
@@ -0,0 +1,285 @@
|
|||||||
|
#ifndef BLUELIB_NUMBER_H
|
||||||
|
#define BLUELIB_NUMBER_H
|
||||||
|
|
||||||
|
#include <blue/object/object.h>
|
||||||
|
#include <blue/object/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;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
union {
|
||||||
|
unsigned char i_bytes[sizeof(uint16_t)];
|
||||||
|
int16_t i_val;
|
||||||
|
uint16_t i_uval;
|
||||||
|
};
|
||||||
|
} b_i16;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
union {
|
||||||
|
unsigned char i_bytes[sizeof(uint32_t)];
|
||||||
|
int32_t i_val;
|
||||||
|
uint32_t i_uval;
|
||||||
|
};
|
||||||
|
} b_i32;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
union {
|
||||||
|
unsigned char i_bytes[sizeof(uint64_t)];
|
||||||
|
int64_t i_val;
|
||||||
|
uint64_t i_uval;
|
||||||
|
};
|
||||||
|
} b_i64;
|
||||||
|
|
||||||
|
extern 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_OBJECT(number)));
|
||||||
|
}
|
||||||
|
static inline void b_number_release(b_number *number)
|
||||||
|
{
|
||||||
|
b_release(B_OBJECT(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);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern b_number_type b_number_get_type(const b_number *number);
|
||||||
|
extern 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern bool b_number_is_integer(const b_number *number);
|
||||||
|
extern bool b_number_is_float(const b_number *number);
|
||||||
|
|
||||||
|
extern size_t b_number_data_size(const b_number *number);
|
||||||
|
|
||||||
|
extern b_i16 b_i16_htob(uint16_t v);
|
||||||
|
extern b_i16 b_i16_htos(uint16_t v);
|
||||||
|
|
||||||
|
extern uint16_t b_i16_btoh(b_i16 v);
|
||||||
|
extern uint16_t b_i16_stoh(b_i16 v);
|
||||||
|
|
||||||
|
extern b_i32 b_i32_htob(uint32_t v);
|
||||||
|
extern b_i32 b_i32_htos(uint32_t v);
|
||||||
|
|
||||||
|
extern uint32_t b_i32_btoh(b_i32 v);
|
||||||
|
extern uint32_t b_i32_stoh(b_i32 v);
|
||||||
|
|
||||||
|
extern b_i64 b_i64_htob(uint64_t v);
|
||||||
|
extern b_i64 b_i64_htos(uint64_t v);
|
||||||
|
|
||||||
|
extern uint64_t b_i64_btoh(b_i64 v);
|
||||||
|
extern uint64_t b_i64_stoh(b_i64 v);
|
||||||
|
|
||||||
|
#endif
|
||||||
38
object/include/blue/object/object.h
Normal file
38
object/include/blue/object/object.h
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
#ifndef BLUELIB_OBJECT_H_
|
||||||
|
#define BLUELIB_OBJECT_H_
|
||||||
|
|
||||||
|
#include <blue/object/type.h>
|
||||||
|
|
||||||
|
#define B_OBJECT(p) ((b_object *)(p))
|
||||||
|
|
||||||
|
#define B_TYPEOF(object) ((struct b_object *)(object)->ob_type)
|
||||||
|
#define B_TYPEID(object) (b_typeid(B_OBJECT(object)))
|
||||||
|
|
||||||
|
#define B_RV(p) (b_make_rvalue(B_OBJECT(p)))
|
||||||
|
#define B_RVT(t, p) ((t *)(b_make_rvalue(B_OBJECT(p))))
|
||||||
|
|
||||||
|
struct b_string;
|
||||||
|
struct b_stringstream;
|
||||||
|
|
||||||
|
typedef enum b_comparison_result {
|
||||||
|
B_LESS = -1,
|
||||||
|
B_EQUAL = 0,
|
||||||
|
B_GREATER = 1,
|
||||||
|
} b_comparison_result_t;
|
||||||
|
|
||||||
|
typedef struct b_object {
|
||||||
|
unsigned int ob_ref;
|
||||||
|
const struct b_object_type *ob_type;
|
||||||
|
} b_object;
|
||||||
|
|
||||||
|
extern b_object *b_make_rvalue(b_object *obj);
|
||||||
|
|
||||||
|
extern b_object *b_retain(b_object *obj);
|
||||||
|
extern void b_release(b_object *obj);
|
||||||
|
|
||||||
|
extern void b_to_string(b_object *obj, struct b_stringstream *out);
|
||||||
|
extern b_object_type_id b_typeid(const b_object *obj);
|
||||||
|
|
||||||
|
extern b_comparison_result_t b_compare(const b_object *a, const b_object *b);
|
||||||
|
|
||||||
|
#endif
|
||||||
77
object/include/blue/object/string.h
Normal file
77
object/include/blue/object/string.h
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
#ifndef BLUELIB_STRING_H_
|
||||||
|
#define BLUELIB_STRING_H_
|
||||||
|
|
||||||
|
#include <blue/core/status.h>
|
||||||
|
#include <blue/object/object.h>
|
||||||
|
#include <blue/object/type.h>
|
||||||
|
|
||||||
|
#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_flags;
|
||||||
|
|
||||||
|
typedef struct b_strv_builder {
|
||||||
|
char *strv_buf;
|
||||||
|
size_t strv_len;
|
||||||
|
size_t strv_max;
|
||||||
|
unsigned char strv_alloc;
|
||||||
|
} b_strv_builder;
|
||||||
|
|
||||||
|
extern b_string *b_string_create(void);
|
||||||
|
extern b_string *b_string_create_from_cstr(const char *s);
|
||||||
|
extern b_string *b_string_create_from_c(char c, size_t count);
|
||||||
|
|
||||||
|
static inline b_string *b_string_retain(b_string *str)
|
||||||
|
{
|
||||||
|
return B_STRING(b_retain(B_OBJECT(str)));
|
||||||
|
}
|
||||||
|
static inline void b_string_release(b_string *str)
|
||||||
|
{
|
||||||
|
b_release(B_OBJECT(str));
|
||||||
|
}
|
||||||
|
extern char *b_string_steal(b_string *str);
|
||||||
|
extern b_status b_string_reserve(b_string *str, size_t capacity);
|
||||||
|
|
||||||
|
extern void b_string_append_s(b_string *dest, const b_string *src);
|
||||||
|
extern void b_string_append_cstr(b_string *dest, const char *src);
|
||||||
|
extern void b_string_append_cstrf(b_string *dest, const char *format, ...);
|
||||||
|
extern void b_string_prepend_s(b_string *dest, const b_string *src);
|
||||||
|
extern void b_string_prepend_cstr(b_string *dest, const char *src);
|
||||||
|
extern void b_string_prepend_cstrf(b_string *dest, const char *format, ...);
|
||||||
|
extern void b_string_insert_s(b_string *dest, const b_string *src, size_t at);
|
||||||
|
extern void b_string_insert_cstr(b_string *dest, const char *src, size_t at);
|
||||||
|
extern void b_string_insert_cstrn(
|
||||||
|
b_string *dest, const char *src, size_t len, size_t at);
|
||||||
|
extern void b_string_insert_cstrf(
|
||||||
|
b_string *dest, size_t at, const char *format, ...);
|
||||||
|
extern void b_string_clear(b_string *str);
|
||||||
|
|
||||||
|
extern size_t b_string_get_size(const b_string *str, b_strlen_flags flags);
|
||||||
|
extern size_t b_string_get_capacity(const b_string *str);
|
||||||
|
|
||||||
|
extern const char *b_string_ptr(const b_string *str);
|
||||||
|
|
||||||
|
extern void b_strv_builder_begin(b_strv_builder *strv, char *buf, size_t max);
|
||||||
|
extern void b_strv_builder_begin_dynamic(b_strv_builder *strv);
|
||||||
|
|
||||||
|
extern b_status b_strv_builder_add(b_strv_builder *strv, const char *str);
|
||||||
|
extern b_status b_strv_builder_addf(b_strv_builder *strv, const char *format, ...);
|
||||||
|
extern b_status b_strv_builder_addv(b_strv_builder *strv, const char **strs);
|
||||||
|
extern b_status b_strv_builder_addvl(
|
||||||
|
b_strv_builder *strv, const char **strs, size_t count);
|
||||||
|
extern b_status b_strv_builder_add_many(b_strv_builder *strv, ...);
|
||||||
|
extern char *b_strv_builder_end(b_strv_builder *strv);
|
||||||
|
|
||||||
|
extern char *b_strdup(const char *s);
|
||||||
|
extern size_t b_strlen(const char *s, b_strlen_flags flags);
|
||||||
|
|
||||||
|
extern uint64_t b_cstr_hash(const char *s);
|
||||||
|
|
||||||
|
#endif
|
||||||
25
object/include/blue/object/string_formatter.h
Normal file
25
object/include/blue/object/string_formatter.h
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
#ifndef BLUELIB_STRING_FORMATTER_H
|
||||||
|
#define BLUELIB_STRING_FORMATTER_H
|
||||||
|
|
||||||
|
#include <blue/object/string.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
typedef struct b_stringstream b_stringstream;
|
||||||
|
|
||||||
|
extern b_stringstream *b_stringstream_create(void);
|
||||||
|
extern b_string *b_stringstream_end(b_stringstream *f);
|
||||||
|
extern void b_stringstream_destroy(b_stringstream *f);
|
||||||
|
extern const b_string *b_stringstream_str(b_stringstream *f);
|
||||||
|
extern const char *b_stringstream_cstr(b_stringstream *f);
|
||||||
|
extern void b_stringstream_clear(b_stringstream *f);
|
||||||
|
|
||||||
|
extern void b_stringstream_push_indent(b_stringstream *f, int indent);
|
||||||
|
extern void b_stringstream_push_indent_abs(b_stringstream *f, int indent);
|
||||||
|
extern void b_stringstream_pop_indent(b_stringstream *f);
|
||||||
|
|
||||||
|
extern void b_stringstream_add(b_stringstream *f, const char *s);
|
||||||
|
extern void b_stringstream_addf(b_stringstream *f, const char *format, ...);
|
||||||
|
extern void b_stringstream_addvf(b_stringstream *f, const char *format, va_list arg);
|
||||||
|
extern void b_stringstream_add_str(b_stringstream *f, const b_string *str);
|
||||||
|
|
||||||
|
#endif
|
||||||
72
object/include/blue/object/tree.h
Normal file
72
object/include/blue/object/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/object/string.h>
|
||||||
|
|
||||||
|
#define B_TREE(p) ((b_tree *)(p))
|
||||||
|
#define B_TREE_NODE_INIT ((b_tree_node) {})
|
||||||
|
|
||||||
|
#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;
|
||||||
|
|
||||||
|
extern b_tree *b_tree_create(void);
|
||||||
|
|
||||||
|
static inline b_tree *b_tree_retain(b_tree *tree)
|
||||||
|
{
|
||||||
|
return B_TREE(b_retain(B_OBJECT(tree)));
|
||||||
|
}
|
||||||
|
static inline void b_tree_release(b_tree *tree)
|
||||||
|
{
|
||||||
|
b_release(B_OBJECT(tree));
|
||||||
|
}
|
||||||
|
|
||||||
|
extern void b_tree_set_root(b_tree *tree, struct b_tree_node *node);
|
||||||
|
|
||||||
|
extern void b_tree_node_add_child(b_tree_node *parent, b_tree_node *child);
|
||||||
|
extern void b_tree_node_add_sibling(b_tree_node *node, b_tree_node *to_add);
|
||||||
|
|
||||||
|
extern b_tree_node *b_tree_node_get_child(b_tree_node *node, size_t at);
|
||||||
|
extern b_tree_node *b_tree_node_get_parent(b_tree_node *node);
|
||||||
|
|
||||||
|
extern int b_tree_iterator_begin(b_tree *tree, b_tree_iterator *it);
|
||||||
|
extern int b_tree_iterator_begin_at_node(b_tree_node *node, b_tree_iterator *it);
|
||||||
|
extern int b_tree_iterator_begin_at_node_recursive(
|
||||||
|
b_tree_node *node, b_tree_iterator *it);
|
||||||
|
|
||||||
|
extern bool b_tree_iterator_next(b_tree_iterator *it);
|
||||||
|
extern b_status b_tree_iterator_erase(b_tree_iterator *it);
|
||||||
|
extern bool b_tree_iterator_is_valid(const b_tree_iterator *it);
|
||||||
|
|
||||||
|
#endif
|
||||||
51
object/include/blue/object/type.h
Normal file
51
object/include/blue/object/type.h
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
#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_object;
|
||||||
|
struct b_stringstream;
|
||||||
|
|
||||||
|
typedef uintptr_t b_object_type_id;
|
||||||
|
|
||||||
|
typedef enum b_fundamental_type_id {
|
||||||
|
B_OBJECT_TYPE_NONE = 0,
|
||||||
|
B_OBJECT_TYPE_ANY,
|
||||||
|
B_OBJECT_TYPE_ARRAY,
|
||||||
|
B_OBJECT_TYPE_BUFFER,
|
||||||
|
B_OBJECT_TYPE_DICT,
|
||||||
|
B_OBJECT_TYPE_ERROR,
|
||||||
|
B_OBJECT_TYPE_HASHMAP,
|
||||||
|
B_OBJECT_TYPE_NUMBER,
|
||||||
|
B_OBJECT_TYPE_STRING,
|
||||||
|
B_OBJECT_TYPE_TREE,
|
||||||
|
B_OBJECT_TYPE_UUID,
|
||||||
|
B_OBJECT_TYPE_FILE,
|
||||||
|
B_OBJECT_TYPE_DIRECTORY,
|
||||||
|
} b_fundamental_type_id;
|
||||||
|
|
||||||
|
typedef enum b_object_type_flags {
|
||||||
|
B_OBJECT_FUNDAMENTAL = 0x01u,
|
||||||
|
} b_object_type_flags;
|
||||||
|
|
||||||
|
typedef struct b_object_type {
|
||||||
|
b_object_type_flags t_flags;
|
||||||
|
char t_name[64];
|
||||||
|
size_t t_instance_size;
|
||||||
|
b_object_type_id t_id;
|
||||||
|
b_queue_entry t_entry;
|
||||||
|
void (*t_init)(struct b_object *);
|
||||||
|
void (*t_release)(struct b_object *);
|
||||||
|
void (*t_to_string)(struct b_object *, struct b_stringstream *);
|
||||||
|
} b_object_type;
|
||||||
|
|
||||||
|
extern b_status b_object_type_register(b_object_type *type);
|
||||||
|
extern struct b_object *b_object_type_instantiate(const b_object_type *type);
|
||||||
|
|
||||||
|
#endif
|
||||||
52
object/include/blue/object/uuid.h
Normal file
52
object/include/blue/object/uuid.h
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
#ifndef BLUELIB_UUID_H_
|
||||||
|
#define BLUELIB_UUID_H_
|
||||||
|
|
||||||
|
#include <blue/core/status.h>
|
||||||
|
#include <blue/object/object.h>
|
||||||
|
#include <blue/object/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_strv_builder;
|
||||||
|
|
||||||
|
typedef struct b_uuid b_uuid;
|
||||||
|
|
||||||
|
typedef struct b_uuid_bytes {
|
||||||
|
unsigned char uuid_bytes[B_UUID_NBYTES];
|
||||||
|
} b_uuid_bytes;
|
||||||
|
|
||||||
|
extern b_uuid *b_uuid_create(void);
|
||||||
|
extern 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);
|
||||||
|
extern b_uuid *b_uuid_create_from_bytev(const unsigned char bytes[B_UUID_NBYTES]);
|
||||||
|
extern b_uuid *b_uuid_create_from_uuid_bytes(const b_uuid_bytes *bytes);
|
||||||
|
extern b_uuid *b_uuid_create_from_string(const struct b_string *string);
|
||||||
|
extern 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_OBJECT(uuid)));
|
||||||
|
}
|
||||||
|
static inline void b_uuid_release(b_uuid *uuid)
|
||||||
|
{
|
||||||
|
b_release(B_OBJECT(uuid));
|
||||||
|
}
|
||||||
|
|
||||||
|
extern b_status b_uuid_to_string(const b_uuid *uuid, struct b_string *out);
|
||||||
|
extern b_status b_uuid_to_cstr(const b_uuid *uuid, char out[B_UUID_STRING_MAX]);
|
||||||
|
extern b_status b_uuid_to_strv_builder(
|
||||||
|
const b_uuid *uuid, struct b_strv_builder *out);
|
||||||
|
extern void b_uuid_get_bytes(
|
||||||
|
const b_uuid *uuid, unsigned char bytes[B_UUID_NBYTES]);
|
||||||
|
extern void b_uuid_get_uuid_bytes(const b_uuid *uuid, b_uuid_bytes *bytes);
|
||||||
|
extern b_uuid_bytes *b_uuid_ptr(b_uuid *uuid);
|
||||||
|
|
||||||
|
#endif
|
||||||
1681
object/number.c
Normal file
1681
object/number.c
Normal file
File diff suppressed because it is too large
Load Diff
29
object/number.h
Normal file
29
object/number.h
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#ifndef _BLUELIB_NUMBER_H_
|
||||||
|
#define _BLUELIB_NUMBER_H_
|
||||||
|
|
||||||
|
#include "object.h"
|
||||||
|
|
||||||
|
#include <blue/object/number.h>
|
||||||
|
|
||||||
|
struct b_number {
|
||||||
|
struct b_object 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
|
||||||
65
object/object.c
Normal file
65
object/object.c
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
#include "object.h"
|
||||||
|
#include <blue/object/object.h>
|
||||||
|
#include <blue/object/type.h>
|
||||||
|
#include <blue/object/string.h>
|
||||||
|
#include <blue/core/stringstream.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
void b_object_init(struct b_object *obj, struct b_object_type *type)
|
||||||
|
{
|
||||||
|
obj->ob_ref = 1;
|
||||||
|
obj->ob_type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct b_object *b_make_rvalue(struct b_object *obj)
|
||||||
|
{
|
||||||
|
obj->ob_ref = 0;
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct b_object *b_retain(struct b_object *obj)
|
||||||
|
{
|
||||||
|
obj->ob_ref++;
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
void b_release(struct b_object *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_object *obj, struct b_stringstream *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_stringstream_addf(out, "<%s@%p>", name, obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
b_object_type_id b_typeid(const struct b_object *obj)
|
||||||
|
{
|
||||||
|
if (obj->ob_type->t_flags & B_OBJECT_FUNDAMENTAL) {
|
||||||
|
return obj->ob_type->t_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (b_object_type_id)obj->ob_type;
|
||||||
|
}
|
||||||
6
object/object.h
Normal file
6
object/object.h
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#ifndef _BLUELIB_OBJECT_H_
|
||||||
|
#define _BLUELIB_OBJECT_H_
|
||||||
|
|
||||||
|
#include <blue/object/object.h>
|
||||||
|
|
||||||
|
#endif
|
||||||
497
object/string.c
Normal file
497
object/string.c
Normal file
@@ -0,0 +1,497 @@
|
|||||||
|
#include "string.h"
|
||||||
|
|
||||||
|
#include <blue/core/stringstream.h>
|
||||||
|
#include <blue/object/string.h>
|
||||||
|
#include <blue/object/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_object *obj);
|
||||||
|
static void string_to_string(struct b_object *obj, struct b_stringstream *out);
|
||||||
|
|
||||||
|
static struct b_object_type string_type = {
|
||||||
|
.t_name = "corelib::string",
|
||||||
|
.t_flags = B_OBJECT_FUNDAMENTAL,
|
||||||
|
.t_id = B_OBJECT_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_object_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
} else {
|
||||||
|
dest = src;
|
||||||
|
str->s_data.d_external = NULL;
|
||||||
|
str->s_max = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void string_release(struct b_object *obj)
|
||||||
|
{
|
||||||
|
struct b_string *str = B_STRING(obj);
|
||||||
|
if (!string_is_inline(str)) {
|
||||||
|
free(string_ptr(str));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void string_to_string(struct b_object *obj, struct b_stringstream *out)
|
||||||
|
{
|
||||||
|
b_string *str = B_STRING(obj);
|
||||||
|
b_stringstream_add(out, b_string_ptr(str));
|
||||||
|
}
|
||||||
|
|
||||||
|
void b_strv_builder_begin(b_strv_builder *strv, char *buf, size_t max)
|
||||||
|
{
|
||||||
|
strv->strv_buf = buf;
|
||||||
|
strv->strv_max = max;
|
||||||
|
strv->strv_len = 0;
|
||||||
|
strv->strv_alloc = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void b_strv_builder_begin_dynamic(b_strv_builder *strv)
|
||||||
|
{
|
||||||
|
strv->strv_buf = NULL;
|
||||||
|
strv->strv_max = 0;
|
||||||
|
strv->strv_len = 0;
|
||||||
|
strv->strv_alloc = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static b_status strv_builder_push_string(
|
||||||
|
b_strv_builder *strv, const char *s, size_t len)
|
||||||
|
{
|
||||||
|
if (strv->strv_len + len >= strv->strv_max && strv->strv_alloc == 1) {
|
||||||
|
char *new_buf = realloc(strv->strv_buf, strv->strv_len + len + 1);
|
||||||
|
if (!new_buf) {
|
||||||
|
return B_ERR_NO_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
strv->strv_buf = new_buf;
|
||||||
|
strv->strv_max = strv->strv_len + len + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < len; i++) {
|
||||||
|
if (strv->strv_len < strv->strv_max) {
|
||||||
|
strv->strv_buf[strv->strv_len++] = s[i];
|
||||||
|
strv->strv_buf[strv->strv_len] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
b_status b_strv_builder_add(struct b_strv_builder *strv, const char *str)
|
||||||
|
{
|
||||||
|
return strv_builder_push_string(strv, str, strlen(str));
|
||||||
|
}
|
||||||
|
|
||||||
|
b_status b_strv_builder_addf(struct b_strv_builder *strv, const char *format, ...)
|
||||||
|
{
|
||||||
|
char str[1024];
|
||||||
|
va_list arg;
|
||||||
|
va_start(arg, format);
|
||||||
|
size_t len = vsnprintf(str, sizeof str, format, arg);
|
||||||
|
va_end(arg);
|
||||||
|
|
||||||
|
return strv_builder_push_string(strv, str, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
b_status b_strv_builder_addv(b_strv_builder *strv, const char **strs)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; strs[i]; i++) {
|
||||||
|
size_t len = strlen(strs[i]);
|
||||||
|
b_status status = strv_builder_push_string(strv, strs[i], len);
|
||||||
|
if (B_ERR(status)) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
b_status b_strv_builder_addvl(b_strv_builder *strv, const char **strs, size_t count)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < count; i++) {
|
||||||
|
if (!strs[i]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t len = strlen(strs[i]);
|
||||||
|
b_status status = strv_builder_push_string(strv, strs[i], len);
|
||||||
|
if (B_ERR(status)) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
b_status b_strv_builder_add_many(b_strv_builder *strv, ...)
|
||||||
|
{
|
||||||
|
va_list arg;
|
||||||
|
va_start(arg, strv);
|
||||||
|
while (1) {
|
||||||
|
const char *s = va_arg(arg, const char *);
|
||||||
|
if (!s) {
|
||||||
|
return B_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t len = strlen(s);
|
||||||
|
b_status status = strv_builder_push_string(strv, s, len);
|
||||||
|
if (B_ERR(status)) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *b_strv_builder_end(b_strv_builder *strv)
|
||||||
|
{
|
||||||
|
char *out = strv->strv_buf;
|
||||||
|
|
||||||
|
strv->strv_alloc = 0;
|
||||||
|
strv->strv_len = 0;
|
||||||
|
strv->strv_max = 0;
|
||||||
|
strv->strv_buf = NULL;
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)) {
|
||||||
|
return strlen(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t out = 0;
|
||||||
|
bool esc = false;
|
||||||
|
for (size_t i = 0; s[i]; i++) {
|
||||||
|
if (s[i] == '\033') {
|
||||||
|
esc = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!esc) {
|
||||||
|
out++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isalpha(s[i])) {
|
||||||
|
esc = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
b_object_type_id b_string_type_id(void)
|
||||||
|
{
|
||||||
|
return (b_object_type_id)&string_type;
|
||||||
|
}
|
||||||
21
object/string.h
Normal file
21
object/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_object 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
object/tree.c
Normal file
300
object/tree.c
Normal file
@@ -0,0 +1,300 @@
|
|||||||
|
#include "tree.h"
|
||||||
|
|
||||||
|
#include <blue/object/object.h>
|
||||||
|
#include <blue/object/tree.h>
|
||||||
|
#include <blue/object/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_object_type tree_type = {
|
||||||
|
.t_name = "corelib::tree",
|
||||||
|
.t_flags = B_OBJECT_FUNDAMENTAL,
|
||||||
|
.t_id = B_OBJECT_TYPE_TREE,
|
||||||
|
.t_instance_size = sizeof(struct b_tree),
|
||||||
|
};
|
||||||
|
|
||||||
|
struct b_tree *b_tree_create(void)
|
||||||
|
{
|
||||||
|
struct b_tree *tree
|
||||||
|
= (struct b_tree *)b_object_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_object_type_id b_tree_type_id(void)
|
||||||
|
{
|
||||||
|
return (b_object_type_id)&tree_type;
|
||||||
|
}
|
||||||
14
object/tree.h
Normal file
14
object/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/object/tree.h>
|
||||||
|
|
||||||
|
struct b_tree {
|
||||||
|
struct b_object t_base;
|
||||||
|
struct b_tree_node *t_root;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
29
object/type.c
Normal file
29
object/type.c
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#include "object.h"
|
||||||
|
|
||||||
|
#include <blue/core/queue.h>
|
||||||
|
#include <blue/object/type.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
struct b_object *b_object_type_instantiate(const b_object_type *type)
|
||||||
|
{
|
||||||
|
if (!type || type->t_instance_size < sizeof(struct b_object)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct b_object *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;
|
||||||
|
}
|
||||||
187
object/uuid.c
Normal file
187
object/uuid.c
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <blue/object/type.h>
|
||||||
|
#include <blue/object/uuid.h>
|
||||||
|
#include <blue/object/string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include "uuid.h"
|
||||||
|
|
||||||
|
static struct b_object_type uuid_type = {
|
||||||
|
.t_name = "corelib::uuid",
|
||||||
|
.t_flags = B_OBJECT_FUNDAMENTAL,
|
||||||
|
.t_id = B_OBJECT_TYPE_UUID,
|
||||||
|
.t_instance_size = sizeof(struct b_uuid),
|
||||||
|
};
|
||||||
|
|
||||||
|
struct b_uuid *b_uuid_create(void)
|
||||||
|
{
|
||||||
|
struct b_uuid *out = (struct b_uuid *)b_object_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_strv_builder(const struct b_uuid *uuid, struct b_strv_builder *out)
|
||||||
|
{
|
||||||
|
char str[B_UUID_STRING_MAX];
|
||||||
|
b_uuid_to_cstr(uuid, str);
|
||||||
|
b_strv_builder_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_object_type_id b_uuid_type_id(void)
|
||||||
|
{
|
||||||
|
return (b_object_type_id)&uuid_type;
|
||||||
|
}
|
||||||
11
object/uuid.h
Normal file
11
object/uuid.h
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#ifndef _BLUELIB_UUID_H_
|
||||||
|
#define _BLUELIB_UUID_H_
|
||||||
|
|
||||||
|
#include <blue/object/uuid.h>
|
||||||
|
|
||||||
|
struct b_uuid {
|
||||||
|
struct b_object u_base;
|
||||||
|
struct b_uuid_bytes uuid_bytes;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
Reference in New Issue
Block a user