]> git.puffer.fish Git - mirror/frr.git/commitdiff
lib: darr: add new access and str functions
authorChristian Hopps <chopps@labn.net>
Mon, 16 Oct 2023 10:26:34 +0000 (06:26 -0400)
committerChristian Hopps <chopps@labn.net>
Thu, 28 Dec 2023 17:52:57 +0000 (17:52 +0000)
- darr_last(), and darr_strdup_cap().
- strcat, strdup, strlen, strnul equivs.

Signed-off-by: Christian Hopps <chopps@labn.net>
configure.ac
lib/darr.c
lib/darr.h
tests/lib/subdir.am
tests/lib/test_darr.c
tests/lib/test_darr.py [new file with mode: 0644]

index 12cb4b9bb33d6a76834ad80a2b5b55f02dcb6fcb..fc3775857f4d6bae89f06857fb546d32ee62fd5f 100644 (file)
@@ -379,6 +379,7 @@ else
 fi
 AC_C_FLAG([-Wno-unused-parameter])
 AC_C_FLAG([-Wno-missing-field-initializers])
+AC_C_FLAG([-Wno-microsoft-anon-tag])
 
 AC_C_FLAG([-Wc++-compat], [], [CXX_COMPAT_CFLAGS="-Wc++-compat"])
 AC_SUBST([CXX_COMPAT_CFLAGS])
index 282e0dc5dc2738409b4754a16a8bafa8cea18cbc..9ec3cb63b07aa97a1ee18a163054d7d08ec38915 100644 (file)
@@ -52,6 +52,44 @@ static size_t darr_size(uint count, size_t esize)
        return count * esize + sizeof(struct darr_metadata);
 }
 
