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:
| M | cmake/CMakeLists.txt | | | 7 | +++++-- |
| A | src/str.c | | | 164 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | src/str.h | | | 154 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | src/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;
+}