#include "string.h" #include #include #include #include #include #include #include #include #include #include static void string_release(struct b_object *obj); static void string_to_string(struct b_object *obj, struct b_stream *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); src[0] = 0; } else { dest = src; str->s_data.d_external = NULL; str->s_max = STRING_INLINE_CAPACITY; } str->s_len = 0; return dest; } b_status b_string_reserve(struct b_string *str, size_t capacity) { if (str->s_max >= capacity) { return B_SUCCESS; } int err = string_change_capacity(str, capacity); return err == 0 ? B_SUCCESS : B_ERR_NO_MEMORY; } b_status b_string_replace_all(b_string *str, const char *new_data) { size_t new_len = strlen(new_data); b_string_reserve(str, new_len); char *dest = (char *)b_string_ptr(str); memcpy(dest, new_data, new_len); dest[new_len] = '\0'; str->s_len = new_len; return B_SUCCESS; } enum b_status stream_close(struct b_stream *stream) { struct b_string *str = stream->s_ptr; b_string_release(str); return B_SUCCESS; } enum b_status stream_getc(struct b_stream *stream, int *out) { struct b_string *str = stream->s_ptr; if (stream->s_cursor >= str->s_len) { return B_ERR_NO_DATA; } char *s = string_ptr(str); *out = s[stream->s_cursor]; stream->s_cursor++; return B_SUCCESS; } enum b_status stream_read( struct b_stream *stream, unsigned char *buf, size_t count, size_t *nr_read) { struct b_string *str = stream->s_ptr; if (stream->s_cursor >= str->s_len) { *nr_read = 0; return B_SUCCESS; } size_t available = str->s_len - stream->s_cursor; size_t to_read = b_min(size_t, count, available); char *s = string_ptr(str) + stream->s_cursor; memcpy(buf, s, to_read); *nr_read = to_read; return B_SUCCESS; } enum b_status stream_write( struct b_stream *stream, const unsigned char *buf, size_t count, size_t *nr_written) { struct b_string *str = stream->s_ptr; enum b_status status = B_SUCCESS; if (stream->s_cursor + count > str->s_max) { status = b_string_reserve(str, stream->s_cursor + count); } if (!B_OK(status)) { return status; } char *s = string_ptr(str) + stream->s_cursor; memcpy(s, buf, count); s[str->s_max] = '\0'; stream->s_cursor += count; str->s_len = b_max(size_t, str->s_len, stream->s_cursor + count); *nr_written = count; return B_SUCCESS; } enum b_status stream_seek( struct b_stream *stream, long long offset, b_stream_seek_origin origin) { struct b_string *str = stream->s_ptr; size_t abs_offset; switch (origin) { case B_STREAM_SEEK_START: abs_offset = offset; break; case B_STREAM_SEEK_CURRENT: abs_offset = stream->s_cursor + offset; break; case B_STREAM_SEEK_END: abs_offset = str->s_len + offset; break; default: return B_ERR_INVALID_ARGUMENT; } stream->s_cursor = abs_offset; return B_SUCCESS; } enum b_status stream_reserve(struct b_stream *stream, size_t len) { struct b_string *str = stream->s_ptr; size_t new_capacity = str->s_len + len; return b_string_reserve(str, new_capacity); } enum b_status b_string_open_stream(struct b_string *str, struct b_stream **out) { struct b_stream *stream = malloc(sizeof *stream); if (!stream) { return B_ERR_NO_MEMORY; } memset(stream, 0x0, sizeof *stream); stream->s_mode |= B_STREAM_READ | B_STREAM_WRITE; stream->s_ptr = b_string_retain(str); stream->s_close = stream_close; stream->s_getc = stream_getc; stream->s_read = stream_read; stream->s_write = stream_write; stream->s_seek = stream_seek; stream->s_reserve = stream_reserve; *out = stream; return B_SUCCESS; } static void string_insert( struct b_string *dest, const char *src, size_t len, size_t at) { if (at >= dest->s_len) { at = dest->s_len; } size_t new_size = dest->s_len + len; if (dest->s_max < new_size) { string_change_capacity(dest, new_size); } char *dest_buf = string_ptr(dest); char *from = dest_buf + at; char *to = dest_buf + at + len; memmove(to, from, dest->s_len - at); memcpy(from, src, len); dest_buf[new_size] = '\0'; dest->s_len = new_size; } static void string_insertf( struct b_string *dest, size_t at, const char *format, va_list arg) { char buf[1024]; size_t len = vsnprintf(buf, sizeof buf, format, arg); string_insert(dest, buf, len, at); } void b_string_insert_s(struct b_string *dest, const struct b_string *src, size_t at) { string_insert(dest, b_string_ptr(src), src->s_len, at); } void b_string_insert_cstr(struct b_string *dest, const char *src, size_t at) { string_insert(dest, src, strlen(src), at); } void b_string_insert_cstrf(struct b_string *dest, size_t at, const char *format, ...) { va_list arg; va_start(arg, format); string_insertf(dest, at, format, arg); va_end(arg); } void b_string_insert_cstrn(b_string *dest, const char *src, size_t len, size_t at) { string_insert(dest, src, len, at); } void b_string_append_s(struct b_string *dest, const struct b_string *src) { b_string_insert_s(dest, src, SIZE_MAX); } void b_string_append_cstr(struct b_string *dest, const char *src) { b_string_insert_cstr(dest, src, SIZE_MAX); } void b_string_append_cstrf(struct b_string *dest, const char *format, ...) { va_list arg; va_start(arg, format); string_insertf(dest, SIZE_MAX, format, arg); va_end(arg); } void b_string_prepend_s(struct b_string *dest, const struct b_string *src) { b_string_insert_s(dest, src, 0); } void b_string_prepend_cstr(struct b_string *dest, const char *src) { b_string_insert_cstr(dest, src, 0); } void b_string_prepend_cstrf(struct b_string *dest, const char *format, ...) { va_list arg; va_start(arg, format); string_insertf(dest, 0, format, arg); va_end(arg); } void b_string_clear(struct b_string *str) { if (str->s_len == 0) { return; } char *s = string_ptr(str); *s = '\0'; str->s_len = 0; } size_t b_string_get_size(const struct b_string *str, b_strlen_flags flags) { if (flags != B_STRLEN_NORMAL) { return b_strlen(b_string_ptr(str), flags); } return str->s_len; } size_t b_string_get_capacity(const struct b_string *str) { return str->s_max; } char b_string_front(const struct b_string *str) { if (str->s_len == 0) { return 0; } const char *s = b_string_ptr(str); return s[0]; } char b_string_back(const struct b_string *str) { if (str->s_len == 0) { return 0; } const char *s = b_string_ptr(str); return s[str->s_len - 1]; } void b_string_pop_back(struct b_string *str) { if (str->s_len == 0) { return; } char *s = string_ptr(str); s[str->s_len - 1] = '\0'; str->s_len--; } const char *b_string_ptr(const struct b_string *str) { if (string_is_inline(str)) { return str->s_data.d_inline; } return str->s_data.d_external; } struct b_string *b_string_substr(const struct b_string *str, size_t start, size_t len) { if (start > b_string_get_size(str, B_STRLEN_NORMAL)) { return NULL; } if (start + len > b_string_get_size(str, B_STRLEN_NORMAL)) { len = b_string_get_size(str, B_STRLEN_NORMAL) - start; } struct b_string *newstr = b_string_create(); b_string_reserve(newstr, len); const char *src = b_string_ptr(str) + start; char *dest = string_ptr(newstr); memcpy(dest, src, len); newstr->s_len = len; return newstr; } static void string_release(struct b_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_stream *out) { b_string *str = B_STRING(obj); b_stream_write_fmt(out, NULL, "%s", b_string_ptr(str)); } char *b_strdup(const char *s) { size_t len = strlen(s); char *p = malloc(len + 1); if (!p) { return NULL; } memcpy(p, s, len); p[len] = '\0'; return p; } size_t b_strlen(const char *s, b_strlen_flags flags) { if (!(flags & (B_STRLEN_IGNORE_ESC | B_STRLEN_IGNORE_MOD))) { return strlen(s); } size_t out = 0; for (size_t i = 0; s[i]; i++) { if (s[i] == '\033' && (flags & B_STRLEN_IGNORE_ESC)) { while (!isalpha(s[i]) && s[i]) { i++; } continue; } if (s[i] == '[' && (flags & B_STRLEN_IGNORE_MOD)) { i++; if (s[i] == '[') { out++; continue; } while (s[i] != ']' && s[i]) { i++; } continue; } out++; } return out; } b_object_type_id b_string_type_id(void) { return (b_object_type_id)&string_type; }