+char *__darr_in_vsprintf(char **sp, bool concat, const char *fmt, va_list ap)
+{
+       size_t inlen = concat ? darr_strlen(*sp) : 0;
+       size_t capcount = strlen(fmt) + MIN(inlen + 64, 128);
+       ssize_t len;
+
+       darr_ensure_cap(*sp, capcount);
+
+       if (!concat)
+               darr_reset(*sp);
+
+       /* code below counts on having a NUL terminated string */
+       if (darr_len(*sp) == 0)
+               *darr_append(*sp) = 0;
+again:
+       len = vsnprintf(darr_last(*sp), darr_avail(*sp), fmt, ap);
+       if (len < 0)
+               darr_in_strcat(*sp, fmt);
+       else if ((size_t)len < darr_avail(*sp))
+               _darr_len(*sp) += len;
+       else {
+               darr_ensure_cap(*sp, darr_len(*sp) + (size_t)len);
+               goto again;
+       }
+       return *sp;
+}
+
+char *__darr_in_sprintf(char **sp, bool concat, const char *fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       (void)__darr_in_vsprintf(sp, concat, fmt, ap);
+       va_end(ap);
+       return *sp;
+}
+
+
 void *__darr_resize(void *a, uint count, size_t esize)
 {
        uint ncount = darr_next_count(count, esize);
@@ -69,7 +107,6 @@ void *__darr_resize(void *a, uint count, size_t esize)
 
 void *__darr_insert_n(void *a, uint at, uint count, size_t esize, bool zero)
 {
-
        struct darr_metadata *dm;
        uint olen, nlen;
 
index d78d97d5f3640f14a14039b34e303b4a10e1af4f..81443bfc83d1b5783fc504e7131618da3e1b0159 100644 (file)
  *  - darr_append_n
  *  - darr_append_nz
  *  - darr_cap
+ *  - darr_ensure_avail
  *  - darr_ensure_cap
  *  - darr_ensure_i
- *  - darr_foreach_i
- *  - darr_foreach_p
  *  - darr_free
  *  - darr_insert
  *  - darr_insertz
  *  - darr_insert_n
  *  - darr_insert_nz
+ *  - darr_last
+ *  - darr_lasti
  *  - darr_len
  *  - darr_maxi
  *  - darr_pop
  *  - darr_remove_n
  *  - darr_reset
  *  - darr_setlen
+ *
+ * Iteration
+ * ---------
+ *  - darr_foreach_i
+ *  - darr_foreach_p
+ *
+ * String Utilities
+ * ----------------
+ *  - darr_in_strcat_tail
+ *  - darr_in_strcatf, darr_in_vstrcatf
+ *  - darr_in_strdup
+ *  - darr_in_strdup_cap
+ *  - darr_in_sprintf, darr_in_vsprintf
+ *  - darr_set_strlen
+ *  - darr_strdup
+ *  - darr_strdup_cap
+ *  - darr_strlen
+ *  - darr_strnul
+ *  - darr_sprintf, darr_vsprintf
  */
 /*
  * A few assured items
  *
  * - DAs will never have capacity 0 unless they are NULL pointers.
  */
+
+/*
+ * NOTE: valgrind by default enables a "length64" heuristic (among others) which
+ * identifies "interior-pointer" 8 bytes forward of a "start-pointer" as a
+ * "start-pointer". This should cause what normally would be "possibly-lost"
+ * errors to instead be definite for dynamic arrays. This is b/c the header is 8 bytes
+ */
+
 #include <zebra.h>
 #include "memory.h"
 
 DECLARE_MTYPE(DARR);
 
 struct darr_metadata {
-       uint len;
-       uint cap;
+       uint32_t len;
+       uint32_t cap;
 };
 void *__darr_insert_n(void *a, uint at, uint count, size_t esize, bool zero);
+char *__darr_in_sprintf(char **sp, bool concat, const char *fmt, ...)
+       PRINTFRR(3, 4);
+char *__darr_in_vsprintf(char **sp, bool concat, const char *fmt, va_list ap)
+       PRINTFRR(3, 0);
 void *__darr_resize(void *a, uint count, size_t esize);
 
+
 #define _darr_esize(A)    sizeof((A)[0])
 #define darr_esize(A)     sizeof((A)[0])
 #define _darr_len(A)      _darr_meta(A)->len
@@ -55,14 +88,15 @@ void *__darr_resize(void *a, uint count, size_t esize);
 /* Get the current capacity of the array */
 #define darr_cap(A) (((A) == NULL) ? 0 : _darr_meta(A)->cap)
 
+/* Get the current available expansion space */
+#define darr_avail(A) (((A) == NULL) ? 0 : (darr_cap(A) - darr_len(A)))
+
 /* Get the largest possible index one can `darr_ensure_i` w/o resizing */
 #define darr_maxi(A) ((int)darr_cap(A) - 1)
 
 /**
- * Get the current length of the array.
- *
- * As long as `A` is non-NULL, this macro may be used as an L-value to modify
- * the length of the array.
+ * darr_len() - Get the current length of the array as a unsigned int.
+ * darr_ilen() - Get the current length of the array as an int.
  *
  * Args:
  *     A: The dynamic array, can be NULL.
@@ -70,7 +104,19 @@ void *__darr_resize(void *a, uint count, size_t esize);
  * Return:
  *      The current length of the array.
  */
-#define darr_len(A) (((A) == NULL) ? 0 : _darr_meta(A)->len)
+#define darr_len(A)  (((A) == NULL) ? 0 : _darr_meta(A)->len)
+#define darr_ilen(A) (((A) == NULL) ? 0 : (ssize_t)_darr_meta(A)->len)
+
+/**
+ * darr_lasti() - Get the last element's index.
+ *
+ * Args:
+ *     A: The dynamic array, can be NULL.
+ *
+ * Return:
+ *      The current last element index, or -1 for none.
+ */
+#define darr_lasti(A) (darr_ilen(A) - 1)
 
 /**
  * Set the current length of the array `A` to 0.
@@ -99,11 +145,41 @@ void *__darr_resize(void *a, uint count, size_t esize);
                assert((A) || !(L));                                           \
                if ((A)) {                                                     \
                        /* have to cast to avoid compiler warning for "0" */   \
-                       assert((long long)darr_cap(A) >= (L));                 \
+                       assert((long long)darr_cap(A) >= (long long)(L));      \
                        _darr_len(A) = (L);                                    \
                }                                                              \
        } while (0)
 
