diff options
Diffstat (limited to 'app/dynstring')
-rw-r--r-- | app/dynstring/CMakeLists.txt | 11 | ||||
-rw-r--r-- | app/dynstring/dynstring.c | 457 | ||||
-rw-r--r-- | app/dynstring/dynstring.h | 40 | ||||
-rw-r--r-- | app/dynstring/unittest/CMakeLists.txt | 8 | ||||
-rw-r--r-- | app/dynstring/unittest/DynStringTest.c | 108 |
5 files changed, 624 insertions, 0 deletions
diff --git a/app/dynstring/CMakeLists.txt b/app/dynstring/CMakeLists.txt new file mode 100644 index 0000000..b407805 --- /dev/null +++ b/app/dynstring/CMakeLists.txt @@ -0,0 +1,11 @@ +# compile the dynstring library
+
+set( SOURCES
+ dynstring.c )
+
+add_library(dynstring
+ ${SOURCES})
+
+if(XTRKCAD_TESTING)
+ add_subdirectory( unittest )
+endif(XTRKCAD_TESTING)
\ No newline at end of file diff --git a/app/dynstring/dynstring.c b/app/dynstring/dynstring.c new file mode 100644 index 0000000..6f3159c --- /dev/null +++ b/app/dynstring/dynstring.c @@ -0,0 +1,457 @@ +/** \file dynstring.c +* Library for dynamic string functions +*/ + +#ifdef HAVE_MALLOC_H +#include <malloc.h> +#endif +#include <stdarg.h> +#include <memory.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> + +#include "dynstring.h" + +/** +* Get the current length of the string +* +* \param s IN the dynamic string +* \return the length of the string in bytes +*/ + +size_t DynStringSize(DynString *s) +{ + if (isnas(s)) { + return 0; + } + + return s->size; +} + +/* An initialized empty struct string */ +#define STRINIT() (DynStringMalloc(16)) + +/** +* Allocate memory for a string of the desired length. To optimize memory usage +* a minimum length of 16 bytes is allocated. The allocated string has to be freed +* using the DynStringFree() function. +* +* \param s IN pointer to DynString structure +* \param size IN number of bytes to allocate +* \return pointer to the DynString +*/ + +DynString *DynStringMalloc(DynString *s, size_t size) +{ + if (size < 16) { + size = 16; + } + + s->s = malloc(size); + s->size = 0; + s->b_size = (size_t)(size | STR_FREEABLE ); + return (s); +} + +/** +* Try to compact string memory by reallocating a buffer large enough for the current +* string content. +* +* \param s IN the dynamic string +*/ + +void DynStringRealloc(DynString *s) +{ + char *buf; + + /* Not a string? */ + if (isnas(s)) { + return; + } + + /* Can't realloc? */ + if (!(s->b_size & STR_FREEABLE)) { + return; + } + + /* Don't invoke undefined behaviour with realloc(x, 0) */ + if (!s->size) { + free(s->s); + s->s = malloc(16); + s->b_size = (size_t)(16 | STR_FREEABLE); + } else { + /* Try to compact */ + buf = realloc(s->s, s->size); + + if (buf) { + s->s = buf; + } + s->b_size = (size_t)(s->size | STR_FREEABLE); + } +} + +/** +* Clear the dynamic string. Current content is deleted. The buffer is shrinked to the +* minimum size. +* +* \param s IN the dynamic string +*/ + +void DynStringClear(DynString *s) +{ + /* Not a string? */ + if (isnas(s)) + { + return; + } + + /* Can't realloc? */ + if (!(s->b_size & STR_FREEABLE)) + { + return; + } + s->size = 0; + + DynStringRealloc(s); +} +/** +* Resize the string for a minimum number of bytes. In order to optimize memory usage the +* actually allocated block of memory can be larger than the requested size. +* In case of an error, the string is set to NaS +* +* \param s IN OUT the string +* \param size IN the requested new size +*/ + +void DynStringResize(DynString *s, size_t size) +{ + char *buf; + size_t bsize; + + /* Are we not a string? */ + if (isnas(s)) { + return; + } + + /* Not resizable */ + if (!(s->b_size & STR_FREEABLE)) { + DynString s2; + + /* Don't do anything if we want to shrink */ + if (size <= s->size) { + return; + } + + /* Need to alloc a new string */ + DynStringMalloc(&s2, size); + /* Copy into new string */ + memcpy(s2.s, s->s, s->size); + /* Point to new string */ + s->s = s2.s; + s->b_size = s2.b_size; + return; + } + + /* Too big */ + if (size & STR_FREEABLE) { + DynString nas = NaS; + free(s->s); + *s = nas; + return; + } + + bsize = (size_t)(s->b_size - STR_FREEABLE); + + /* Keep at least 16 bytes */ + if (size < 16) { + size = 16; + } + + /* Nothing to do? */ + if ((4 * size > 3 * bsize) && (size <= bsize)) { + return; + } + + /* Try to double size instead of using a small increment */ + if ((size > bsize) && (size < bsize * 2)) { + size = bsize * 2; + } + + /* Keep at least 16 bytes */ + if (size < 16) { + size = 16; + } + + buf = realloc(s->s, size); + + if (!buf) { + DynString nas = NaS; + /* Failed, go to NaS state */ + free(s->s); + *s = nas; + } else { + s->s = buf; + s->b_size = (size_t)(size | STR_FREEABLE); + } +} + +/** +* Free the previously allocated string. +* +* \param s IN OUT the dynamic string +*/ + +void DynStringFree(DynString *s) +{ + DynString nas = NaS; + + if (s->b_size & STR_FREEABLE) { + free(s->s); + } + + *s = nas; +} + +/** +* Create a newly allocated copy of the passed dynamic string. +* +* \param s IN the dynamic string +* \return the newly allocated dynamic string +*/ + +/* Create a new string as a copy of an old one */ +DynString *DynStringDupStr(DynString *s2, DynString *s) +{ + DynString nas = NaS; + + /* Not a string? */ + if (isnas(s)) { + return NULL; + } + + DynStringMalloc(s2, s->size); + s2->size = s->size; + memcpy(s2->s, s->s, s->size); + return s2; +} + +/** +* Copy the memory from the source string into the dest string. +* +* \param dest IN the destination dynamic string +* \param src IN the source dynamic string +*/ + +void DynStringCpyStr(DynString *dest, DynString *src) +{ + /* Are we no a string */ + if (isnas(src)) { + return; + } + + DynStringResize(dest, src->size); + + if (isnas(dest)) { + return; + } + + dest->size = src->size; + memcpy(dest->s, src->s, src->size); +} + +/** +* Return the content of the dynamic string as a \0 terminated C string. This memory may not be freed by the +* caller. +* +* \param s IN the dynamic string +* \return the C string +*/ + +char *DynStringToCStr(DynString *s) +{ + size_t bsize; + + /* Are we not a string? */ + if (isnas(s)) { + return NULL; + } + + /* Get real buffer size */ + bsize = s->b_size & ~STR_FREEABLE; + + if (s->size == bsize) { + /* Increase buffer size */ + DynStringResize(s, bsize + 1); + + /* Are we no longer a string? */ + if (isnas(s)) { + return NULL; + } + } + + /* Tack a zero on the end */ + s->s[s->size] = 0; + /* Don't update the size */ + /* Can use this buffer as long as you don't append anything else */ + return s->s; +} + +/** +* Concatenate a number of bytes from the source string to the dynamic string. +* +* \param s IN the dynamic string +* \param len IN the number of bytes to append to the dynamic string +* \param str IN the source string +*/ + +void DynStringNCatCStr(DynString *s, size_t len, const char *str) +{ + size_t bsize; + + /* Are we not a string? */ + if (isnas(s)) { + return; + } + + /* Nothing to do? */ + if (!str || !len) { + return; + } + + /* Get real buffer size */ + bsize = s->b_size & ~STR_FREEABLE; + + if (s->size + len >= bsize) { + DynStringResize(s, s->size + len); + + /* Are we no longer a string? */ + if (isnas(s)) { + return; + } + } + + memcpy(&s->s[s->size], str, len); + s->size += len; +} + +/** +* Concatenate a zero-terminated source string to the dynamic string. +* +* \param s IN the dynamic string +* \param str IN the source string +*/ + +void DynStringCatCStr(DynString *s, const char *str) +{ + if (str) { + DynStringNCatCStr(s, strlen(str), str); + } +} + +/** +* Concatenate a dynamic string to another dynamic string. +* +* \param s IN the destination dynamic string +* \param s2 IN the source dynamic string +*/ + +void DynStringCatStr(DynString *s, const DynString *s2) +{ + DynStringNCatCStr(s, s2->size, s2->s); +} + +/** +* Concatenate a variable number zero terminated strings to the dynamic string. The +* list of source strings has to be terminated by a NULL pointer. +* +* \param s IN the dynamic string +* \param ... IN variable number of C strings +*/ + +void DynStringCatCStrs(DynString *s, ...) +{ + const char *str; + va_list v; + + /* Are we not a string? */ + if (isnas(s)) { + return; + } + + va_start(v, s); + + for (str = va_arg(v, const char *); str; str = va_arg(v, const char *)) { + DynStringNCatCStr(s, strlen(str), str); + } + + va_end(v); +} + +/** +* Concatenate a variable number of dynamic string to another dynamic string. +* The list of source strings has to be terminated by a NULL pointer. +* +* \param s IN the destination dynamic string +* \param s2 IN the source dynamic strings +*/ + +void DynStringCatStrs(DynString *s1, ...) +{ + const DynString *s2; + va_list v; + + /* Are we not a string? */ + if (isnas(s1)) { + return; + } + + va_start(v, s1); + + for (s2 = va_arg(v, const DynString *); s2; s2 = va_arg(v, const DynString *)) { + DynStringNCatCStr(s1, s2->size, s2->s); + } + + va_end(v); +} + +/** +* Return a formatted dynamic string. Formatting is performed similar to the printf style +* of functions. +* +* \param s IN the dynamic string +* \param fmt IN format specification +* \param ... IN values +*/ + +void DynStringPrintf(DynString *s, const char *fmt, ...) +{ + va_list v; + size_t len; + DynString nas = NaS; + + ///* Are we not a string? */ + //if (isnas(s)) { + // *s = STRINIT(); + //} + + /* Nothing to do? */ + if (!fmt) { + return; + } + + va_start(v, fmt); + len = vsnprintf(NULL, 0, fmt, v) + 1; + va_end(v); + DynStringResize(s, len); + + /* Are we no longer a string? */ + if (isnas(s)) { + return; + } + + va_start(v, fmt); + vsnprintf(s->s, len, fmt, v); + va_end(v); + s->size = len - 1; +} diff --git a/app/dynstring/dynstring.h b/app/dynstring/dynstring.h new file mode 100644 index 0000000..d6fac51 --- /dev/null +++ b/app/dynstring/dynstring.h @@ -0,0 +1,40 @@ +#ifndef HAVE_DYNSTRING_H
+#define HAVE_DYNSTRING_H
+
+typedef struct DynString DynString;
+struct DynString
+{
+ char *s;
+ size_t size; // length of the string
+ size_t b_size; // length of the buffer containing the string
+};
+
+#define NaS {NULL, 0, 0}
+#define isnas(S) (!(S)->s)
+
+// define highest bit depending on 32 or 64 bit compile
+
+#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
+ #define STR_FREEABLE (1ULL << 63)
+#else
+ #define STR_FREEABLE (1ULL << 31)
+#endif
+
+size_t DynStringSize(DynString * s);
+
+DynString * DynStringMalloc(DynString *s, size_t size);
+void DynStringClear(DynString *s);
+void DynStringRealloc(DynString * s);
+void DynStringResize(DynString * s, size_t size);
+void DynStringFree(DynString * s);
+DynString * DynStringDupStr(DynString *s2, DynString * s);
+void DynStringCpyStr(DynString * dest, DynString * src);
+char * DynStringToCStr(DynString * s);
+void DynStringNCatCStr(DynString * s, size_t len, const char * str);
+void DynStringCatCStr(DynString * s, const char * str);
+void DynStringCatStr(DynString * s, const DynString * s2);
+void DynStringCatCStrs(DynString * s, ...);
+void DynStringCatStrs(DynString * s1, ...);
+void DynStringPrintf(DynString * s, const char * fmt, ...);
+
+#endif // !HAVE_DYNSTRING_H
diff --git a/app/dynstring/unittest/CMakeLists.txt b/app/dynstring/unittest/CMakeLists.txt new file mode 100644 index 0000000..7aad7c1 --- /dev/null +++ b/app/dynstring/unittest/CMakeLists.txt @@ -0,0 +1,8 @@ +# build unit tests for the dynstring library
+
+add_executable(dynstringtest DynStringTest.c)
+target_link_libraries(dynstringtest
+ dynstring
+ ${LIBS})
+
+add_test(DynStringTest dynstringtest)
\ No newline at end of file diff --git a/app/dynstring/unittest/DynStringTest.c b/app/dynstring/unittest/DynStringTest.c new file mode 100644 index 0000000..33d4c9a --- /dev/null +++ b/app/dynstring/unittest/DynStringTest.c @@ -0,0 +1,108 @@ +/** \file DynStringTest.c +* Unit tests for the dynstring library +*/ + +#include <stdarg.h> +#include <stddef.h> +#include <string.h> +#include <stdio.h> +#include <setjmp.h> +#include <cmocka.h> + +#include "../dynstring.h" + +#define TEXT1 "Pastry gummi bears candy canes jelly beans macaroon choc" +#define TEXT2 "olate jelly beans. Marshmallow cupcake tart jelly apple pie sesame snaps ju" +#define TEXT3 "jubes. Tootsie roll dessert gummi bears jelly." + +static void PrintfString(void **state) +{ + DynString string; + + (void)state; + DynStringMalloc(&string, 0); + DynStringPrintf(&string, "%d", 1); + assert_string_equal(DynStringToCStr(&string), "1"); + DynStringFree(&string); +} + +static void CopyString(void **state) +{ + DynString string; + DynString string2; + (void)state; + DynStringMalloc(&string, 0); + DynStringCatCStr(&string, TEXT1); + DynStringDupStr(&string2, &string); + assert_int_equal(DynStringSize(&string2), strlen(TEXT1)); + assert_string_equal(DynStringToCStr(&string2), TEXT1); + DynStringFree(&string2); + DynStringMalloc(&string2, 0); + DynStringCatCStr(&string2, TEXT2); + DynStringCatStr(&string, &string2); + assert_int_equal(DynStringSize(&string), strlen(TEXT1) + strlen(TEXT2)); + assert_string_equal(DynStringToCStr(&string), TEXT1 TEXT2); +} + +static void VarStringCount(void **state) +{ + DynString string; + (void)state; + DynStringMalloc(&string, 0); + DynStringCatCStrs(&string, TEXT1, TEXT2, TEXT3, NULL); + assert_int_equal(DynStringSize(&string), + strlen(TEXT1) + strlen(TEXT2) + strlen(TEXT3)); + assert_string_equal(DynStringToCStr(&string), TEXT1 TEXT2 TEXT3); + DynStringFree(&string); +} + +static void MultipleStrings(void **state) +{ + DynString string; + (void)state; + DynStringMalloc(&string, 0); + DynStringCatCStr(&string, TEXT1); + DynStringCatCStr(&string, TEXT2); + assert_int_equal(DynStringSize(&string), strlen(TEXT1)+strlen(TEXT2)); + assert_string_equal(DynStringToCStr(&string), TEXT1 TEXT2); + DynStringFree(&string); +} + +static void SingleString(void **state) +{ + DynString string; + (void)state; + DynStringMalloc(&string, 0); + DynStringCatCStr(&string, TEXT1); + assert_int_equal(DynStringSize(&string), strlen(TEXT1)); + assert_string_equal(DynStringToCStr(&string), TEXT1); + + DynStringClear(&string); + assert_int_equal(DynStringSize(&string), 0); + + DynStringFree(&string); +} + +static void SimpleInitialization(void **state) +{ + DynString string; + (void)state; + DynStringMalloc(&string, 0); + assert_non_null((void *)&string); + assert_false(isnas(&string)); + assert_int_equal(DynStringSize(&string), 0); + DynStringFree(&string); +} + +int main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(SimpleInitialization), + cmocka_unit_test(SingleString), + cmocka_unit_test(MultipleStrings), + cmocka_unit_test(VarStringCount), + cmocka_unit_test(CopyString), + cmocka_unit_test(PrintfString) + }; + return cmocka_run_group_tests(tests, NULL, NULL); +}
\ No newline at end of file |