|
// retoor <retoor@molodetz.nl>
|
|
|
|
#include "string.h"
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
struct string_t {
|
|
char *data;
|
|
size_t length;
|
|
size_t capacity;
|
|
};
|
|
|
|
static const size_t DEFAULT_CAPACITY = 32;
|
|
|
|
string_handle string_create(const char *str) {
|
|
if (!str) return string_create_empty();
|
|
return string_create_n(str, strlen(str));
|
|
}
|
|
|
|
string_handle string_create_empty(void) {
|
|
struct string_t *s = calloc(1, sizeof(struct string_t));
|
|
if (!s) return NULL;
|
|
|
|
s->data = malloc(DEFAULT_CAPACITY);
|
|
if (!s->data) {
|
|
free(s);
|
|
return NULL;
|
|
}
|
|
|
|
s->data[0] = '\0';
|
|
s->length = 0;
|
|
s->capacity = DEFAULT_CAPACITY;
|
|
return s;
|
|
}
|
|
|
|
string_handle string_create_n(const char *str, size_t len) {
|
|
if (!str) return string_create_empty();
|
|
if (len == 0) return string_create_empty();
|
|
|
|
struct string_t *s = malloc(sizeof(struct string_t));
|
|
if (!s) return NULL;
|
|
|
|
s->capacity = len + 1;
|
|
if (s->capacity < DEFAULT_CAPACITY) s->capacity = DEFAULT_CAPACITY;
|
|
|
|
s->data = malloc(s->capacity);
|
|
if (!s->data) {
|
|
free(s);
|
|
return NULL;
|
|
}
|
|
|
|
memcpy(s->data, str, len);
|
|
s->data[len] = '\0';
|
|
s->length = len;
|
|
return s;
|
|
}
|
|
|
|
string_handle string_format(const char *fmt, ...) {
|
|
if (!fmt) return string_create_empty();
|
|
|
|
va_list args, args_copy;
|
|
va_start(args, fmt);
|
|
va_copy(args_copy, args);
|
|
|
|
int len = vsnprintf(NULL, 0, fmt, args_copy);
|
|
va_end(args_copy);
|
|
|
|
if (len < 0) {
|
|
va_end(args);
|
|
return string_create_empty();
|
|
}
|
|
|
|
struct string_t *s = malloc(sizeof(struct string_t));
|
|
if (!s) {
|
|
va_end(args);
|
|
return NULL;
|
|
}
|
|
|
|
s->capacity = (size_t)len + 1;
|
|
s->data = malloc(s->capacity);
|
|
if (!s->data) {
|
|
free(s);
|
|
va_end(args);
|
|
return NULL;
|
|
}
|
|
|
|
vsnprintf(s->data, s->capacity, fmt, args);
|
|
s->length = (size_t)len;
|
|
va_end(args);
|
|
|
|
return s;
|
|
}
|
|
|
|
string_handle string_clone(const char *str) {
|
|
return string_create(str);
|
|
}
|
|
|
|
string_handle string_clone_handle(string_handle s) {
|
|
if (!s) return NULL;
|
|
return string_create_n(s->data, s->length);
|
|
}
|
|
|
|
string_handle string_concat(const char *a, const char *b) {
|
|
if (!a) return string_create(b);
|
|
if (!b) return string_create(a);
|
|
|
|
size_t len_a = strlen(a);
|
|
size_t len_b = strlen(b);
|
|
|
|
struct string_t *s = malloc(sizeof(struct string_t));
|
|
if (!s) return NULL;
|
|
|
|
s->length = len_a + len_b;
|
|
s->capacity = s->length + 1;
|
|
s->data = malloc(s->capacity);
|
|
if (!s->data) {
|
|
free(s);
|
|
return NULL;
|
|
}
|
|
|
|
memcpy(s->data, a, len_a);
|
|
memcpy(s->data + len_a, b, len_b);
|
|
s->data[s->length] = '\0';
|
|
|
|
return s;
|
|
}
|
|
|
|
string_handle string_concat_handle(string_handle a, const char *b) {
|
|
if (!a) return string_create(b);
|
|
if (!b) return string_clone_handle(a);
|
|
|
|
size_t len_b = strlen(b);
|
|
size_t new_len = a->length + len_b;
|
|
|
|
if (new_len + 1 > a->capacity) {
|
|
size_t new_capacity = a->capacity * 2;
|
|
while (new_capacity < new_len + 1) new_capacity *= 2;
|
|
|
|
char *new_data = realloc(a->data, new_capacity);
|
|
if (!new_data) return NULL;
|
|
|
|
a->data = new_data;
|
|
a->capacity = new_capacity;
|
|
}
|
|
|
|
memcpy(a->data + a->length, b, len_b);
|
|
a->length = new_len;
|
|
a->data[a->length] = '\0';
|
|
|
|
return a;
|
|
}
|
|
|
|
void string_append(string_handle s, const char *str) {
|
|
if (!s || !str) return;
|
|
string_append_n(s, str, strlen(str));
|
|
}
|
|
|
|
void string_append_n(string_handle s, const char *str, size_t len) {
|
|
if (!s || !str || len == 0) return;
|
|
|
|
if (s->length + len + 1 > s->capacity) {
|
|
size_t new_capacity = s->capacity;
|
|
while (new_capacity < s->length + len + 1) new_capacity *= 2;
|
|
|
|
char *new_data = realloc(s->data, new_capacity);
|
|
if (!new_data) return;
|
|
|
|
s->data = new_data;
|
|
s->capacity = new_capacity;
|
|
}
|
|
|
|
memcpy(s->data + s->length, str, len);
|
|
s->length += len;
|
|
s->data[s->length] = '\0';
|
|
}
|
|
|
|
void string_appendf(string_handle s, const char *fmt, ...) {
|
|
if (!s || !fmt) return;
|
|
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
|
|
int len = vsnprintf(NULL, 0, fmt, args);
|
|
va_end(args);
|
|
|
|
if (len < 0) return;
|
|
|
|
if (s->length + (size_t)len + 1 > s->capacity) {
|
|
size_t new_capacity = s->capacity;
|
|
while (new_capacity < s->length + (size_t)len + 1) new_capacity *= 2;
|
|
|
|
char *new_data = realloc(s->data, new_capacity);
|
|
if (!new_data) return;
|
|
|
|
s->data = new_data;
|
|
s->capacity = new_capacity;
|
|
}
|
|
|
|
va_start(args, fmt);
|
|
vsnprintf(s->data + s->length, s->capacity - s->length, fmt, args);
|
|
s->length += (size_t)len;
|
|
va_end(args);
|
|
}
|
|
|
|
void string_append_handle(string_handle dest, string_handle src) {
|
|
if (!dest || !src) return;
|
|
string_append_n(dest, src->data, src->length);
|
|
}
|
|
|
|
size_t string_length(string_handle s) {
|
|
return s ? s->length : 0;
|
|
}
|
|
|
|
size_t string_capacity(string_handle s) {
|
|
return s ? s->capacity : 0;
|
|
}
|
|
|
|
const char *string_c_str(string_handle s) {
|
|
return s ? s->data : "";
|
|
}
|
|
|
|
char *string_release(string_handle s) {
|
|
if (!s) return NULL;
|
|
|
|
char *data = s->data;
|
|
free(s);
|
|
return data;
|
|
}
|
|
|
|
bool string_equals(string_handle a, string_handle b) {
|
|
if (!a && !b) return true;
|
|
if (!a || !b) return false;
|
|
if (a->length != b->length) return false;
|
|
return memcmp(a->data, b->data, a->length) == 0;
|
|
}
|
|
|
|
bool string_equals_c_str(string_handle s, const char *str) {
|
|
if (!s && !str) return true;
|
|
if (!s || !str) return false;
|
|
return strcmp(s->data, str) == 0;
|
|
}
|
|
|
|
bool string_is_empty(string_handle s) {
|
|
return !s || s->length == 0;
|
|
}
|
|
|
|
void string_clear(string_handle s) {
|
|
if (!s) return;
|
|
s->length = 0;
|
|
if (s->data) s->data[0] = '\0';
|
|
}
|
|
|
|
void string_destroy(string_handle s) {
|
|
if (!s) return;
|
|
if (s->data) free(s->data);
|
|
free(s);
|
|
}
|