feat(ustring): 支持负索引访问并给uview结构新增类别。

- insert问题是set/get/at接口异常导致。
- set/get/at对外索引禁止访问'\0'。和len和capacity一样,保持对外接口的一致性。
This commit is contained in:
建峰 2026-05-19 13:50:07 +08:00
parent 6b4ef1d775
commit ad9f642ada
5 changed files with 302 additions and 58 deletions

View File

@ -20,7 +20,7 @@ void demo_ustring(void)
printf("\n@len=%d\n", str->len(str));
str->print(str);
str->append(str, uvc('#'));
str->append(str, uvch('#'));
printf("\n@len=%d\n", str->len(str));
str->print(str);

View File

@ -20,10 +20,20 @@
#define UVIEW_BUF_SIZE 64
// clang-format on
typedef enum{
UV_CSTR,
UV_CHAR,
UV_USTRING,
UV_INT,
UV_LONG,
UV_DOBULE
}uv_type;
typedef struct _uview
{
const char *str;
size_t len;
uv_type type;
} uview_t;
struct _ustring
@ -38,9 +48,9 @@ struct _ustring
// -------------------- public --------------------
// char operations
bool (*set)(struct _ustring *self, size_t index, const char c); // O(1)
bool (*get)(struct _ustring *self, size_t index, char *c); // O(1)
const char* (*at)(struct _ustring *self, size_t index); // O(1)
bool (*set)(struct _ustring *self, ssize_t index, const char* ch); // O(1)
bool (*get)(struct _ustring *self, ssize_t index, char *ch); // O(1)
const char* (*at)(struct _ustring *self, ssize_t index); // O(1)
// base
bool (*reserve)(struct _ustring *self, size_t capacity);
@ -61,31 +71,31 @@ struct _ustring
size_t (*count)(struct _ustring *self, const void *obj); // O(nlogn) if sorted; O(n) if not sorted
// -------------------- uview --------------------
// append and remove
bool (*append)(struct _ustring *self, uview_t v);
// bool (*pop)(struct _ustring *self);
// append
bool (*append)(struct _ustring *self, uview_t newstr);
bool (*insert)(struct _ustring *self, size_t index, uview_t newstr);
// find and ...
ssize_t (*find)(struct _ustring *str, uview_t v);
bool (*replace)(struct _ustring *self, uview_t oldstr, uview_t newstr);
bool (*remove)(struct _ustring *self, uview_t oldstr);
bool (*insert)(struct _ustring *self, size_t index, uview_t v);
// split
struct _ustring *(*substr)(struct _ustring *self, size_t start, size_t end);
bool (*split)(struct _ustring *self, uview_t delim);
bool (*splitlines)(struct _ustring *self);
bool (*join)(struct _ustring *self, uview_t delim);
// substring
ssize_t (*find)(struct _ustring *str, uview_t v);
struct _ustring *(*substr)(struct _ustring *self, size_t start, size_t end);
bool (*replace)(struct _ustring *self, uview_t oldstr, uview_t newstr);
// judge
bool (*isdigit)(struct _ustring *self);
bool (*isalpha)(struct _ustring *self);
bool (*isxdigit)(struct _ustring *self);
bool (*isalnum)(struct _ustring *self);
bool (*isalpha)(struct _ustring *self);
bool (*isspace)(struct _ustring *self);
bool (*islower)(struct _ustring *self);
bool (*isupper)(struct _ustring *self);
bool (*iscntrl)(struct _ustring *self);
bool (*isprint)(struct _ustring *self);
bool (*iscntrl)(struct _ustring *self);
bool (*ispunct)(struct _ustring *self);
bool (*isgraph)(struct _ustring *self);
@ -131,7 +141,7 @@ typedef struct _ustring *ustring_t;
*/
static inline uview_t uv(const char *cstr)
{
return (uview_t){cstr, cstr ? strlen(cstr) : 0};
return (uview_t){cstr, cstr ? strlen(cstr) : 0, UV_CSTR};
}
/**
@ -144,15 +154,15 @@ static inline uview_t uvs(struct _ustring *string)
return (uview_t){NULL, 0};
}
arraylist_t alist = string->_alist;
return (uview_t){alist->_darray->obj, string->len(string)};
return (uview_t){alist->_darray->obj, string->len(string), UV_USTRING};
}
/**
* @brief create a uview from char
*/
static inline uview_t uvc(const char c)
static inline uview_t uvch(const char c)
{
return (uview_t){&c, 1};
return (uview_t){&c, 1, UV_CHAR};
}
/**
@ -162,7 +172,7 @@ static inline uview_t uvi(int i)
{
static char buf[UVIEW_BUF_SIZE + 1] = {0};
snprintf(buf, sizeof(buf), "%d", i);
return uv(buf);
return (uview_t){buf, strlen(buf), UV_INT};
}
/**
@ -172,7 +182,7 @@ static inline uview_t uvl(long l)
{
static char buf[UVIEW_BUF_SIZE + 1] = {0};
snprintf(buf, sizeof(buf), "%ld", l);
return uv(buf);
return (uview_t){buf, strlen(buf), UV_LONG};
}
/**
@ -182,7 +192,7 @@ static inline uview_t uvf(double f)
{
static char buf[UVIEW_BUF_SIZE + 1] = {0};
snprintf(buf, sizeof(buf), "%f", f);
return uv(buf);
return (uview_t){buf, strlen(buf), UV_DOBULE};
}
ustring_t ustring_new(uview_t view);

View File

@ -9,6 +9,7 @@
*
*/
#include "unicstl_internal.h"
#include <ctype.h>
const char *unicstl_version(void)
{
@ -52,8 +53,10 @@ size_t unicstl_new_capacity(size_t capacity)
{ \
const type num1 = *(const type *)obj1; \
const type num2 = *(const type *)obj2; \
if (num1 < num2) return -1; \
if (num1 > num2) return 1; \
if (num1 < num2) \
return -1; \
if (num1 > num2) \
return 1; \
return 0; \
}
@ -99,7 +102,15 @@ int compare_string(const void *obj1, const void *obj2)
return strcmp(*(const char **)obj1, *(const char **)obj2);
}
void uprint_char(const void* obj)
void uprint_char(const void *obj)
{
printf("%c", *(char*)obj);
uint8_t ch = *(uint8_t *)obj;
if (isgraph(ch) || isspace(ch))
{
putchar(ch);
}
else
{
printf("\\x%02x", ch);
}
}