+/**
+ * Set the string length of the array `S` to `L`, and NUL
+ * terminate the string at L. The dynamic array length will be `L` + 1.
+ *
+ * Thus after calling:
+ *
+ *    darr_len(S) == L + 1
+ *    darr_strlen(S) == L
+ *    S[L] == 0
+ *
+ * This function does *not* guarantee the `L` + 1 memory is allocated to
+ * the array, use `darr_ensure` or `*_cap` functions for that.
+ *
+ * Args:
+ *     S: The dynamic array, cannot be NULL.
+ *      L: The new str length of the array, will set
+ *
+ * Return:
+ *      A pointer to the end of S (i.e., pointing to the NUL byte).
+ */
+#define darr_set_strlen(S, L)                                                  \
+       ({                                                                     \
+               assert((S));                                                   \
+               /* have to cast to avoid compiler warning for "0" */           \
+               assert((long long)darr_cap(S) >= (long long)(L));              \
+               _darr_len(S) = (L) + 1;                                        \
+               *darr_last(S) = 0;                                             \
+               darr_last(S);                                                  \
+       })
+
 /**
  * Free memory allocated for the dynamic array `A`
  *
@@ -120,6 +196,31 @@ void *__darr_resize(void *a, uint count, size_t esize);
                }                                                              \
        } while (0)
 
+/**
+ * Make sure that there is room in the dynamic array `A` to add `C` elements.
+ *
+ * Available space is `darr_cap(a) - darr_len(a)`.
+ *
+ * The value `A` may be changed as a result of this call in which case any
+ * pointers into the previous memory block are no longer valid. The `A` value
+ * is guaranteed not to change if there is sufficient capacity in the array.
+ *
+ * Args:
+ *     A: (IN/OUT) the dynamic array, can be NULL.
+ *     S: Amount of free space to guarantee.
+ *
+ * Return:
+ *      A pointer to the (possibly moved) array.
+ */
+#define darr_ensure_avail(A, S)                                                \
+       ({                                                                     \
+               ssize_t need = (ssize_t)(S) -                                  \
+                              (ssize_t)(darr_cap(A) - darr_len(A));           \
+               if (need > 0)                                                  \
+                       _darr_resize((A), darr_cap(A) + need);                 \
+               (A);                                                           \
+       })
+
 /**
  * Make sure that there is room in the dynamic array `A` for `C` elements.
  *
@@ -129,14 +230,15 @@ void *__darr_resize(void *a, uint count, size_t esize);
  *
  * Args:
  *     A: (IN/OUT) the dynamic array, can be NULL.
- *     I: the index to guarantee memory exists for
+ *     C: Total capacity to guarantee.
  *
  * Return:
  *      A pointer to the (possibly moved) array.
  */
 #define darr_ensure_cap(A, C)                                                  \
        ({                                                                     \
-               if (darr_cap(A) < (C))                                         \
+               /* Cast to avoid warning when C == 0 */                        \
+               if ((ssize_t)darr_cap(A) < (ssize_t)(C))                       \
                        _darr_resize((A), (C));                                \
                (A);                                                           \
        })
@@ -348,6 +450,242 @@ void *__darr_resize(void *a, uint count, size_t esize);
  */
 #define darr_end(A) ((A) + darr_len(A))
 
