rsys

Basic data structures and low-level features
git clone git://git.meso-star.fr/rsys.git
Log | Files | Refs | README | LICENSE

commit 7f3a85372fd15944e178ffa18fb2319e530cc123
parent cf8953e8fd5933255a91560b2b4cad6431a05fba
Author: vaplv <vaplv@free.fr>
Date:   Sun, 23 Feb 2014 14:04:55 +0100

Add and test the string data structure

Diffstat:
Mcmake/CMakeLists.txt | 7+++++--
Asrc/str.c | 164+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/str.h | 154+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_str.c | 124+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 447 insertions(+), 2 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -55,7 +55,8 @@ set(RSYS_FILES_SRC library.c mem_allocator.c pthread/pthread_condition.c - pthread/pthread_mutex.c) + pthread/pthread_mutex.c + str.c) set(RSYS_FILES_INC_COMMON clock_time.h dynamic_array.h @@ -68,7 +69,8 @@ set(RSYS_FILES_INC_COMMON mutex.h ref_count.h rsys.h - signal.h) + signal.h + str.h) set(RSYS_FILES_INC_EDIT ${RSYS_FILES_INC_COMMON} rsys_version.h.in) set(RSYS_FILES_INC_INSTALL ${RSYS_FILES_INC_COMMON} rsys_version.h) @@ -109,6 +111,7 @@ new_test(test_list rsys) new_test(test_mem_allocator rsys) new_test(test_ref) new_test(test_signal rsys) +new_test(test_str rsys) new_test(test_time rsys) add_library(test_lib SHARED ${CMAKE_CURRENT_SOURCE_DIR}/test_library.c) diff --git a/src/str.c b/src/str.c @@ -0,0 +1,164 @@ +#include "str.h" +#include <string.h> + +#define IS_MEMORY_OVERLAPPED__( A, SzA, B, SzB ) \ + (((uintptr_t)(A) >= (uintptr_t)(B) && \ + (uintptr_t)(A) < ((uintptr_t)(B) + (SzB))) || \ + (((uintptr_t)(A) + (SzA)) >= (uintptr_t)(B) && \ + ((uintptr_t)(A) + (SzA)) < ((uintptr_t)(B) + (SzB)))) + +/******************************************************************************* + * helper function + ******************************************************************************/ +static int +ensure_allocated(struct str* str, const size_t len, const char keep_old) +{ + char* buf = NULL; + const size_t alloc_granularity = 32; + size_t mod = 0; + size_t new_len = 0; + size_t new_size = 0; + ASSERT( str ); + + if(len * sizeof(char) <= str->allocated) + return 0; + + mod = len % alloc_granularity; + new_len = !mod ? len : len - mod + alloc_granularity; + new_size = new_len * sizeof(char); + buf = MEM_ALLOC(str->allocator, new_size); + if( !buf ) + return -1; + + if(keep_old) { + strncpy( buf, str->cstr, new_len - 1); + buf[new_len - 1] = '\0'; + } + + str->allocated = new_len * sizeof(char); + if(str->cstr != str->buffer) + MEM_FREE(str->allocator, str->cstr); + + str->cstr = buf; + return 0; +} + +/******************************************************************************* + * Exported functions + ******************************************************************************/ +int +str_set(struct str* str, const char* cstr) +{ + size_t cstr_len = 0; + int res = 0; + ASSERT(str && cstr); + + cstr_len = strlen(cstr); + res = ensure_allocated(str, cstr_len + 1, 0); + if( res ) return res; + strncpy(str->cstr, cstr, cstr_len + 1); + str->len = cstr_len; + return 0; +} + +int +str_insert(struct str* str, const size_t i, const char* cstr) +{ + size_t cstr_len = 0; + ASSERT(str); + + if(i > str->len) + return -1 ; + + cstr_len = strlen(cstr); + ASSERT(!IS_MEMORY_OVERLAPPED__ + (str->cstr, str->allocated, cstr, (cstr_len + 1) * sizeof(char))); + + if(i == str->len) { + return str_append(str, cstr); + } else { + const int res = ensure_allocated(str, cstr_len + str->len + 1, 1); + if( res ) + return res; + memmove + (str->cstr + i + cstr_len, + str->cstr + i, + (str->len - i) * sizeof(char)); + memcpy(str->cstr + i, cstr, cstr_len * sizeof(char)); + str->len = str->len + cstr_len; + str->cstr[str->len] = '\0'; + } + return 0; +} + +int +str_insert_char(struct str* str, const size_t i, const char ch) +{ + if(i > str->len) + return -1; + + if(i == str->len) { + return str_append_char(str, ch); + } else if(ch == '\0') { + str->cstr[i] = ch; + str->len = i; + } else { + const int res = ensure_allocated(str, str->len + 2, 1); + if( res ) + return res; + memmove + (str->cstr + i + 1, + str->cstr + i, + (str->len - i) * sizeof(char)); + str->cstr[i] = ch; + ++str->len; + str->cstr[str->len] = '\0'; + } + return 0; +} + +int +str_append(struct str* str, const char* cstr) +{ + size_t cstr_len = 0; + int res = 0; + ASSERT(str && cstr); + + cstr_len = strlen(cstr); + ASSERT(!IS_MEMORY_OVERLAPPED__ + (str->cstr, str->allocated, cstr, (cstr_len + 1) * sizeof(char))); + + res = ensure_allocated(str, cstr_len + str->len + 1, 1); + if(res) + return res; + + memcpy(str->cstr + str->len, cstr, cstr_len * sizeof(char)); + str->len += cstr_len; + str->cstr[str->len] = '\0'; + return 0; +} + +int +str_append_char(struct str* str, const char ch) +{ + int res = 0; + ASSERT( str ); + + if(ch == '\0') + return 0; + + res = ensure_allocated(str, str->len + 2, 1); + if(res) return res; + + str->cstr[str->len] = ch; + ++str->len; + str->cstr[str->len] = '\0'; + return 0; +} + +int +str_reserve(struct str* str, const size_t capacity) +{ + return ensure_allocated(str, capacity / sizeof(char), 1); +} + diff --git a/src/str.h b/src/str.h @@ -0,0 +1,154 @@ +#ifndef STR_H +#define STR_H + +#include "rsys.h" +#include "mem_allocator.h" + +struct str { + /* Internal data. Should not be publicly accessed */ + struct mem_allocator* allocator; + size_t allocated; /* <=> string capacity */ + size_t len; + char* cstr; + char buffer[16]; /* static buffer. Avoid allocation on small string */ +}; + +static INLINE void +str_init(struct mem_allocator* allocator, struct str* str) +{ + ASSERT(str); + str->allocator = allocator ? allocator : &mem_default_allocator; + str->allocated = sizeof(str->buffer); + str->len = 0; + str->cstr = str->buffer; + str->buffer[0] = '\0'; +} + +static INLINE void +str_release(struct str* str) +{ + ASSERT(str); + if(str->cstr != str->buffer) + MEM_FREE(str->allocator, str->cstr); + str->cstr = NULL; +} + +static INLINE size_t +str_len(struct str* str) +{ + ASSERT(str); + return str->len; +} + +static INLINE void +str_clear(struct str* str) +{ + ASSERT(str); + str->len = 0; + str->cstr[0] = '\0'; +} + +static INLINE const char* +str_get(struct str* str) +{ + return str->cstr; +} + +static INLINE const char* +str_cget(const struct str* str) +{ + return str->cstr; +} + +#ifdef __cplusplus +extern "C" { +#endif + +RSYS_API int /* return 0 on success != 0 otherwise */ +str_set + (struct str* str, + const char* cstr); + +RSYS_API int /* return 0 on success != 0 otherwise */ +str_insert + (struct str* str, + const size_t i, + const char* cstr); + +RSYS_API int /* return 0 on success != 0 otherwise */ +str_insert_char + (struct str* str, + const size_t i, + const char ch); + +RSYS_API int /* return 0 on success != 0 otherwise */ +str_append + (struct str* str, + const char* cstr); + +RSYS_API int /* return 0 on success != 0 otherwise */ +str_append_char + (struct str* str, + const char ch); + +RSYS_API int /* return 0 on success != 0 otherwise */ +str_reserve + (struct str* str, + const size_t capacity); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +static INLINE int /* return 0 on success != 0 otherwise */ +str_copy(struct str* dst, const struct str* src) +{ + ASSERT(dst && src); + if(dst == src) + return 0; + return str_set(dst, str_cget(src)); +} + +static INLINE int /* return 0 on success != 0 otherwise */ +str_copy_and_clear( struct str* dst, struct str* src ) +{ + int res = 0; + ASSERT(dst && src); + if(dst == src) { + str_clear(dst); + return 0; + } + + if(src->cstr != src->buffer && src->allocator == dst->allocator) { + /* Give the ownership of src->cstr to dst */ + if(dst->cstr != dst->buffer ) + MEM_FREE(dst->allocator, dst->cstr); + dst->cstr = src->cstr; + dst->allocated = src->allocated; + dst->len = src->len; + /* Reset the src to its initial state */ + str_init(src->allocator, src); + } else { + res = str_copy(dst, src); + if(!res) + str_clear(src); + } + return res; +} + +static INLINE int /* return 0 on success != 0 otherwise */ +str_copy_and_release(struct str* dst, struct str* src) +{ + int res = 0; + ASSERT( dst && src ); + if(dst == src) { + str_release(dst); + } else { + res = str_copy_and_clear(dst, src); + if( !res ) + str_release(src); + } + return res; +} + +#endif /* STR_H */ diff --git a/src/test_str.c b/src/test_str.c @@ -0,0 +1,124 @@ +#include "mem_allocator.h" +#include "str.h" +#include <string.h> + +int +main(int argc, char** argv) +{ + struct mem_allocator allocator_proxy; + struct str str, str2; + (void)argc, (void)argv; + + mem_init_proxy_allocator(&allocator_proxy, &mem_default_allocator); + + str_init(&allocator_proxy, &str); + CHECK(strcmp(str_get(&str), ""), 0); + CHECK(str_len(&str), 0); + + CHECK(str_set(&str, "Foo"), 0); + CHECK(strcmp(str_get(&str), "Foo"), 0); + CHECK(str_len(&str), strlen("Foo")); + + str_clear(&str); + CHECK(strcmp(str_get(&str), ""), 0); + CHECK(str_len(&str), 0); + + CHECK(str_set(&str, __FILE__), 0); + CHECK(strcmp(str_get(&str), __FILE__), 0); + + str_release(&str); + str_init(&allocator_proxy, &str); + CHECK(strcmp(str_get(&str), ""), 0); + CHECK(str_len(&str), 0); + + CHECK(str_set(&str, "Hello world!"), 0); + CHECK(strcmp(str_get(&str), "Hello world!"), 0); + NCHECK(str_insert(&str, 13, " insert"), 0); + CHECK(str_insert(&str, 12, " insert"), 0); + CHECK(strcmp(str_get(&str), "Hello world! insert"), 0); + + CHECK(str_insert(&str, 6, "abcdefgh "), 0); + CHECK(strcmp(str_get(&str), "Hello abcdefgh world! insert"), 0); + CHECK(str_insert(&str, 0, "ABC "), 0); + CHECK(strcmp(str_get(&str), "ABC Hello abcdefgh world! insert"), 0); + CHECK(str_insert(&str, 11, "0123456789ABCDEF"), 0); + CHECK(strcmp(str_get(&str), + "ABC Hello a0123456789ABCDEFbcdefgh world! insert"), 0); + CHECK(str_len(&str), + strlen("ABC Hello a0123456789ABCDEFbcdefgh world! insert")); + + CHECK(str_set(&str, "Hello world!"), 0); + CHECK(str_append(&str, " Append"), 0); + CHECK(strcmp(str_get(&str), "Hello world! Append"), 0); + CHECK(str_append(&str, "!"), 0); + CHECK(strcmp(str_get(&str), "Hello world! Append!"), 0); + + NCHECK(str_insert_char(&str, 21, 'a'), 0); + CHECK(strcmp(str_get(&str), "Hello world! Append!"), 0); + CHECK(str_insert_char(&str, 20, 'a'), 0); + CHECK(strcmp(str_get(&str), "Hello world! Append!a"), 0); + CHECK(str_insert_char(&str, 0, '0'), 0); + CHECK(strcmp(str_get(&str), "0Hello world! Append!a"), 0); + CHECK(str_insert_char(&str, 13, 'A'), 0); + CHECK(strcmp(str_get(&str), "0Hello world!A Append!a"), 0); + + CHECK(str_append_char(&str, 'z'), 0); + CHECK(strcmp(str_get(&str), "0Hello world!A Append!az"), 0); + + CHECK(str_reserve(&str, 128), 0); + CHECK(str_reserve(&str, 0), 0); + CHECK(strcmp(str_get(&str), "0Hello world!A Append!az"), 0); + + CHECK(str_insert_char(&str, 13, '\0'), 0); + CHECK(strcmp(str_get(&str), "0Hello world!"), 0); + CHECK(str_len(&str), strlen("0Hello world!")); + CHECK(str_append_char(&str, '\0'), 0); + CHECK(strcmp(str_get(&str), "0Hello world!"), 0); + CHECK(str_len(&str), strlen("0Hello world!")); + + str_init(&allocator_proxy, &str2); + str_copy(&str2, &str); + CHECK(strcmp(str_cget(&str2), "0Hello world!"), 0); + CHECK(strcmp(str_cget(&str), "0Hello world!"), 0); + + str_clear(&str2); + str_copy_and_clear(&str2, &str); + CHECK(strcmp(str_cget(&str2), "0Hello world!"), 0); + CHECK(str_len(&str2), strlen(str_cget(&str2))); + CHECK(str_len(&str), 0); + str_copy_and_release(&str, &str2); + CHECK(strcmp(str_cget(&str), "0Hello world!"), 0); + + str_init(&allocator_proxy, &str2); + str_set(&str2, "ABC Hello a0123456789ABCDEFbcdefgh world! insert"); + str_copy_and_clear(&str, &str2); + CHECK(0, strcmp + (str_cget(&str), "ABC Hello a0123456789ABCDEFbcdefgh world! insert")); + CHECK(str_len(&str), strlen(str_cget(&str))); + + str_set(&str2, "Hello world!"); + CHECK(strcmp(str_cget(&str2), "Hello world!"), 0); + str_clear(&str2); + str_copy_and_clear(&str, &str2); + CHECK(str_len(&str), 0); + CHECK(str_len(&str2), 0); + CHECK(strlen(str_cget(&str)), 0); + + str_copy_and_release(&str2, &str2); + + str_set(&str, "Hello World!"); + str_copy_and_clear(&str, &str); + CHECK(str_len(&str), 0); + + str_release(&str); + + if(MEM_ALLOCATED_SIZE(&allocator_proxy)) { + char dump[512]; + MEM_DUMP(&allocator_proxy, dump, sizeof(dump) / sizeof(char)); + fprintf(stderr, "error: %s\n", dump); + FATAL("Memory leaks\n"); + } + mem_shutdown_proxy_allocator(&allocator_proxy); + CHECK(mem_allocated_size(), 0); + return 0; +}