View File

@ -25,7 +25,7 @@ static size_t ustring_capacity(struct _ustring *self)
{
unicstl_assert(self != NULL);
unicstl_assert(self->_alist != NULL);
return self->_alist->capacity(self->_alist);
return self->_alist->capacity(self->_alist) - 1;
}
static bool ustring_empty(struct _ustring *self)
@ -62,8 +62,12 @@ static void ustring_print(struct _ustring *self)
{
unicstl_assert(self != NULL);
unicstl_assert(self->_alist != NULL);
self->_alist->print_obj = self->print_obj;
self->_alist->print(self->_alist);
// self->_alist->print_obj = self->print_obj;
log_debug("ustring_print: len = %d", self->len(self));
for (size_t i = 0; i < self->len(self); i++)
{
self->print_obj(self->at(self, i));
}
}
static bool ustring_reserve(struct _ustring *self, size_t capacity)
@ -90,6 +94,49 @@ static bool ustring_insert(struct _ustring *self, size_t index, uview_t v)
{
unicstl_assert(self != NULL);
unicstl_assert(self->_alist != NULL);
size_t len_cur = self->len(self);
size_t len_sum = len_cur + v.len;
if (!self->resize(self, len_sum))
{
return false;
}
size_t left = len_cur - index;
size_t idx = 0;
char temp = 0;
for (size_t i = 0; i < left; i++)
{
idx = len_cur - 1 - i;
if (!self->get(self, idx, &temp))
{
log_error("get ustring[i] error");
return false;
}
idx = len_sum - 1 - i;
if (!self->set(self, idx, &temp))
{
log_error("get ustring[i-1] error");
return false;
}
}
for (size_t i = 0; i < v.len; i++)
{
if (!self->set(self, index + i, &v.str[i]))
{
log_error("ustring_insert error");
return false;
}
}
if (!self->set(self, len_sum, &null_char))
{
log_error("set '\0' error");
return false;
}
return true;
}
@ -100,12 +147,12 @@ static bool ustring_remove(struct _ustring *self, uview_t oldstr)
return true;
}
static bool ustring_pop(struct _ustring *self)
{
unicstl_assert(self != NULL);
unicstl_assert(self->_alist != NULL);
return self->_alist->pop(self->_alist, NULL);
}
// static bool ustring_pop(struct _ustring *self)
// {
// unicstl_assert(self != NULL);
// unicstl_assert(self->_alist != NULL);
// return self->_alist->pop(self->_alist, NULL);
// }
static bool ustring_append(struct _ustring *self, uview_t v)
{
@ -129,24 +176,56 @@ static bool ustring_append(struct _ustring *self, uview_t v)
return true;
}
static bool ustring_set(struct _ustring *self, size_t index, const char c)
static bool ustring_set(struct _ustring *self, ssize_t index, const char *ch)
{
unicstl_assert(self != NULL);
unicstl_assert(self->_alist != NULL);
return self->_alist->set(self->_alist, index, &c);
if (index > (ssize_t)self->len(self) + 1)
{
log_error("size out of range");
return false;
}
if(index == self->len(self) + 1 && *ch != null_char)
{
log_error("!notice: the last char is not null_char");
}
if(index < 0)
{
index -= 1;
}
return self->_alist->set(self->_alist, index, ch);
}
static bool ustring_get(struct _ustring *self, size_t index, char *c)
static bool ustring_get(struct _ustring *self, ssize_t index, char *ch)
{
unicstl_assert(self != NULL);
unicstl_assert(self->_alist != NULL);
return self->_alist->get(self->_alist, index, &c);
if (index > (ssize_t)self->len(self))
{
log_error("size out of range");
return false;
}
if(index < 0)
{
index -= 1;
}
return self->_alist->get(self->_alist, index, ch);
}
static const char* ustring_at(struct _ustring *self, size_t index)
static const char *ustring_at(struct _ustring *self, ssize_t index)
{
unicstl_assert(self != NULL);
unicstl_assert(self->_alist != NULL);
if (index > (ssize_t)self->len(self))
{
log_error("size out of range");
return false;
}
if(index < 0)
{
index -= 1;
}
return self->_alist->at(self->_alist, index);
}
@ -261,6 +340,21 @@ bool ustring_isdigit(struct _ustring *self)
}
return true;
}
bool ustring_isxdigit(struct _ustring *self)
{
unicstl_assert(self != NULL);
unicstl_assert(self->_alist != NULL);
for (size_t i = 0; i < self->len(self); i++)
{
if (!isxdigit(*(char *)self->_alist->at(self->_alist, i)))
{
return false;
}
}
return true;
}
bool ustring_isalpha(struct _ustring *self)
{
unicstl_assert(self != NULL);
@ -391,8 +485,7 @@ const char *ustring_cstr(struct _ustring *self)
{
unicstl_assert(self != NULL);
unicstl_assert(self->_alist != NULL);
arraylist_t alist = self->_alist;
return (const char *)alist->_darray->obj;
return (const char *)self->at(self, 0);
}
bool ustring_tolower(struct _ustring *self)
@ -428,7 +521,7 @@ bool ustring_reverse(struct _ustring *self)
size_t j = self->len(self) - i - 1;
da->get(da, i, &temp);
da->set(da, i, da->at(da, j));
da->set(da, j, da->at(da, i));
da->set(da, j, &temp);
}
}
@ -569,6 +662,7 @@ static bool ustring_init(struct _ustring *self, uview_t view, bool is_view)
// string
self->isdigit = ustring_isdigit;
self->isxdigit = ustring_isxdigit;
self->isalpha = ustring_isalpha;
self->isalnum = ustring_isalnum;
self->isspace = ustring_isspace;
@ -578,6 +672,8 @@ static bool ustring_init(struct _ustring *self, uview_t view, bool is_view)
self->isprint = ustring_isprint;
self->ispunct = ustring_ispunct;
self->isgraph = ustring_isgraph;
// format
self->cstr = ustring_cstr;
self->tolower = ustring_tolower;
self->toupper = ustring_toupper;
@ -595,7 +691,7 @@ static bool ustring_init(struct _ustring *self, uview_t view, bool is_view)
self->gt = ustring_gt;
self->ge = ustring_ge;
self->cmp = ustring_cmp;
// -------------------- default --------------------
self->print_obj = uprint_char;

