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 | 
