diff options
author | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2017-04-29 12:11:35 +0200 |
---|---|---|
committer | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2017-04-29 12:11:35 +0200 |
commit | dcef13a23c06398c0c9b3145c08b28d1110bc169 (patch) | |
tree | df8d3227b3d71fdd356b3c1e69c62533d1759f72 /app/dynstring/dynstring.c | |
parent | 3e11d9c0e6173fce8a482efed8d7f965e8bd6dfa (diff) | |
parent | 16e9630b79f0a7a90c6cedb6781175bb8b337dc1 (diff) |
Merge tag 'upstream/4.3.0'
Upstream version 4.3.0
Diffstat (limited to 'app/dynstring/dynstring.c')
-rw-r--r-- | app/dynstring/dynstring.c | 457 |
1 files changed, 457 insertions, 0 deletions
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; +} |