summaryrefslogtreecommitdiff
path: root/app/dynstring/dynstring.c
diff options
context:
space:
mode:
Diffstat (limited to 'app/dynstring/dynstring.c')
-rw-r--r--app/dynstring/dynstring.c457
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;
+}