View File

@ -14,8 +14,8 @@ void test_ustring_new(void)
{
ustring_t str = ustring_new(uv("hello"));
TEST_ASSERT_EQUAL_INT(5, str->len(str));
TEST_ASSERT_EQUAL_INT(6, str->capacity(str));
TEST_ASSERT_EQUAL_STRING(str->cstr(str), "hello");
TEST_ASSERT_EQUAL_INT(5, str->capacity(str));
TEST_ASSERT_EQUAL_STRING("hello", str->cstr(str));
ustring_free(&str);
}
@ -23,8 +23,8 @@ void test_ustring_new_lazy(void)
{
ustring_t str = ustring_new(uv(""));
TEST_ASSERT_EQUAL_INT(0, str->len(str));
TEST_ASSERT_EQUAL_INT(1, str->capacity(str));
TEST_ASSERT_EQUAL_STRING(str->cstr(str), "");
TEST_ASSERT_EQUAL_INT(0, str->capacity(str));
TEST_ASSERT_EQUAL_STRING("", str->cstr(str));
ustring_free(&str);
}
@ -33,8 +33,8 @@ void test_ustring_new_int(void)
const int num = 1234567890;
ustring_t str = ustring_new(uvi(num));
TEST_ASSERT_EQUAL_INT(10, str->len(str));
TEST_ASSERT_EQUAL_INT(11, str->capacity(str));
TEST_ASSERT_EQUAL_STRING(str->cstr(str), "1234567890");
TEST_ASSERT_EQUAL_INT(10, str->capacity(str));
TEST_ASSERT_EQUAL_STRING("1234567890", str->cstr(str));
ustring_free(&str);
}
@ -43,16 +43,66 @@ void test_ustring_new_float(void)
const double num = 123.456789;
ustring_t str = ustring_new(uvf(num));
TEST_ASSERT_EQUAL_INT(10, str->len(str));
TEST_ASSERT_EQUAL_INT(11, str->capacity(str));
TEST_ASSERT_EQUAL_STRING(str->cstr(str), "123.456789");
TEST_ASSERT_EQUAL_INT(10, str->capacity(str));
TEST_ASSERT_EQUAL_STRING("123.456789", str->cstr(str));
ustring_free(&str);
}
void test_ustring_get(void)
{
ustring_t str = ustring_new(uv("hello"));
TEST_ASSERT_EQUAL_INT(5, str->len(str));
char ch = 0;
TEST_ASSERT_TRUE(str->get(str, 0, &ch));
TEST_ASSERT_EQUAL_CHAR('h', ch);
TEST_ASSERT_TRUE(str->get(str, 5, &ch));
TEST_ASSERT_EQUAL_CHAR('\0', ch);
TEST_ASSERT_TRUE(str->get(str, -1, &ch));
TEST_ASSERT_EQUAL_CHAR('o', ch);
ustring_free(&str);
}
void test_ustring_at(void)
{
ustring_t str = ustring_new(uv("hello"));
TEST_ASSERT_EQUAL_INT(5, str->len(str));
TEST_ASSERT_EQUAL_CHAR('h', *(char *)str->at(str, 0));
TEST_ASSERT_EQUAL_CHAR('\0', *(char *)str->at(str, 5));
TEST_ASSERT_EQUAL_CHAR('o', *(char *)str->at(str, -1));
ustring_free(&str);
}
void test_ustring_set(void)
{
ustring_t str = ustring_new(uv("hello"));
TEST_ASSERT_EQUAL_INT(5, str->len(str));
char ch = '1';
TEST_ASSERT_TRUE(str->set(str, 0, &ch));
TEST_ASSERT_EQUAL_CHAR('1', *(char *)str->at(str, 0));
ch = '5';
TEST_ASSERT_TRUE(str->set(str, 5, &ch));
TEST_ASSERT_EQUAL_CHAR('5', *(char *)str->at(str, 5));
TEST_ASSERT_TRUE(str->set(str, -1, &ch));
TEST_ASSERT_EQUAL_CHAR('5', *(char *)str->at(str, -1));
ustring_free(&str);
}
void test_ustring_tolower(void)
{
ustring_t str = ustring_new(uv("HELLO wolrD"));
str->tolower(str);
TEST_ASSERT_EQUAL_STRING(str->cstr(str), "hello wolrd");
TEST_ASSERT_EQUAL_STRING("hello wolrd", str->cstr(str));
ustring_free(&str);
}
@ -60,7 +110,15 @@ void test_ustring_toupper(void)
{
ustring_t str = ustring_new(uv("hello"));
str->toupper(str);
TEST_ASSERT_EQUAL_STRING(str->cstr(str), "HELLO");
TEST_ASSERT_EQUAL_STRING("HELLO",str->cstr(str));
ustring_free(&str);
}
void test_ustring_reverse(void)
{
ustring_t str = ustring_new(uv("hello"));
str->reverse(str);
TEST_ASSERT_EQUAL_STRING("olleh", str->cstr(str));
ustring_free(&str);
}
@ -97,6 +155,17 @@ void test_ustring_isdigit(void)
ustring_free(&str);
}
void test_ustring_isxdigit(void)
{
ustring_t str = ustring_new(uv("GHgh"));
TEST_ASSERT_FALSE(str->isxdigit(str));
ustring_free(&str);
str = ustring_new(uv("ABCDEF1234567890abcdef"));
TEST_ASSERT_TRUE(str->isxdigit(str));
ustring_free(&str);
}
void test_ustring_isprint(void)
{
ustring_t str = ustring_new(uv("unicstl"));
@ -141,6 +210,39 @@ void test_ustring_isupper(void)
ustring_free(&str);
}
void test_ustring_iscntrl(void)
{
ustring_t str = ustring_new(uv("\x01\x02\x03\t\b\n"));
TEST_ASSERT_TRUE(str->iscntrl(str));
ustring_free(&str);
str = ustring_new(uv("@#$#*"));
TEST_ASSERT_FALSE(str->iscntrl(str));
ustring_free(&str);
}
void test_ustring_ispunct(void)
{
ustring_t str = ustring_new(uv("unicstl"));
TEST_ASSERT_FALSE(str->ispunct(str));
ustring_free(&str);
str = ustring_new(uv(",.;:?!"));
TEST_ASSERT_TRUE(str->ispunct(str));
ustring_free(&str);
}
void test_ustring_isgrach(void)
{
ustring_t str = ustring_new(uv("\1\2\0"));
TEST_ASSERT_FALSE(str->isgraph(str));
ustring_free(&str);
str = ustring_new(uv("unicstl123*&#@"));
TEST_ASSERT_TRUE(str->isgraph(str));
ustring_free(&str);
}
void test_ustring_strip_right(void)
{
ustring_t str = ustring_new(uv(" unicstl\r\n\t"));
@ -177,12 +279,27 @@ void test_ustring_append(void)
ustring_t str2 = ustring_new_fromcstr("ustring");
size_t len = str->len(str);
size_t len2 = str2->len(str2);
TEST_ASSERT_TRUE(str->append(str, uvs(str2)));
TEST_ASSERT_EQUAL_INT(len + len2, str->len(str));
ustring_free(&str);
ustring_free(&str2);
}
void test_ustring_insert(void)
{
ustring_t str = ustring_new_fromcstr("hello wolrd");
ustring_t str2 = ustring_new_fromcstr(" unicstl");
size_t len = str->len(str);
size_t len2 = str2->len(str2);
TEST_ASSERT_TRUE(str->insert(str, 5, uvs(str2)));
TEST_ASSERT_EQUAL_INT(len + len2, str->len(str));
TEST_ASSERT_EQUAL_STRING("hello unicstl wolrd", str->cstr(str));
ustring_free(&str);
ustring_free(&str2);
}
void test_ustring_cmp(void)
{
ustring_t str = ustring_new_fromcstr("unicstl");
@ -212,22 +329,32 @@ void test_ustring(void)
RUN_TEST(test_ustring_new_int);
RUN_TEST(test_ustring_new_float);
RUN_TEST(test_ustring_tolower);
RUN_TEST(test_ustring_toupper);
RUN_TEST(test_ustring_get);
RUN_TEST(test_ustring_at);
RUN_TEST(test_ustring_set);
RUN_TEST(test_ustring_isalpha);
RUN_TEST(test_ustring_isalnum);
RUN_TEST(test_ustring_isdigit);
RUN_TEST(test_ustring_isxdigit);
RUN_TEST(test_ustring_isalnum);
RUN_TEST(test_ustring_isalpha);
RUN_TEST(test_ustring_isprint);
RUN_TEST(test_ustring_isspace);
RUN_TEST(test_ustring_islower);
RUN_TEST(test_ustring_isupper);
RUN_TEST(test_ustring_iscntrl);
RUN_TEST(test_ustring_ispunct);
RUN_TEST(test_ustring_isgrach);
RUN_TEST(test_ustring_tolower);
RUN_TEST(test_ustring_toupper);
RUN_TEST(test_ustring_reverse);
RUN_TEST(test_ustring_strip_right);
RUN_TEST(test_ustring_strip_left);
RUN_TEST(test_ustring_strip);
RUN_TEST(test_ustring_append);
RUN_TEST(test_ustring_insert);
RUN_TEST(test_ustring_cmp);
}