diff --git a/core/include/blue/core/rope.h b/core/include/blue/core/rope.h new file mode 100644 index 0000000..dd3666d --- /dev/null +++ b/core/include/blue/core/rope.h @@ -0,0 +1,103 @@ +#ifndef BLUE_CORE_ROPE_H_ +#define BLUE_CORE_ROPE_H_ + +#include +#include +#include +#include + +struct b_string; + +#define B_ROPE_TYPE(f) ((f) & 0xFF) + +#define B_ROPE_CHAR(c) \ + \ + { \ + .r_flags = B_ROPE_F_CHAR, .r_len_total = 1, \ + .r_v = {.v_char = (c) } \ + } + +#define B_ROPE_CSTR(str) \ + { \ + .r_flags = B_ROPE_F_CSTR_BORROWED, \ + .r_len_total = strlen(str), \ + .r_v = { \ + .v_cstr = { \ + .s = (str), \ + .hash = b_hash_string(str), \ + }, \ + }, \ + } + +#define B_ROPE_CSTR_STATIC(str) \ + { \ + .r_flags = B_ROPE_F_CSTR_STATIC, \ + .r_len_total = strlen(str), \ + .r_v = { \ + .v_cstr = { \ + .s = (str), \ + .hash = b_hash_string(str), \ + }, \ + }, \ + } +#define B_ROPE_INT(v) \ + \ + { \ + .r_flags = B_ROPE_F_INT, .r_len_total = b_int_length(v), \ + .r_v = {.v_int = (v) } \ + } +#define B_ROPE_UINT(v) \ + \ + { \ + .r_flags = B_ROPE_F_UINT, .r_len_total = b_uint_length(v), \ + .r_v = {.v_uint = (v) } \ + } + +typedef enum b_rope_flags { + B_ROPE_F_NONE = 0x0000u, + B_ROPE_F_CHAR = 0x0001u, + B_ROPE_F_CSTR = 0x0002u, + B_ROPE_F_CSTR_BORROWED = 0x0003u, + B_ROPE_F_CSTR_STATIC = 0x0004u, + B_ROPE_F_INT = 0x0005u, + B_ROPE_F_UINT = 0x0006u, + B_ROPE_F_COMPOSITE = 0x0007u, + B_ROPE_F_MALLOC = 0x0100u, +} b_rope_flags; + +typedef struct b_rope { + b_rope_flags r_flags; + unsigned long r_len_left, r_len_total; + + union { + char v_char; + intptr_t v_int; + uintptr_t v_uint; + + struct { + const char *s; + uint64_t hash; + } v_cstr; + + struct { + const struct b_rope *r_left, *r_right; + } v_composite; + } r_v; +} b_rope; + +BLUE_API void b_rope_init_char(b_rope *rope, char c); +BLUE_API void b_rope_init_cstr(b_rope *rope, const char *s); +BLUE_API void b_rope_init_cstr_borrowed(b_rope *rope, const char *s); +BLUE_API void b_rope_init_cstr_static(b_rope *rope, const char *s); +BLUE_API void b_rope_init_int(b_rope *rope, intptr_t v); +BLUE_API void b_rope_init_uint(b_rope *rope, uintptr_t v); + +BLUE_API void b_rope_destroy(b_rope *rope); + +BLUE_API size_t b_rope_get_size(const b_rope *rope); +BLUE_API void b_rope_concat(b_rope *result, const b_rope *left, const b_rope *right); +BLUE_API void b_rope_join(b_rope *result, const b_rope **ropes, size_t nr_ropes); + +BLUE_API void b_rope_to_cstr(const b_rope *rope, char *out, size_t max); + +#endif diff --git a/core/rope.c b/core/rope.c new file mode 100644 index 0000000..ad146d6 --- /dev/null +++ b/core/rope.c @@ -0,0 +1,236 @@ +#include +#include +#include +#include +#include + +void b_rope_init_char(struct b_rope *rope, char c) +{ + memset(rope, 0x0, sizeof *rope); + rope->r_flags = B_ROPE_F_CHAR; + rope->r_len_left = rope->r_len_total = 1; + rope->r_v.v_char = c; +} + +void b_rope_init_cstr(struct b_rope *rope, const char *s) +{ + memset(rope, 0x0, sizeof *rope); + rope->r_flags = B_ROPE_F_CSTR; + rope->r_v.v_cstr.hash = b_hash_string_ex(s, &rope->r_len_total); + rope->r_len_left = rope->r_len_total; + + char *s2 = malloc(rope->r_len_total + 1); + + if (!s2) { + return; + } + + strcpy(s2, s); + s2[rope->r_len_total] = '\0'; + + rope->r_v.v_cstr.s = s2; +} + +void b_rope_init_cstr_borrowed(struct b_rope *rope, const char *s) +{ + memset(rope, 0x0, sizeof *rope); + rope->r_flags = B_ROPE_F_CSTR_BORROWED; + rope->r_v.v_cstr.s = s; + rope->r_v.v_cstr.hash = b_hash_string_ex(s, &rope->r_len_total); + rope->r_len_left = rope->r_len_total; +} + +void b_rope_init_cstr_static(struct b_rope *rope, const char *s) +{ + memset(rope, 0x0, sizeof *rope); + rope->r_flags = B_ROPE_F_CSTR_STATIC; + rope->r_v.v_cstr.s = s; + rope->r_v.v_cstr.hash = b_hash_string_ex(s, &rope->r_len_total); + rope->r_len_left = rope->r_len_total; +} + +void b_rope_init_int(struct b_rope *rope, intptr_t v) +{ + memset(rope, 0x0, sizeof *rope); + rope->r_flags = B_ROPE_F_INT; + rope->r_len_left = rope->r_len_total = b_int_length(v); + rope->r_v.v_int = v; +} + +void b_rope_init_uint(struct b_rope *rope, uintptr_t v) +{ + memset(rope, 0x0, sizeof *rope); + rope->r_flags = B_ROPE_F_UINT; + rope->r_len_left = rope->r_len_total = b_uint_length(v); + rope->r_v.v_uint = v; +} + +void b_rope_destroy(struct b_rope *rope) +{ + unsigned int type = B_ROPE_TYPE(rope->r_flags); + + switch (type) { + case B_ROPE_F_CSTR: + free((char *)rope->r_v.v_cstr.s); + break; + case B_ROPE_F_COMPOSITE: + b_rope_destroy((struct b_rope *)rope->r_v.v_composite.r_left); + b_rope_destroy((struct b_rope *)rope->r_v.v_composite.r_right); + break; + default: + break; + } + + if (rope->r_flags & B_ROPE_F_MALLOC) { + free(rope); + } +} + +size_t b_rope_get_size(const struct b_rope *rope) +{ + return rope->r_len_total; +} + +void b_rope_concat( + struct b_rope *result, const struct b_rope *left, const struct b_rope *right) +{ + memset(result, 0x0, sizeof *result); + + result->r_flags = B_ROPE_F_COMPOSITE; + result->r_len_left = left->r_len_total; + result->r_len_total = left->r_len_total + right->r_len_total; + result->r_v.v_composite.r_left = left; + result->r_v.v_composite.r_right = right; +} + +static struct b_rope *create_composite_node(void) +{ + struct b_rope *out = malloc(sizeof *out); + if (!out) { + return NULL; + } + + memset(out, 0x0, sizeof *out); + + out->r_flags = B_ROPE_F_COMPOSITE | B_ROPE_F_MALLOC; + + return out; +} + +void b_rope_join(b_rope *result, const b_rope **ropes, size_t nr_ropes) +{ + if (nr_ropes == 0) { + return; + } + + if (nr_ropes == 1) { + *result = *ropes[0]; + return; + } + + struct b_rope *prev = NULL; + + for (size_t i = 0; i < nr_ropes - 1; i++) { + const struct b_rope *left = ropes[i]; + const struct b_rope *right = ropes[i + 1]; + + if (prev) { + left = prev; + } + + struct b_rope *composite = create_composite_node(); + if (!composite) { + return; + } + + composite->r_len_left = left->r_len_total; + composite->r_len_total = left->r_len_total + right->r_len_total; + composite->r_v.v_composite.r_left = left; + composite->r_v.v_composite.r_right = right; + + prev = composite; + } + + *result = *prev; + + result->r_flags &= ~B_ROPE_F_MALLOC; + free(prev); +} + +static void rope_iterate( + const struct b_rope *rope, + void (*callback)(const struct b_rope *, void *), void *arg) +{ + unsigned int type = B_ROPE_TYPE(rope->r_flags); + + switch (type) { + case B_ROPE_F_CHAR: + case B_ROPE_F_CSTR: + case B_ROPE_F_CSTR_BORROWED: + case B_ROPE_F_CSTR_STATIC: + case B_ROPE_F_INT: + case B_ROPE_F_UINT: + callback(rope, arg); + break; + case B_ROPE_F_COMPOSITE: + rope_iterate(rope->r_v.v_composite.r_left, callback, arg); + rope_iterate(rope->r_v.v_composite.r_right, callback, arg); + break; + default: + break; + } +} + +struct to_cstr_args { + char *str; + size_t max; + size_t ptr; +}; + +static void to_cstr(const struct b_rope *rope, void *arg) +{ + struct to_cstr_args *str = arg; + if (str->ptr >= str->max) { + return; + } + + unsigned int type = B_ROPE_TYPE(rope->r_flags); + + char *dest = str->str + str->ptr; + size_t capacity = str->max - str->ptr; + size_t nr_written = 0; + + switch (type) { + case B_ROPE_F_CHAR: + nr_written = snprintf(dest, capacity, "%c", rope->r_v.v_char); + break; + case B_ROPE_F_CSTR: + case B_ROPE_F_CSTR_BORROWED: + case B_ROPE_F_CSTR_STATIC: + nr_written = snprintf(dest, capacity, "%s", rope->r_v.v_cstr.s); + break; + case B_ROPE_F_INT: + nr_written + = snprintf(dest, capacity, "%" PRIdPTR, rope->r_v.v_int); + break; + case B_ROPE_F_UINT: + nr_written + = snprintf(dest, capacity, "%" PRIuPTR, rope->r_v.v_uint); + break; + default: + break; + } + + str->ptr += nr_written; +} + +void b_rope_to_cstr(const struct b_rope *rope, char *out, size_t max) +{ + struct to_cstr_args args = { + .str = out, + .max = max, + .ptr = 0, + }; + + rope_iterate(rope, to_cstr, &args); +}