+/**
+ * darr_last() - Get a pointer to the last element of the array.
+ * darr_strnul() - Get a pointer to the NUL byte of the darr string or NULL.
+ *
+ * Args:
+ *     A: The dynamic array, can be NULL.
+ *
+ * Return:
+ *      A pointer to the last element of the array or NULL if the array is
+ *      empty.
+ */
+#define darr_last(A)                                                           \
+       ({                                                                     \
+               uint __len = darr_len(A);                                      \
+               ((__len > 0) ? &(A)[__len - 1] : NULL);                        \
+       })
+#define darr_strnul(S) darr_last(S)
+
+/**
+ * darr_in_sprintf() - sprintf into D.
+ *
+ * Args:
+ *      D: The destination darr, D's value may be NULL.
+ *      F: The format string
+ *      ...: variable arguments for format string.
+ *
+ * Return:
+ *     The dynamic_array D with the new string content.
+ */
+#define darr_in_sprintf(D, F, ...) __darr_in_sprintf(&(D), 0, F, __VA_ARGS__)
+
+
+/**
+ * darr_in_strcat() - concat a string into a darr string.
+ *
+ * Args:
+ *      D: The destination darr, D's value may be NULL.
+ *      S: The string to concat onto D.
+ *
+ * Return:
+ *     The dynamic_array D with the new string content.
+ */
+#define darr_in_strcat(D, S)                                                   \
+       ({                                                                     \
+               uint __dlen = darr_strlen(D);                                  \
+               uint __slen = strlen(S);                                       \
+               darr_ensure_cap(D, __dlen + __slen + 1);                       \
+               if (darr_len(D) == 0)                                          \
+                       *darr_append(D) = 0;                                   \
+               memcpy(darr_last(D), (S), __slen + 1);                         \
+               _darr_len(D) += __slen;                                        \
+               D;                                                             \
+       })
+
+/**
+ * darr_in_strcatf() - concat a formatted string into a darr string.
+ *
+ * Args:
+ *      D: The destination darr, D's value may be NULL.
+ *      F: The format string to concat onto D after adding arguments.
+ *    ...: The arguments for the format string.
+ * Return:
+ *     The dynamic_array D with the new string content.
+ */
+#define darr_in_strcatf(D, F, ...)                                             \
+       __darr_in_sprintf(&(D), true, (F), __VA_ARGS__)
+
+/**
+ * darr_in_strcat_tail() - copies end of one darr str to another.
+ *
+ * This is a rather specialized function, it takes 2 darr's, a destination and a
+ * source. If the source is not longer than the destination nothing is done.
+ * Otherwise the characters in the source that lie beyond the length of the dest
+ * are added to the dest. No checking is done to make sure the common prefix
+ * matches. For example:
+ *
+ *     D: "/foo"
+ *     S: "/foo/bar"
+ *  -> D: "/foo/bar"
+ *
+ *     perhaps surprising results:
+ *     D: "/foo"
+ *     S: "/zoo/bar"
+ *  -> D: "/foo/bar"
+ *
+ * Args:
+ *      D: The destination darr, D's value may be NULL.
+ *      S: The string to copy the tail from.
+ *
+ * Return:
+ *     The dynamic_array D with the extended string content.
+ */
+#define darr_in_strcat_tail(D, S)                                              \
+       ({                                                                     \
+               int __dsize, __ssize, __extra;                                 \
+                                                                              \
+               if (darr_len(D) == 0)                                          \
+                       *darr_append(D) = 0;                                   \
+               __dsize = darr_ilen(D);                                        \
+               __ssize = darr_ilen(S);                                        \
+               __extra = __ssize - __dsize;                                   \
+               if (__extra > 0) {                                             \
+                       darr_ensure_cap(D, (uint)__ssize);                     \
+                       memcpy(darr_last(D), (S) + __dsize - 1, __extra + 1);  \
+                       _darr_len(D) += __extra;                               \
+               }                                                              \
+               D;                                                             \
+       })
+
+/**
+ * darr_in_strdup_cap() - duplicate the string into a darr reserving capacity.
+ * darr_in_strdup() - duplicate the string into a darr.
+ *
+ * Args:
+ *      D: The destination darr, D's value may be NULL.
+ *      S: The string to duplicate.
+ *      C: The capacity to reserve.
+ *
+ * Return:
+ *     The dynamic_array D with the duplicated string.
+ */
+#define darr_in_strdup_cap(D, S, C)                                            \
+       ({                                                                     \
+               size_t __size = strlen(S) + 1;                                 \
+               darr_reset(D);                                                 \
+               darr_ensure_cap(D, ((size_t)(C) > __size) ? (size_t)(C)        \
+                                                         : __size);           \
+               strlcpy(D, (S), darr_cap(D));                                  \
+               darr_setlen((D), (size_t)__size);                              \
+               D;                                                             \
+       })
+#define darr_in_strdup(D, S) darr_in_strdup_cap(D, S, 1)
+
+/**
+ * darr_in_vsprintf() - vsprintf into D.
+ *
+ * Args:
+ *      D: The destination darr, D's value may be NULL.
+ *      F: The format string
+ *      A: Varargs
+ *
+ * Return:
+ *     The dynamic_array D with the new string content.
+ */
+#define darr_in_vsprintf(D, F, A) __darr_in_vsprintf(&(D), 0, F, A)
+
+/**
+ * darr_in_vstrcatf() - concat a formatted string into a darr string.
+ *
+ * Args:
+ *      D: The destination darr, D's value may be NULL.
+ *      F: The format string to concat onto D after adding arguments.
+ *      A: Varargs
+ *
+ * Return:
+ *     The dynamic_array D with the new string content.
+ */
+#define darr_in_vstrcatf(D, F, A) __darr_in_vsprintf(&(D), true, (F), (A))
+
+/**
+ * darr_sprintf() - sprintf into a new dynamic array.
+ *
+ * Args:
+ *      F: The format string
+ *      ...: variable arguments for format string.
+ *
+ * Return:
+ *     A char * dynamic_array with the new string content.
+ */
+#define darr_sprintf(F, ...)                                                   \
+       ({                                                                     \
+               char *d = NULL;                                                \
+               __darr_in_sprintf(&d, false, F, __VA_ARGS__);                  \
+               d;                                                             \
+       })
+
+/**
+ * darr_strdup_cap() - duplicate the string reserving capacity.
+ * darr_strdup() - duplicate the string into a dynamic array.
+ *
+ * Args:
+ *      S: The string to duplicate.
+ *      C: The capacity to reserve.
+ *
+ * Return:
+ *     The dynamic_array with the duplicated string.
+ */
+#define darr_strdup_cap(S, C)                                                  \
+       ({                                                                     \
+               size_t __size = strlen(S) + 1;                                 \
+               char *__s = NULL;                                              \
+               /* Cast to ssize_t to avoid warning when C == 0 */             \
+               darr_ensure_cap(__s, ((ssize_t)(C) > (ssize_t)__size)          \
+                                            ? (size_t)(C)                     \
+                                            : __size);                        \
+               strlcpy(__s, (S), darr_cap(__s));                              \
+               darr_setlen(__s, (size_t)__size);                              \
+               __s;                                                           \
+       })
+#define darr_strdup(S) darr_strdup_cap(S, 0)
+
+/**
+ * darr_strlen() - get the length of the NUL terminated string in a darr.
+ *
+ * Args:
+ *      S: The string to measure, value may be NULL.
+ *
+ * Return:
+ *     The length of the NUL terminated string in @S
+ */
+#define darr_strlen(S)                                                         \
+       ({                                                                     \
+               uint __size = darr_len(S);                                     \
+               if (__size)                                                    \
+                       __size -= 1;                                           \
+               assert(!(S) || ((char *)(S))[__size] == 0);                    \
+               __size;                                                        \
+       })
+
+/**
+ * darr_vsprintf() - vsprintf into a new dynamic array.
+ *
+ * Args:
+ *      F: The format string
+ *      A: Varargs
+ *
+ * Return:
+ *     The dynamic_array D with the new string content.
+ */
+#define darr_vsprintf(F, A)                                                    \
+       ({                                                                     \
+               char *d = NULL;                                                \
+               darr_in_vsprintf(d, F, A);                                     \
+               d;                                                             \
+       })
+
 /**
  * Iterate over array `A` using a pointer to each element in `P`.
  *
index 6c1be5020181e0c8a32d28d0e39b35f5cc83319a..9247ac33587ac2b91f98cdd1dd860631103ff0f3 100644 (file)
@@ -162,6 +162,7 @@ tests_lib_test_darr_CFLAGS = $(TESTS_CFLAGS)
 tests_lib_test_darr_CPPFLAGS = $(TESTS_CPPFLAGS)
 tests_lib_test_darr_LDADD = $(ALL_TESTS_LDADD)
 tests_lib_test_darr_SOURCES = tests/lib/test_darr.c
+EXTRA_DIST += tests/lib/test_darr.py
 
 
 check_PROGRAMS += tests/lib/test_graph
index 9150aed09dfa081cb17a10f7dd86c0db3742e9e5..78e3a1417c7cbd165a9c2d8f2a4c6856eacb798b 100644 (file)
@@ -14,7 +14,8 @@
  * [x] - darr_append_n
  * [x] - darr_append_nz
  * [x] - darr_cap
- * [-] - darr_ensure_cap
+ * [x] - darr_ensure_avail
+ * [x] - darr_ensure_cap
  * [x] - darr_ensure_i
  * [x] - darr_foreach_i
  * [x] - darr_foreach_p
  * [ ] - darr_insertz
  * [x] - darr_insert_n
  * [x] - darr_insert_nz
+ * [x] - darr_in_sprintf
+ * [x] - darr_in_strcat
+ * [x] - darr_in_strcat_tail
+ * [ ] - darr_in_strcatf
+ * [ ] - darr_in_vstrcatf
+ * [x] - darr_in_strdup
+ * [x] - darr_in_strdup_cap
+ * [-] - darr_in_vsprintf
+ * [x] - darr_lasti
  * [x] - darr_maxi
  * [x] - darr_pop
  * [x] - darr_push
  * [x] - darr_remove_n
  * [x] - darr_reset
  * [x] - darr_setlen
+ * [x] - darr_set_strlen
+ * [x] - darr_sprintf
+ * [x] - darr_strdup
+ * [x] - darr_strdup_cap
+ * [x] - darr_strlen
+ * [x] - darr_strnul
+ * [ ] - darr_vsprintf
  */
 
 static void test_int(void)
