core: add rope data structure for efficiently building strings
This commit is contained in:
103
core/include/blue/core/rope.h
Normal file
103
core/include/blue/core/rope.h
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
#ifndef BLUE_CORE_ROPE_H_
|
||||||
|
#define BLUE_CORE_ROPE_H_
|
||||||
|
|
||||||
|
#include <blue/core/hash.h>
|
||||||
|
#include <blue/core/misc.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
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
|
||||||
236
core/rope.c
Normal file
236
core/rope.c
Normal file
@@ -0,0 +1,236 @@
|
|||||||
|
#include <blue/core/rope.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user