Files
bluelib/object/string.c

586 lines
12 KiB
C
Raw Normal View History

2024-10-24 19:24:54 +01:00
#include "string.h"
#include <blue/core/stream.h>
2024-10-24 19:24:54 +01:00
#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_stream *out);
2024-10-24 19:24:54 +01:00
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;
2024-10-24 19:24:54 +01:00
}
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;
2024-10-24 19:24:54 +01:00
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;
2024-10-24 19:24:54 +01:00
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;
2024-10-24 19:24:54 +01:00
} else {
dest = src;
str->s_data.d_external = NULL;
str->s_max = STRING_INLINE_CAPACITY;
2024-10-24 19:24:54 +01:00
}
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;
}
static enum b_status stream_close(struct b_stream *stream)
{
struct b_string *str = stream->s_ptr;
b_string_release(str);
return B_SUCCESS;
}
static enum b_status stream_getc(struct b_stream *stream, int *out)
{
struct b_string *str = stream->s_ptr;
if (stream->s_cursor >= str->s_len) {
return B_ERR_NO_DATA;
}
char *s = string_ptr(str);
*out = s[stream->s_cursor];
stream->s_cursor++;
return B_SUCCESS;
}
static enum b_status stream_read(
struct b_stream *stream, unsigned char *buf, size_t count, size_t *nr_read)
{
struct b_string *str = stream->s_ptr;
if (stream->s_cursor >= str->s_len) {
*nr_read = 0;
return B_SUCCESS;
}
size_t available = str->s_len - stream->s_cursor;
size_t to_read = b_min(size_t, count, available);
char *s = string_ptr(str) + stream->s_cursor;
memcpy(buf, s, to_read);
*nr_read = to_read;
return B_SUCCESS;
}
static enum b_status stream_write(
struct b_stream *stream, const unsigned char *buf, size_t count,
size_t *nr_written)
{
struct b_string *str = stream->s_ptr;
enum b_status status = B_SUCCESS;
if (stream->s_cursor + count > str->s_max) {
status = b_string_reserve(str, stream->s_cursor + count);
}
if (!B_OK(status)) {
return status;
}
char *s = string_ptr(str) + stream->s_cursor;
memcpy(s, buf, count);
s[str->s_max] = '\0';
stream->s_cursor += count;
str->s_len = b_max(size_t, str->s_len, stream->s_cursor + count);
*nr_written = count;
return B_SUCCESS;
}
static enum b_status stream_seek(
struct b_stream *stream, long long offset, b_stream_seek_origin origin)
{
struct b_string *str = stream->s_ptr;
size_t abs_offset;
switch (origin) {
case B_STREAM_SEEK_START:
abs_offset = offset;
break;
case B_STREAM_SEEK_CURRENT:
abs_offset = stream->s_cursor + offset;
break;
case B_STREAM_SEEK_END:
abs_offset = str->s_len + offset;
break;
default:
return B_ERR_INVALID_ARGUMENT;
}
stream->s_cursor = abs_offset;
return B_SUCCESS;
}
static enum b_status stream_reserve(struct b_stream *stream, size_t len)
{
struct b_string *str = stream->s_ptr;
size_t new_capacity = str->s_len + len;
return b_string_reserve(str, new_capacity);
}
enum b_status b_string_open_stream(struct b_string *str, struct b_stream **out)
{
struct b_stream *stream = malloc(sizeof *stream);
if (!stream) {
return B_ERR_NO_MEMORY;
}
memset(stream, 0x0, sizeof *stream);
stream->s_mode |= B_STREAM_READ | B_STREAM_WRITE;
stream->s_ptr = b_string_retain(str);
stream->s_close = stream_close;
stream->s_getc = stream_getc;
stream->s_read = stream_read;
stream->s_write = stream_write;
stream->s_seek = stream_seek;
stream->s_reserve = stream_reserve;
*out = stream;
return B_SUCCESS;
}
2024-10-24 19:24:54 +01:00
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--;
}
2024-10-24 19:24:54 +01:00
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;
2024-10-24 19:24:54 +01:00
}
2025-04-11 13:55:36 +01:00
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;
}
2024-10-24 19:24:54 +01:00
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)
2024-10-24 19:24:54 +01:00
{
b_string *str = B_STRING(obj);
b_stream_write_fmt(out, NULL, "%s", b_string_ptr(str));
2024-10-24 19:24:54 +01:00
}
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))) {
2024-10-24 19:24:54 +01:00
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++;
}
2024-10-24 19:24:54 +01:00
continue;
2024-10-24 19:24:54 +01:00
}
if (s[i] == '[' && (flags & B_STRLEN_IGNORE_MOD)) {
i++;
if (s[i] == '[') {
out++;
continue;
}
while (s[i] != ']' && s[i]) {
i++;
}
continue;
2024-10-24 19:24:54 +01:00
}
out++;
2024-10-24 19:24:54 +01:00
}
return out;
}
b_object_type_id b_string_type_id(void)
{
return (b_object_type_id)&string_type;
}