Initial version of struct DStr
This commit is contained in:
@@ -0,0 +1,75 @@
|
|||||||
|
#ifndef UC_DSTR_H_
|
||||||
|
#define UC_DSTR_H_
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** A dynamic string.
|
||||||
|
* The string might not always be nul terminated,
|
||||||
|
* Normally, use uc_dstr_str() to manipulate the managed string
|
||||||
|
* instead of accessing ->str directly.
|
||||||
|
*/
|
||||||
|
struct DStr {
|
||||||
|
size_t len;
|
||||||
|
size_t allocated;
|
||||||
|
char *str;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define UC_DSTR_STATIC_INIT {0, 0, NULL}
|
||||||
|
|
||||||
|
/** Initialize a DStr.
|
||||||
|
* Note that str->str will be NULL after initialization.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void uc_dstr_init(struct DStr *str);
|
||||||
|
|
||||||
|
/** Destroy/free the string.
|
||||||
|
* (Does not free str itself, only the managed resource of the str)
|
||||||
|
*/
|
||||||
|
void uc_dstr_destroy(struct DStr *str);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the available/unused tail bytes in the string
|
||||||
|
*/
|
||||||
|
size_t uc_dstr_available(const struct DStr *str);
|
||||||
|
|
||||||
|
/** Ensure the DStr is of at least 'len' bytes,
|
||||||
|
* allocating room if needed.
|
||||||
|
*/
|
||||||
|
int uc_dstr_ensure(struct DStr *str, size_t len);
|
||||||
|
|
||||||
|
void uc_dstr_reset(struct DStr *str);
|
||||||
|
|
||||||
|
void uc_dstr_terminate(struct DStr *str);
|
||||||
|
|
||||||
|
char *uc_dstr_str(struct DStr *str);
|
||||||
|
|
||||||
|
char *uc_dstr_put(struct DStr *str, size_t len);
|
||||||
|
|
||||||
|
size_t uc_dstr_put_str_filter(struct DStr *str, const char *s, int (*is_x)(int c));
|
||||||
|
|
||||||
|
int uc_dstr_put_str_sz(struct DStr *str, const char *s, size_t len);
|
||||||
|
|
||||||
|
int uc_dstr_put_str(struct DStr *str, const char *s);
|
||||||
|
|
||||||
|
int uc_dstr_put_char(struct DStr *str, char c);
|
||||||
|
|
||||||
|
void uc_dstr_own(struct DStr *str, char *s);
|
||||||
|
|
||||||
|
char *uc_dstr_steal(struct DStr *str);
|
||||||
|
|
||||||
|
void uc_str_swap(struct DStr *a, struct DStr *b);
|
||||||
|
|
||||||
|
size_t uc_dstr_replace(struct DStr *str, char f, char r);
|
||||||
|
|
||||||
|
void uc_dstr_trim(struct DStr *str);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
+207
@@ -0,0 +1,207 @@
|
|||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "ucore/dstr.h"
|
||||||
|
#include "ucore/utils.h"
|
||||||
|
|
||||||
|
void uc_dstr_init(struct DStr *str)
|
||||||
|
{
|
||||||
|
str->len = 0;
|
||||||
|
str->allocated = 0;
|
||||||
|
str->str = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void uc_dstr_destroy(struct DStr *str)
|
||||||
|
{
|
||||||
|
free(str->str);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t uc_dstr_available(const struct DStr *str)
|
||||||
|
{
|
||||||
|
return str->allocated - str->len;
|
||||||
|
}
|
||||||
|
|
||||||
|
int uc_dstr_ensure(struct DStr *str, size_t len)
|
||||||
|
{
|
||||||
|
if (str->allocated < len) {
|
||||||
|
//allocate a minimum no. of bytes, and one
|
||||||
|
//additional for the common case of nul terminating
|
||||||
|
//the string
|
||||||
|
size_t new_len = str->len + UC_MAX(7U, len) + 1;
|
||||||
|
char *new_str = realloc(str->str, new_len);
|
||||||
|
|
||||||
|
if (new_str == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
str->str = new_str;
|
||||||
|
str->allocated = new_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void uc_dstr_reset(struct DStr *str)
|
||||||
|
{
|
||||||
|
str->len = 0;
|
||||||
|
if (str->str) {
|
||||||
|
str->str[0] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void uc_dstr_terminate(struct DStr *str)
|
||||||
|
{
|
||||||
|
uc_dstr_ensure(str, str->len + 1);
|
||||||
|
str->str[str->len] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *uc_dstr_str(struct DStr *str)
|
||||||
|
{
|
||||||
|
uc_dstr_terminate(str);
|
||||||
|
|
||||||
|
return str->str;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *uc_dstr_put(struct DStr *str, size_t len)
|
||||||
|
{
|
||||||
|
char *start;
|
||||||
|
|
||||||
|
if (uc_dstr_ensure(str, str->len + len) != 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
start = str->str + str->len;
|
||||||
|
str->len += len;
|
||||||
|
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t uc_dstr_put_str_filter(struct DStr *str, const char *s, int (*is_x)(int c))
|
||||||
|
{
|
||||||
|
size_t len = strlen(s);
|
||||||
|
size_t i;
|
||||||
|
size_t idx = 0;
|
||||||
|
|
||||||
|
if (uc_dstr_ensure(str, str->len + len) != 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
if (is_x(s[i])) {
|
||||||
|
str->str[str->len + idx]= s[i];
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
str->len += idx;
|
||||||
|
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
int uc_dstr_put_str_sz(struct DStr *str, const char *s, size_t len)
|
||||||
|
{
|
||||||
|
char *start = uc_dstr_put(str, len);
|
||||||
|
|
||||||
|
if (start == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
memcpy(start, s, len);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int uc_dstr_put_str(struct DStr *str, const char *s)
|
||||||
|
{
|
||||||
|
size_t len = strlen(s);
|
||||||
|
|
||||||
|
return uc_dstr_put_str_sz(str, s ,len);
|
||||||
|
}
|
||||||
|
|
||||||
|
int uc_dstr_put_char(struct DStr *str, char c)
|
||||||
|
{
|
||||||
|
char *start = uc_dstr_put(str, 1);
|
||||||
|
|
||||||
|
if (start == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
*start = c;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void uc_dstr_own(struct DStr *str, char *s)
|
||||||
|
{
|
||||||
|
size_t len = strlen(s);
|
||||||
|
|
||||||
|
if (str->str) {
|
||||||
|
free(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
str->len = len;
|
||||||
|
str->allocated = len + 1;
|
||||||
|
str->str = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *uc_dstr_steal(struct DStr *str)
|
||||||
|
{
|
||||||
|
char *s = str->str;
|
||||||
|
|
||||||
|
uc_dstr_init(str);
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void uc_str_swap(struct DStr *a, struct DStr *b)
|
||||||
|
{
|
||||||
|
struct DStr tmp;
|
||||||
|
|
||||||
|
tmp = *a;
|
||||||
|
*a = *b;
|
||||||
|
*b = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t uc_dstr_replace(struct DStr *str, char f, char r)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
size_t count = 0;
|
||||||
|
|
||||||
|
if (str->len == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < str->len; i++) {
|
||||||
|
if (str->str[i] == f) {
|
||||||
|
str->str[i] = r;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void uc_dstr_trim(struct DStr *str)
|
||||||
|
{
|
||||||
|
size_t spaces;
|
||||||
|
|
||||||
|
uc_dstr_terminate(str);
|
||||||
|
|
||||||
|
//spaces at the start
|
||||||
|
spaces = strspn(str->str, " \t\n\v\f\r");
|
||||||
|
if (spaces > 0) {
|
||||||
|
memmove(str->str, str->str + spaces, str->len - spaces);
|
||||||
|
str->len -= spaces;
|
||||||
|
}
|
||||||
|
|
||||||
|
//spaces at the end
|
||||||
|
if (str->len > 0) {
|
||||||
|
size_t idx = str->len - 1;
|
||||||
|
do {
|
||||||
|
if (strchr(" \t\n\v\f\r", str->str[idx]) == NULL) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
str->len--;
|
||||||
|
idx--;
|
||||||
|
}
|
||||||
|
} while (idx != 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
#include <check.h>
|
||||||
|
#include <ucore/dstr.h>
|
||||||
|
#include <ucore/utils.h>
|
||||||
|
|
||||||
|
|
||||||
|
START_TEST (test_dstr_basics)
|
||||||
|
struct DStr str;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
char *s;
|
||||||
|
|
||||||
|
uc_dstr_init(&str);
|
||||||
|
rc = uc_dstr_put_str(&str, "Hello");
|
||||||
|
ck_assert_int_eq(rc, 0);
|
||||||
|
ck_assert_int_eq(str.len, 5);
|
||||||
|
ck_assert_str_eq(uc_dstr_str(&str), "Hello");
|
||||||
|
|
||||||
|
rc = uc_dstr_put_char(&str, '!');
|
||||||
|
ck_assert_int_eq(rc, 0);
|
||||||
|
ck_assert_int_eq(str.len, 6);
|
||||||
|
ck_assert_str_eq(uc_dstr_str(&str), "Hello!");
|
||||||
|
|
||||||
|
rc = uc_dstr_put_str_sz(&str, " World 12345678", 13);
|
||||||
|
ck_assert_int_eq(rc, 0);
|
||||||
|
ck_assert_int_eq(str.len, 19);
|
||||||
|
ck_assert_str_eq(uc_dstr_str(&str), "Hello! World 123456");
|
||||||
|
|
||||||
|
uc_dstr_reset(&str);
|
||||||
|
ck_assert_int_eq(str.len, 0);
|
||||||
|
ck_assert_str_eq(uc_dstr_str(&str), "");
|
||||||
|
|
||||||
|
uc_dstr_reset(&str);
|
||||||
|
ck_assert_int_ge(uc_dstr_available(&str), 0);
|
||||||
|
|
||||||
|
s = uc_dstr_put(&str, 2);
|
||||||
|
fail_if(s == NULL);
|
||||||
|
s[0] = '1';
|
||||||
|
s[1] = '2';
|
||||||
|
|
||||||
|
ck_assert_str_eq(uc_dstr_str(&str), "12");
|
||||||
|
|
||||||
|
uc_dstr_destroy(&str);
|
||||||
|
|
||||||
|
//just test that this doesn't crash:
|
||||||
|
uc_dstr_init(&str);
|
||||||
|
uc_dstr_destroy(&str);
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
START_TEST (test_dstr_own_steal)
|
||||||
|
struct DStr str;
|
||||||
|
char *s;
|
||||||
|
|
||||||
|
uc_dstr_init(&str);
|
||||||
|
uc_dstr_own(&str, strdup("Hello !"));
|
||||||
|
ck_assert_int_eq(str.len, 7);
|
||||||
|
ck_assert_str_eq(uc_dstr_str(&str), "Hello !");
|
||||||
|
|
||||||
|
s = uc_dstr_steal(&str);
|
||||||
|
ck_assert_str_eq(s, "Hello !");
|
||||||
|
ck_assert_int_eq(str.len, 0);
|
||||||
|
ck_assert_str_eq(uc_dstr_str(&str), "");
|
||||||
|
|
||||||
|
free(s);
|
||||||
|
uc_dstr_destroy(&str);
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
START_TEST (test_dstr_trim)
|
||||||
|
struct DStr str;
|
||||||
|
|
||||||
|
uc_dstr_init(&str);
|
||||||
|
uc_dstr_put_str(&str, " \t \nHello !\r\n");
|
||||||
|
uc_dstr_trim(&str);
|
||||||
|
ck_assert_str_eq(uc_dstr_str(&str), "Hello !");
|
||||||
|
|
||||||
|
uc_dstr_destroy(&str);
|
||||||
|
uc_dstr_init(&str);
|
||||||
|
|
||||||
|
uc_dstr_put_str(&str, " ");
|
||||||
|
|
||||||
|
uc_dstr_trim(&str);
|
||||||
|
ck_assert_str_eq(uc_dstr_str(&str), "");
|
||||||
|
|
||||||
|
uc_dstr_destroy(&str);
|
||||||
|
uc_dstr_init(&str);
|
||||||
|
|
||||||
|
uc_dstr_trim(&str);
|
||||||
|
ck_assert_str_eq(uc_dstr_str(&str), "");
|
||||||
|
|
||||||
|
uc_dstr_destroy(&str);
|
||||||
|
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
START_TEST (test_dstr_replace)
|
||||||
|
struct DStr str;
|
||||||
|
size_t rc;
|
||||||
|
|
||||||
|
uc_dstr_init(&str);
|
||||||
|
uc_dstr_put_str(&str, "-1-2-3-4-5-");
|
||||||
|
rc = uc_dstr_replace(&str, '-',' ');
|
||||||
|
ck_assert_int_eq(rc, 6);
|
||||||
|
|
||||||
|
ck_assert_str_eq(uc_dstr_str(&str), " 1 2 3 4 5 ");
|
||||||
|
rc = uc_dstr_replace(&str, 'X','2');
|
||||||
|
ck_assert_int_eq(rc, 0);
|
||||||
|
ck_assert_str_eq(uc_dstr_str(&str), " 1 2 3 4 5 ");
|
||||||
|
|
||||||
|
uc_dstr_destroy(&str);
|
||||||
|
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
Suite *dstr_suite(void)
|
||||||
|
{
|
||||||
|
Suite *s = suite_create("dstr");
|
||||||
|
TCase *tc = tcase_create("dstr tests");
|
||||||
|
tcase_add_test(tc, test_dstr_basics);
|
||||||
|
tcase_add_test(tc, test_dstr_own_steal);
|
||||||
|
tcase_add_test(tc, test_dstr_trim);
|
||||||
|
tcase_add_test(tc, test_dstr_replace);
|
||||||
|
|
||||||
|
suite_add_tcase(s, tc);
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user