@@ -43,6 +60,11 @@ static void test_int(void)
        int *dap;
        uint i;
 
+       assert(darr_len(da1) == 0);
+       assert(darr_lasti(da1) == -1);
+       assert(darr_last(da1) == NULL);
+       assert(darr_end(da1) == NULL);
+
        darr_ensure_i(da1, 0);
        da1[0] = 0;
        assert(darr_len(da1) == 1);
@@ -57,9 +79,11 @@ static void test_int(void)
                da1[i] = i;
 
        assert(darr_len(da1) == 5);
+       assert(darr_lasti(da1) == 4);
        /* minimum non-pow2 array size for long long and smaller */
        assert(darr_cap(da1) == 8);
        assert(!memcmp(da1, a1, sizeof(a1)));
+       assert(&da1[darr_lasti(da1)] == darr_last(da1));
 
        /* reverse the numbers */
        darr_foreach_p (da1, dap)
@@ -185,6 +209,20 @@ static void test_struct(void)
        assert(darr_cap(da1) == 8);
        assert(!memcmp(da1, a1, sizeof(a1)));
 
+       assert(darr_cap(da1) - darr_len(da1) == 3);
+       darr_ensure_avail(da1, 2);
+       assert(darr_cap(da1) == 8);
+       darr_ensure_avail(da1, 3);
+       assert(darr_cap(da1) == 8);
+       darr_ensure_avail(da1, 4);
+       assert(darr_cap(da1) == 16);
+
+       darr_ensure_cap(da1, 16);
+       assert(darr_cap(da1) == 16);
+
+       darr_ensure_cap(da1, 20);
+       assert(darr_cap(da1) == 32);
+
        darr_append_n(da1, 100);
 
        assert(darr_len(da1) == 105);
