summaryrefslogtreecommitdiff
path: root/app/dynstring
diff options
context:
space:
mode:
Diffstat (limited to 'app/dynstring')
-rw-r--r--app/dynstring/CMakeLists.txt11
-rw-r--r--app/dynstring/dynstring.c457
-rw-r--r--app/dynstring/dynstring.h40
-rw-r--r--app/dynstring/unittest/CMakeLists.txt8
-rw-r--r--app/dynstring/unittest/DynStringTest.c108
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