@@ -272,8 +310,116 @@ static void test_struct(void)
        darr_free(da2);
 }
 
+static void test_string(void)
+{
+       const char *src = "ABCDE";
+       const char *add = "FGHIJ";
+       uint srclen = strlen(src);
+       uint addlen = strlen(add);
+       char *da1 = NULL;
+       char *da2 = NULL;
+
+       assert(darr_strlen(da1) == 0);
+
+       da1 = darr_strdup(src);
+       assert(darr_strlen(da1) == strlen(da1));
+       assert(darr_strlen(da1) == srclen);
+       assert(darr_len(da1) == srclen + 1);
+       assert(darr_ilen(da1) == (int)srclen + 1);
+       assert(darr_cap(da1) >= 8);
+       assert(darr_last(da1) == darr_strnul(da1));
+       assert(darr_strnul(da1) == da1 + darr_strlen(da1));
+
+       da2 = da1;
+       darr_in_strdup(da1, src);
+       assert(da1 == da2);
+       assert(darr_strlen(da1) == strlen(da1));
+       assert(darr_strlen(da1) == srclen);
+       assert(darr_len(da1) == srclen + 1);
+       darr_free(da1);
+       assert(da1 == NULL);
+
+       da1 = darr_strdup_cap(src, 128);
+       assert(darr_strlen(da1) == srclen);
+       assert(darr_cap(da1) >= 128);
+
+       da2 = da1;
+       darr_in_strdup_cap(da1, src, 1024);
+       assert(da1 != da2);
+       assert(darr_strlen(da1) == srclen);
+       assert(darr_cap(da1) >= 1024);
+       darr_free(da1);
+       da2 = NULL;
+
+       da1 = darr_strdup_cap(add, 2);
+       assert(darr_strlen(da1) == addlen);
+       assert(darr_cap(da1) >= 8);
+
+       darr_in_strdup(da1, "ab");
+       darr_in_strcat(da1, "/");
+       darr_in_strcat(da1, "foo");
+       assert(!strcmp("ab/foo", da1));
+       darr_free(da1);
+
+       da1 = darr_in_strcat(da1, "ab");
+       darr_in_strcat(da1, "/");
+       darr_in_strcat(da1, "foo");
+       assert(!strcmp("ab/foo", da1));
+
+       darr_set_strlen(da1, 5);
+       assert(!strcmp("ab/fo", da1));
+       darr_set_strlen(da1, 1);
+       assert(!strcmp("a", da1));
+
+       darr_in_strdup(da1, "ab");
+       da2 = darr_strdup(add);
+       darr_in_strcat_tail(da1, da2);
+       assert(!strcmp("abHIJ", da1));
+       assert(darr_strlen(da1) == 5);
+       assert(darr_len(da1) == 6);
+       darr_free(da1);
+       darr_free(da2);
+
+       da1 = darr_strdup("abcde");
+       da2 = darr_strdup(add);
+       darr_in_strcat_tail(da1, da2);
+       assert(!strcmp("abcde", da1));
+       assert(darr_strlen(da1) == 5);
+       assert(darr_len(da1) == 6);
+       darr_free(da1);
+       darr_free(da2);
+
+       da1 = darr_sprintf("0123456789: %08X", 0xDEADBEEF);
+       assert(!strcmp(da1, "0123456789: DEADBEEF"));
+       assert(darr_strlen(da1) == 20);
+       assert(darr_cap(da1) == 128);
+       da2 = da1;
+       darr_in_sprintf(da1, "9876543210: %08x", 0x0BADF00D);
+       assert(da1 == da2);
+       assert(!strcmp("9876543210: 0badf00d", da2));
+       darr_free(da1);
+       da2 = NULL;
+
+       da1 = NULL;
+       darr_in_sprintf(da1, "0123456789: %08X", 0xDEADBEEF);
+       assert(!strcmp(da1, "0123456789: DEADBEEF"));
+       assert(darr_strlen(da1) == 20);
+       assert(darr_cap(da1) == 128);
+       darr_free(da1);
+
+       da1 = darr_sprintf("0123456789: %08x", 0xDEADBEEF);
+       darr_in_strcatf(da1, " 9876543210: %08x", 0x0BADF00D);
+       assert(!strcmp("0123456789: deadbeef 9876543210: 0badf00d", da1));
+       darr_free(da1);
+
+       da1 = darr_in_strcatf(da1, "0123456789: %08x", 0xDEADBEEF);
+       assert(!strcmp("0123456789: deadbeef", da1));
+       darr_free(da1);
+}
+
 int main(int argc, char **argv)
 {
        test_int();
        test_struct();
+       test_string();
 }
diff --git a/tests/lib/test_darr.py b/tests/lib/test_darr.py
new file mode 100644 (file)
index 0000000..dea3bdf
--- /dev/null
@@ -0,0 +1,8 @@
+import frrtest
+
+
+class TestDarr(frrtest.TestMultiOut):
+    program = "./test_darr"
+
+
+TestDarr.exit_cleanly()