aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/plugins/unittest/string_test.c1584
-rw-r--r--src/vppinfra/string.c369
-rw-r--r--src/vppinfra/string.h713
-rw-r--r--test/test_string.py41
4 files changed, 2682 insertions, 25 deletions
diff --git a/src/plugins/unittest/string_test.c b/src/plugins/unittest/string_test.c
index a5caa9094f0..2beee139ec9 100644
--- a/src/plugins/unittest/string_test.c
+++ b/src/plugins/unittest/string_test.c
@@ -16,7 +16,7 @@
#include <vppinfra/string.h>
static int
-test_clib_memset (vlib_main_t * vm, unformat_input_t * input)
+test_memset_s (vlib_main_t * vm, unformat_input_t * input)
{
u8 dst[64];
int i;
@@ -42,7 +42,28 @@ test_clib_memset (vlib_main_t * vm, unformat_input_t * input)
}
static int
-test_memcpy (vlib_main_t * vm, unformat_input_t * input)
+test_clib_memset (vlib_main_t * vm, unformat_input_t * input)
+{
+ u8 dst[64];
+ int i;
+ errno_t err;
+
+ vlib_cli_output (vm, "Test clib_memset...");
+
+ err = clib_memset (dst, 0xfe, ARRAY_LEN (dst));
+
+ if (err != EOK)
+ return -1;
+
+ for (i = 0; i < ARRAY_LEN (dst); i++)
+ if (dst[i] != 0xFE)
+ return -1;
+
+ return 0;
+}
+
+static int
+test_memcpy_s (vlib_main_t * vm, unformat_input_t * input)
{
char src[64], dst[64];
int i;
@@ -86,40 +107,1548 @@ test_memcpy (vlib_main_t * vm, unformat_input_t * input)
return 0;
}
+static int
+test_clib_memcpy (vlib_main_t * vm, unformat_input_t * input)
+{
+ char src[64], dst[64];
+ int i;
+ errno_t err;
+
+ vlib_cli_output (vm, "Test clib_memcpy...");
+
+ for (i = 0; i < ARRAY_LEN (src); i++)
+ src[i] = i + 1;
+
+ /* Typical case */
+ err = clib_memcpy (dst, src, sizeof (src));
+
+ if (err != EOK)
+ return -1;
+
+ /* This better not fail but check anyhow */
+ for (i = 0; i < ARRAY_LEN (dst); i++)
+ if (src[i] != dst[i])
+ return -1;
+ /* verify it against memcpy */
+ memcpy (dst, src, sizeof (src));
+
+ /* This better not fail but check anyhow */
+ for (i = 0; i < ARRAY_LEN (dst); i++)
+ if (src[i] != dst[i])
+ return -1;
+
+ /* Zero length copy */
+ err = clib_memcpy (0, src, 0);
+
+ if (err != EOK)
+ return -1;
+
+ /* OK, seems to work */
+ return 0;
+}
+
+static int
+test_memcmp_s (vlib_main_t * vm, unformat_input_t * input)
+{
+ char src[64], dst[64];
+ errno_t err;
+ int diff = 0;
+
+ vlib_cli_output (vm, "Test memcmp_s...");
+
+ /* Fill array with different values */
+ err = clib_memset (src, 0x1, ARRAY_LEN (src));
+ if (err != EOK)
+ return -1;
+ err = clib_memset (dst, 0x3, ARRAY_LEN (dst));
+ if (err != EOK)
+ return -1;
+
+ /* s1 > s2, > 0 is expected in diff */
+ err = memcmp_s (dst, ARRAY_LEN (dst), src, ARRAY_LEN (src), &diff);
+ if (err != EOK)
+ return -1;
+ if (!(diff > 0))
+ return -1;
+
+ /* s1 < s2, < 0 is expected in diff */
+ err = memcmp_s (src, ARRAY_LEN (src), dst, ARRAY_LEN (dst), &diff);
+ if (err != EOK)
+ return -1;
+ if (!(diff < 0))
+ return -1;
+
+ err = clib_memset (dst, 0x1, ARRAY_LEN (dst));
+ if (err != EOK)
+ return -1;
+
+ /* s1 == s2, 0 is expected in diff */
+ err = memcmp_s (src, ARRAY_LEN (src), dst, ARRAY_LEN (dst), &diff);
+ if (err != EOK)
+ return -1;
+ if (diff != 0)
+ return -1;
+
+ /* Try negative tests */
+ err = memcmp_s (0, 0, 0, 0, 0);
+ if (err != EINVAL)
+ return -1;
+
+ /* Try s2max > s1max */
+ err = memcmp_s (src, ARRAY_LEN (src) - 1, dst, ARRAY_LEN (dst), &diff);
+ if (err != EINVAL)
+ return -1;
+
+ /* OK, seems to work */
+ return 0;
+}
+
+static int
+test_clib_memcmp (vlib_main_t * vm, unformat_input_t * input)
+{
+ char src[64], dst[64];
+ errno_t err;
+ char *s;
+
+ vlib_cli_output (vm, "Test clib_memcmp...");
+
+ /* Fill array with different values */
+ err = clib_memset (src, 0x1, ARRAY_LEN (src));
+ if (err != EOK)
+ return -1;
+ err = clib_memset (dst, 0x3, ARRAY_LEN (dst));
+ if (err != EOK)
+ return -1;
+
+ /* s1 > s2, > 0 is expected in diff */
+ if (!(clib_memcmp (dst, src, ARRAY_LEN (src)) > 0))
+ return -1;
+ /* verify it against memcmp */
+ if (!(memcmp (dst, src, ARRAY_LEN (src)) > 0))
+ return -1;
+
+ /* s1 < s2, < 0 is expected in diff */
+ if (!(clib_memcmp (src, dst, ARRAY_LEN (dst)) < 0))
+ return -1;
+ /* verify it against memcmp */
+ if (!(memcmp (src, dst, ARRAY_LEN (dst)) < 0))
+ return -1;
+
+ err = clib_memset (dst, 0x1, ARRAY_LEN (dst));
+ if (err != EOK)
+ return -1;
+
+ /* s1 == s2, 0 is expected in diff */
+ if (clib_memcmp (src, dst, ARRAY_LEN (dst)) != 0)
+ return -1;
+ /* verify it against memcmp */
+ if (memcmp (src, dst, ARRAY_LEN (dst)) != 0)
+ return -1;
+
+ /* Try negative tests */
+ s = 0;
+ if (clib_memcmp (s, s, 0) != 0)
+ return -1;
+ /* verify it against memcmp */
+ if (memcmp (s, s, 0) != 0)
+ return -1;
+
+ /* OK, seems to work */
+ return 0;
+}
+
+static int
+test_strcmp_s (vlib_main_t * vm, unformat_input_t * input)
+{
+ char s1[] = "Simplicity is the ultimate sophistication";
+ uword s1len = sizeof (s1) - 1; // excluding null
+ errno_t err;
+ int indicator = 0;
+
+ vlib_cli_output (vm, "Test strcmp_s...");
+
+ /* s1 == s2, 0 is expected */
+ err = strcmp_s (s1, s1len, "Simplicity is the ultimate sophistication",
+ &indicator);
+ if (err != EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+
+ /* s1 > s2, > 0 is expected */
+ err = strcmp_s (s1, s1len, "Simplicity is the ultimate", &indicator);
+ if (err != EOK)
+ return -1;
+ if (!(indicator > 0))
+ return -1;
+
+ /* s1 < s2, < 0 is expected */
+ err = strcmp_s (s1, s1len, "Simplicity is the ultimate sophistication!",
+ &indicator);
+ if (err != EOK)
+ return -1;
+ if (!(indicator < 0))
+ return -1;
+
+ /* Try some negative tests */
+
+ /* Null pointers test */
+ err = strcmp_s (0, 0, 0, 0);
+ if (err != EINVAL)
+ return -1;
+
+ /* non-null terminated s1 */
+ s1[s1len] = 0x1;
+ err = strcmp_s (s1, s1len, "Simplicity is the ultimate sophistication",
+ &indicator);
+ if (err != EINVAL)
+ return -1;
+
+ /* OK, seems to work */
+ return 0;
+}
+
+static int
+test_clib_strcmp (vlib_main_t * vm, unformat_input_t * input)
+{
+ char s1[] = "Simplicity is the ultimate sophistication";
+ int indicator;
+ char *s;
+
+ vlib_cli_output (vm, "Test clib_strcmp...");
+
+ /* s1 == s2, 0 is expected */
+ indicator = clib_strcmp (s1, "Simplicity is the ultimate sophistication");
+ if (indicator != 0)
+ return -1;
+ /* verify it against strcmp */
+ indicator = strcmp (s1, "Simplicity is the ultimate sophistication");
+ if (indicator != 0)
+ return -1;
+
+ /* s1 > s2, > 0 is expected */
+ indicator = clib_strcmp (s1, "Simplicity is the ultimate");
+ if (!(indicator > 0))
+ return -1;
+ /* verify it against strcmp */
+ indicator = strcmp (s1, "Simplicity is the ultimate");
+ if (!(indicator > 0))
+ return -1;
+
+ /* s1 < s2, < 0 is expected */
+ indicator = clib_strcmp (s1, "Simplicity is the ultimate sophistication!");
+ if (!(indicator < 0))
+ return -1;
+ /* verify it against strcmp */
+ indicator = strcmp (s1, "Simplicity is the ultimate sophistication!");
+ if (!(indicator < 0))
+ return -1;
+
+ /* Try some negative tests */
+
+ /* Null pointers comparison */
+ s = 0;
+ indicator = clib_strcmp (s, s);
+ if (indicator != 0)
+ return -1;
+ /* verify it against strcmp */
+ indicator = strcmp (s, s);
+ if (indicator != 0)
+ return -1;
+
+ /* OK, seems to work */
+ return 0;
+}
+
+static int
+test_strncmp_s (vlib_main_t * vm, unformat_input_t * input)
+{
+ char s1[] = "Every moment is a fresh beginning";
+ uword s1len = sizeof (s1) - 1; // excluding null
+ errno_t err;
+ int indicator = 0;
+
+ vlib_cli_output (vm, "Test strncmp_s...");
+
+ /* s1 == s2, 0 is expected */
+ err = strncmp_s (s1, s1len, "Every moment is a fresh beginning", s1len,
+ &indicator);
+ if (err != EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+
+ /* s1 > s2, 0 is expected since comparison is no more than n character */
+ err = strncmp_s (s1, s1len, "Every moment is a fresh begin",
+ sizeof ("Every moment is a fresh begin") - 1, &indicator);
+ if (err != EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+
+ /* s1 < s2, < 0 is expected */
+ err = strncmp_s (s1, s1len, "Every moment is fresh beginning",
+ sizeof ("Every moment is fresh beginning") - 1,
+ &indicator);
+ if (err != EOK)
+ return -1;
+ if (!(indicator < 0))
+ return -1;
+
+ /* s1 > s2, > 0 is expected */
+ err = strncmp_s ("Every moment is fresh beginning. ",
+ sizeof ("Every moment is fresh beginning. ") - 1, s1,
+ s1len, &indicator);
+ if (err != EOK)
+ return -1;
+ if (!(indicator > 0))
+ return -1;
+
+ /* Try some negative tests */
+
+ /* Null pointers */
+ err = strncmp_s (0, 0, 0, 0, 0);
+ if (err != EINVAL)
+ return -1;
+
+ /* n > s1max */
+ err = strncmp_s (s1, s1len, "Every moment is a fresh beginning", s1len + 1,
+ &indicator);
+ if (err != EINVAL)
+ return -1;
+
+ /* unterminated s1 */
+ s1[s1len] = 0x1;
+ err = strncmp_s (s1, s1len, "Every moment is a fresh beginning",
+ sizeof ("Every moment is a fresh beginning") - 1,
+ &indicator);
+ if (err != EINVAL)
+ return -1;
+
+ /* OK, seems to work */
+ return 0;
+}
+
+static int
+test_clib_strncmp (vlib_main_t * vm, unformat_input_t * input)
+{
+ char s1[] = "Every moment is a fresh beginning";
+ uword s1len = sizeof (s1) - 1; // excluding null
+ int indicator, v_indicator;
+
+ vlib_cli_output (vm, "Test clib_strncmp...");
+
+ /* s1 == s2, 0 is expected */
+ indicator = clib_strncmp (s1, "Every moment is a fresh beginning", s1len);
+ if (indicator != 0)
+ return -1;
+ /* verify it against strncmp */
+ v_indicator = strncmp (s1, "Every moment is a fresh beginning", s1len);
+ if (v_indicator != 0)
+ return -1;
+ if (v_indicator != indicator)
+ return -1;
+
+ /* s1 > s2, 0 is expected since comparison is no more than n character */
+ indicator = clib_strncmp (s1, "Every moment is a fresh begin",
+ sizeof ("Every moment is a fresh begin") - 1);
+ if (indicator != 0)
+ return -1;
+ /* verify it against strncmp */
+ v_indicator = strncmp (s1, "Every moment is a fresh begin",
+ sizeof ("Every moment is a fresh begin") - 1);
+ if (v_indicator != 0)
+ return -1;
+ if (v_indicator != indicator)
+ return -1;
+
+ /* s1 < s2, < 0 is expected */
+ indicator = clib_strncmp (s1, "Every moment is fresh beginning",
+ sizeof ("Every moment is fresh beginning") - 1);
+ if (!(indicator < 0))
+ return -1;
+ /* verify it against strncmp */
+ v_indicator = strncmp (s1, "Every moment is fresh beginning",
+ sizeof ("Every moment is fresh beginning") - 1);
+ if (!(v_indicator < 0))
+ return -1;
+ if (v_indicator != indicator)
+ return -1;
+
+ /* s1 > s2, > 0 is expected */
+ indicator = clib_strncmp ("Every moment is fresh beginning. ", s1, s1len);
+ if (!(indicator > 0))
+ return -1;
+ /* verify it against strncmp */
+ v_indicator = strncmp ("Every moment is fresh beginning. ", s1, s1len);
+ if (!(v_indicator > 0))
+ return -1;
+ if (v_indicator != indicator)
+ return -1;
+
+ /* Try some negative tests */
+
+ /* Null pointers */
+
+ /* make sure we don't crash */
+ indicator = clib_strncmp (0, 0, 0);
+ if (indicator != EOK)
+ return -1;
+
+ /* n > s1 len */
+ indicator =
+ clib_strncmp (s1, "Every moment is a fresh beginning", s1len + 1);
+ if (indicator != 0)
+ return -1;
+ /* verify it against strncmp */
+ v_indicator = strncmp (s1, "Every moment is a fresh beginning", s1len + 1);
+ if (v_indicator != 0)
+ return -1;
+ if (v_indicator != indicator)
+ return -1;
+
+ /* unterminated s1 */
+ s1[s1len] = 0x1;
+ indicator = clib_strncmp (s1, "Every moment is a fresh beginning",
+ sizeof ("every moment is a fresh beginning") - 1);
+ if (indicator != 0)
+ return -1;
+ /* verify it against strncmp */
+ v_indicator = strncmp (s1, "Every moment is a fresh beginning",
+ sizeof ("Every moment is a fresh beginning") - 1);
+ if (v_indicator != 0)
+ return -1;
+ if (v_indicator != indicator)
+ return -1;
+
+ /* OK, seems to work */
+ return 0;
+}
+
+static int
+test_strcpy_s (vlib_main_t * vm, unformat_input_t * input)
+{
+ char src[] = "To err is human.";
+ char dst[64];
+ int indicator;
+ size_t s1size = sizeof (dst); // including null
+ errno_t err;
+
+ vlib_cli_output (vm, "Test strcpy_s...");
+
+ err = strcpy_s (dst, s1size, src);
+ if (err != EOK)
+ return -1;
+
+ /* This better not fail but check anyhow */
+ if (strcmp_s (dst, clib_strnlen (dst, sizeof (dst)), src, &indicator) !=
+ EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+
+ /* Negative tests */
+
+ err = strcpy_s (0, 0, 0);
+ if (err == EOK)
+ return -1;
+
+ /* Size fail */
+ err = strcpy_s (dst, 10, src);
+ if (err == EOK)
+ return -1;
+
+ /* overlap fail */
+ err = strcpy_s (dst, s1size, dst);
+ if (err == EOK)
+ return -1;
+
+ /* overlap fail */
+ err = strcpy_s (dst, s1size, dst + 1);
+ if (err == EOK)
+ return -1;
+
+ /* OK, seems to work */
+ return 0;
+}
+
+static int
+test_clib_strcpy (vlib_main_t * vm, unformat_input_t * input)
+{
+ char src[] = "The journey of a one thousand miles begins with one step.";
+ char dst[100];
+ int indicator;
+ errno_t err;
+
+ vlib_cli_output (vm, "Test clib_strcpy...");
+
+ err = clib_strcpy (dst, src);
+ if (err != EOK)
+ return -1;
+
+ /* This better not fail but check anyhow */
+ if (strcmp_s (dst, clib_strnlen (dst, sizeof (dst)), src, &indicator) !=
+ EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+
+ /* verify it against strcpy */
+ strcpy (dst, src);
+
+ /* This better not fail but check anyhow */
+ if (strcmp_s (dst, clib_strnlen (dst, sizeof (dst)), src, &indicator) !=
+ EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+
+ /* Negative tests */
+
+ err = clib_strcpy (0, 0);
+ if (err == EOK)
+ return -1;
+
+ /* overlap fail */
+ err = clib_strcpy (dst, dst);
+ if (err == EOK)
+ return -1;
+
+ /* overlap fail */
+ err = clib_strcpy (dst, dst + 1);
+ if (err == EOK)
+ return -1;
+
+ /* OK, seems to work */
+ return 0;
+}
+
+static int
+test_strncpy_s (vlib_main_t * vm, unformat_input_t * input)
+{
+ char src[] = "Those who dare to fail miserably can achieve greatly.";
+ char dst[100], old_dst[100];
+ int indicator;
+ size_t s1size = sizeof (dst); // including null
+ errno_t err;
+
+ vlib_cli_output (vm, "Test strncpy_s...");
+
+ /* dmax includes null, n excludes null */
+
+ /* n == string len of src */
+ err = strncpy_s (dst, s1size, src, clib_strnlen (src, sizeof (src)));
+ if (err != EOK)
+ return -1;
+ if (strcmp_s (dst, clib_strnlen (dst, sizeof (dst)), src, &indicator) !=
+ EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+
+ /* limited copy -- strlen src > n, copy up to n */
+ err = strncpy_s (dst, s1size, "The price of greatness is responsibility.",
+ 10);
+ if (err != EOK)
+ return -1;
+ if (strcmp_s (dst, clib_strnlen (dst, sizeof (dst)), "The price ",
+ &indicator) != EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+
+ /* n > string len of src */
+ err = strncpy_s (dst, s1size, src, clib_strnlen (src, sizeof (src)) + 10);
+ if (err != EOK)
+ return -1;
+ if (strcmp_s (dst, clib_strnlen (dst, sizeof (dst)), src, &indicator) !=
+ EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+
+ /* truncation, n >= dmax */
+ err = strncpy_s (dst, clib_strnlen (src, sizeof (src)), src,
+ clib_strnlen (src, sizeof (src)));
+ if (err != EOVERFLOW)
+ return -1;
+
+ /* Check dst content */
+ if (dst[strlen (dst)] != '\0')
+ return -1;
+ if (strncmp_s (dst, clib_strnlen (dst, sizeof (dst)), src,
+ clib_strnlen (dst, sizeof (dst)), &indicator) != EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+
+ /* zero length copy */
+ clib_strncpy (old_dst, dst, clib_strnlen (dst, sizeof (dst)));
+ err = strncpy_s (dst, sizeof (dst), src, 0);
+ if (err != EOK)
+ return -1;
+ /* verify dst is untouched */
+ if (strcmp_s (dst, clib_strnlen (dst, sizeof (dst)), old_dst, &indicator) !=
+ EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+
+ /* Negative tests */
+
+ err = strncpy_s (0, 0, 0, 1);
+ if (err == EOK)
+ return -1;
+
+ /* overlap fail */
+ err = strncpy_s (dst, s1size, dst + 1, s1size - 1);
+ if (err == EOK)
+ return -1;
+
+ /* overlap fail */
+ err = strncpy_s (dst, s1size, dst, s1size);
+ if (err == EOK)
+ return -1;
+
+ /* OK, seems to work */
+ return 0;
+}
+
+static int
+test_clib_strncpy (vlib_main_t * vm, unformat_input_t * input)
+{
+ char src[] = "Those who dare to fail miserably can achieve greatly.";
+ char dst[100], old_dst[100];
+ int indicator;
+ size_t s1size = sizeof (dst); // including null
+ errno_t err;
+
+ vlib_cli_output (vm, "Test clib_strncpy...");
+
+ /* n == string len of src */
+ err = clib_strncpy (dst, src, clib_strnlen (src, sizeof (src)));
+ if (err != EOK)
+ return -1;
+
+ /* This better not fail but check anyhow */
+ if (strcmp_s (dst, clib_strnlen (dst, sizeof (dst)), src, &indicator) !=
+ EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+
+ /* Verify it against strncpy */
+ strncpy (dst, src, strlen (src));
+
+ /* This better not fail but check anyhow */
+ if (strcmp_s (dst, clib_strnlen (dst, sizeof (dst)), src, &indicator) !=
+ EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+
+ /* limited copy -- strlen src > n, copy up to n */
+ err = clib_strncpy (dst, "The price of greatness is responsibility.", 10);
+ if (err != EOK)
+ return -1;
+ if (strcmp_s (dst, clib_strnlen (dst, sizeof (dst)), "The price ",
+ &indicator) != EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+ /* verify it against strncpy */
+ memset_s (dst, sizeof (dst), 0, sizeof (dst));
+ strncpy (dst, "The price of greatness is responsibility.", 10);
+ if (strcmp_s (dst, clib_strnlen (dst, sizeof (dst)), "The price ",
+ &indicator) != EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+
+ /* n > string len of src */
+ err = clib_strncpy (dst, src, clib_strnlen (src, sizeof (src)) + 10);
+ if (err != EOK)
+ return -1;
+ if (strcmp_s (dst, clib_strnlen (dst, sizeof (dst)), src, &indicator) !=
+ EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+ /* Verify it against strncpy */
+ strncpy (dst, src, strlen (src));
+ if (strcmp_s (dst, clib_strnlen (dst, sizeof (dst)), src, &indicator) !=
+ EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+
+ /* zero length copy */
+ clib_strncpy (old_dst, dst, clib_strnlen (dst, sizeof (dst)));
+ err = clib_strncpy (dst, src, 0);
+ if (err != EOK)
+ return -1;
+ /* verify dst is untouched */
+ if (strcmp_s (dst, clib_strnlen (dst, sizeof (dst)), old_dst, &indicator) !=
+ EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+
+ /* Negative tests */
+
+ err = clib_strncpy (0, 0, 1);
+ if (err == EOK)
+ return -1;
+
+ /* overlap fail */
+ err = clib_strncpy (dst, dst + 1, s1size);
+ if (err == EOK)
+ return -1;
+
+ /* overlap fail */
+ err = clib_strncpy (dst, dst, s1size);
+ if (err == EOK)
+ return -1;
+
+ /* OK, seems to work */
+ return 0;
+}
+
+static int
+test_strcat_s (vlib_main_t * vm, unformat_input_t * input)
+{
+ char src[100], dst[100], old_dst[100];
+ size_t s1size = sizeof (dst); // including null
+ errno_t err;
+ int indicator;
+
+ vlib_cli_output (vm, "Test strcat_s...");
+
+ strcpy_s (dst, sizeof (dst), "Tough time never last ");
+ strcpy_s (src, sizeof (src), "but tough people do");
+ err = strcat_s (dst, s1size, src);
+ if (err != EOK)
+ return -1;
+ if (strcmp_s (dst, s1size - 1,
+ "Tough time never last but tough people do",
+ &indicator) != EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+
+ /* empty string concatenation */
+ clib_strncpy (old_dst, dst, clib_strnlen (dst, sizeof (dst)));
+ err = strcat_s (dst, s1size, "");
+ if (err != EOK)
+ return -1;
+ /* verify dst is untouched */
+ if (strcmp_s (dst, s1size - 1, old_dst, &indicator) != EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+
+ /* negative stuff */
+ err = strcat_s (0, 0, 0);
+ if (err != EINVAL)
+ return -1;
+
+ /* overlap fail */
+ err = strcat_s (dst, s1size, dst + 1);
+ if (err != EINVAL)
+ return -1;
+
+ /* overlap fail */
+ err = strcat_s (dst, s1size, dst);
+ if (err != EINVAL)
+ return -1;
+
+ /* not enough space for dst */
+ err = strcat_s (dst, 10, src);
+ if (err != EINVAL)
+ return -1;
+
+ /* OK, seems to work */
+ return 0;
+}
+
+static int
+test_clib_strcat (vlib_main_t * vm, unformat_input_t * input)
+{
+ char src[100], dst[100], old_dst[100];
+ size_t s1size = sizeof (dst); // including null
+ errno_t err;
+ int indicator;
+
+ vlib_cli_output (vm, "Test clib_strcat...");
+
+ strcpy_s (dst, sizeof (dst), "Tough time never last ");
+ strcpy_s (src, sizeof (src), "but tough people do");
+ err = clib_strcat (dst, src);
+ if (err != EOK)
+ return -1;
+ if (strcmp_s (dst, s1size - 1,
+ "Tough time never last but tough people do",
+ &indicator) != EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+ /* verify it against strcat */
+ strcpy_s (dst, sizeof (dst), "Tough time never last ");
+ strcpy_s (src, sizeof (src), "but tough people do");
+ strcat (dst, src);
+ if (strcmp_s (dst, s1size - 1,
+ "Tough time never last but tough people do",
+ &indicator) != EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+
+ /* empty string concatenation */
+ clib_strncpy (old_dst, dst, clib_strnlen (dst, sizeof (dst)));
+ err = clib_strcat (dst, "");
+ if (err != EOK)
+ return -1;
+ /* verify dst is untouched */
+ if (strcmp_s (dst, s1size - 1, old_dst, &indicator) != EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+
+ /* negative stuff */
+ err = clib_strcat (0, 0);
+ if (err != EINVAL)
+ return -1;
+
+ /* overlap fail */
+ err = clib_strcat (dst, dst + 1);
+ if (err != EINVAL)
+ return -1;
+
+ /* overlap fail */
+ err = clib_strcat (dst, dst);
+ if (err != EINVAL)
+ return -1;
+
+ /* OK, seems to work */
+ return 0;
+}
+
+static int
+test_strncat_s (vlib_main_t * vm, unformat_input_t * input)
+{
+ char src[100], dst[100], old_dst[100];
+ size_t s1size = sizeof (dst); // including null
+ errno_t err;
+ char s1[] = "Two things are infinite: ";
+ char s2[] = "the universe and human stupidity; ";
+ char s3[] = "I am not sure about the universe.";
+ int indicator;
+
+ vlib_cli_output (vm, "Test strncat_s...");
+
+ strcpy_s (dst, sizeof (dst), s1);
+ strcpy_s (src, sizeof (src), s2);
+ err = strncat_s (dst, s1size, src, clib_strnlen (src, sizeof (src)));
+ if (err != EOK)
+ return -1;
+ if (strcmp_s (dst, s1size - 1,
+ "Two things are infinite: the universe and human stupidity; ",
+ &indicator) != EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+
+ /* truncation, n >= dmax - strnlen_s (dst, dmax) */
+ err = strncat_s (dst, clib_strnlen (dst, sizeof (dst)) +
+ clib_strnlen (s3, sizeof (s3)), s3,
+ clib_strnlen (s3, sizeof (s3)));
+ if (err != EOVERFLOW)
+ return -1;
+ /*
+ * resulting string is dst + strlen (s3) - 1 characters + null.
+ * notice the "." is missing at the end of the resulting string because
+ * the space is needed to accommodate the null
+ * Notice strcmp_s will check s1 or dst to make sure it is null terminated
+ */
+ if (strcmp_s (dst, s1size - 1,
+ "Two things are infinite: the universe and human stupidity; "
+ "I am not sure about the universe", &indicator) != EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+
+ /* n > strlen src */
+ strcpy_s (dst, sizeof (dst), s1);
+ err = strncat_s (dst, s1size, src, clib_strnlen (src, sizeof (src)) + 10);
+ if (err != EOK)
+ return -1;
+ if (strcmp_s (dst, s1size - 1,
+ "Two things are infinite: the universe and human stupidity; ",
+ &indicator) != EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+
+ /* zero length strncat */
+ clib_strncpy (old_dst, dst, clib_strnlen (dst, sizeof (dst)));
+ err = strncat_s (dst, sizeof (dst), src, 0);
+ if (err != EOK)
+ return -1;
+ /* verify dst is untouched */
+ if (strcmp_s (dst, s1size - 1, old_dst, &indicator) != EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+
+ /* empty string, wrong n concatenation */
+ err = strncat_s (dst, sizeof (dst), "", 10);
+ if (err != EOK)
+ return -1;
+ /* verify dst is untouched */
+ if (strcmp_s (dst, s1size - 1, old_dst, &indicator) != EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+
+ /* limited concatenation, string > n, copy up to n */
+ strcpy_s (dst, sizeof (dst), s1);
+ err = strncat_s (dst, s1size, s2, 13);
+ if (err != EOK)
+ return -1;
+ if (strcmp_s (dst, s1size - 1, "Two things are infinite: the universe ",
+ &indicator) != EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+ /* verify it against strncat */
+ strcpy_s (dst, sizeof (dst), s1);
+ strncat (dst, s2, 13);
+ if (strcmp_s (dst, s1size - 1, "Two things are infinite: the universe ",
+ &indicator) != EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+
+ /* negative stuff */
+ err = strncat_s (0, 0, 0, 1);
+ if (err != EINVAL)
+ return -1;
+
+ /* no room for dst -- dmax - strnlen_s (dst, dmax) == 0 */
+ err = strncat_s (dst, clib_strnlen (dst, sizeof (dst)), s2,
+ clib_strnlen (s2, sizeof (s2)));
+ if (err != EINVAL)
+ return -1;
+
+ /* overlap fail */
+ err = strncat_s (dst, s1size, dst + 1, clib_strnlen (dst + 1, s1size - 1));
+ if (err != EINVAL)
+ return -1;
+
+ /* overlap fail */
+ err = strncat_s (dst, s1size, dst, clib_strnlen (dst, sizeof (dst)));
+ if (err != EINVAL)
+ return -1;
+
+ /* OK, seems to work */
+ return 0;
+}
+
+static int
+test_clib_strncat (vlib_main_t * vm, unformat_input_t * input)
+{
+ char src[100], dst[100], old_dst[100];
+ size_t s1size = sizeof (dst); // including null
+ errno_t err;
+ char s1[] = "Two things are infinite: ";
+ char s2[] = "the universe and human stupidity; ";
+ int indicator;
+
+ vlib_cli_output (vm, "Test clib_strncat...");
+
+ /* n == strlen src */
+ strcpy_s (dst, sizeof (dst), s1);
+ strcpy_s (src, sizeof (src), s2);
+ err = clib_strncat (dst, src, clib_strnlen (src, sizeof (src)));
+ if (err != EOK)
+ return -1;
+ if (strcmp_s (dst, s1size - 1,
+ "Two things are infinite: the universe and human stupidity; ",
+ &indicator) != EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+ /* verify it against strncat */
+ strcpy_s (dst, sizeof (dst), s1);
+ strncat (dst, src, clib_strnlen (src, sizeof (src)));
+ if (strcmp_s (dst, s1size - 1,
+ "Two things are infinite: the universe and human stupidity; ",
+ &indicator) != EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+
+ /* n > strlen src */
+ strcpy_s (dst, sizeof (dst), s1);
+ err = clib_strncat (dst, src, clib_strnlen (src, sizeof (src)) + 10);
+ if (err != EOK)
+ return -1;
+ if (strcmp_s (dst, s1size - 1,
+ "Two things are infinite: the universe and human stupidity; ",
+ &indicator) != EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+ /* verify it against strncat */
+ strcpy_s (dst, sizeof (dst), s1);
+ strncat (dst, src, clib_strnlen (src, sizeof (src)));
+ if (strcmp_s (dst, s1size - 1,
+ "Two things are infinite: the universe and human stupidity; ",
+ &indicator) != EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+
+ /* zero length strncat */
+ clib_strncpy (old_dst, dst, clib_strnlen (dst, sizeof (dst)));
+ err = clib_strncat (dst, src, 0);
+ if (err != EOK)
+ return -1;
+ /* verify dst is untouched */
+ if (strcmp_s (dst, s1size - 1, old_dst, &indicator) != EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+
+ /* empty string, wrong n concatenation */
+ err = clib_strncat (dst, "", 10);
+ if (err != EOK)
+ return -1;
+ /* verify dst is untouched */
+ if (strcmp_s (dst, s1size - 1, old_dst, &indicator) != EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+
+ /* limited concatenation, string > n, copy up to n */
+ strcpy_s (dst, sizeof (dst), s1);
+ err = clib_strncat (dst, s2, 13);
+ if (err != EOK)
+ return -1;
+ if (strcmp_s (dst, s1size - 1, "Two things are infinite: the universe ",
+ &indicator) != EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+ /* verify it against strncat */
+ strcpy_s (dst, sizeof (dst), s1);
+ strncat (dst, s2, 13);
+ if (strcmp_s (dst, s1size - 1, "Two things are infinite: the universe ",
+ &indicator) != EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+
+ /* negative stuff */
+ err = clib_strncat (0, 0, 1);
+ if (err != EINVAL)
+ return -1;
+
+ /* overlap fail */
+ err = clib_strncat (dst, dst + 1, s1size - 1);
+ if (err != EINVAL)
+ return -1;
+
+ /* overlap fail */
+ err = clib_strncat (dst, dst, clib_strnlen (dst, sizeof (dst)));
+ if (err != EINVAL)
+ return -1;
+
+ /* OK, seems to work */
+ return 0;
+}
+
+static int
+test_strtok_s (vlib_main_t * vm, unformat_input_t * input)
+{
+ int indicator;
+ char *tok, *ptr;
+ char str2[20];
+ char str1[40];
+ uword len;
+ char *p2str = 0;
+ char *tok1, *tok2, *tok3, *tok4, *tok5, *tok6, *tok7;
+
+ vlib_cli_output (vm, "Test strtok_s...");
+ strcpy_s (str1, sizeof (str1), "brevity is the soul of wit");
+ len = strnlen_s (str1, sizeof (str1));
+ tok1 = strtok_s (str1, &len, " ", &p2str);
+ tok2 = strtok_s (0, &len, " ", &p2str);
+ tok3 = strtok_s (0, &len, " ", &p2str);
+ tok4 = strtok_s (0, &len, " ", &p2str);
+ tok5 = strtok_s (0, &len, " ", &p2str);
+ tok6 = strtok_s (0, &len, " ", &p2str);
+ tok7 = strtok_s (0, &len, " ", &p2str);
+ if ((tok1 == 0) ||
+ strcmp_s (tok1, strlen (tok1), "brevity", &indicator) != EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+ if ((tok2 == 0) || strcmp_s (tok2, strlen (tok2), "is", &indicator) != EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+ if ((tok3 == 0) || strcmp_s (tok3, strlen (tok3), "the", &indicator) != EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+ if ((tok4 == 0)
+ || strcmp_s (tok4, strlen (tok4), "soul", &indicator) != EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+ if ((tok5 == 0) || strcmp_s (tok5, strlen (tok5), "of", &indicator) != EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+ if ((tok6 == 0) || strcmp_s (tok6, strlen (tok6), "wit", &indicator) != EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+ if (tok7 != 0)
+ return -1;
+
+ /* delimiter not present in the string -- the whole string is returned */
+ strcpy_s (str1, sizeof (str1), "brevity is the soul of wit");
+ len = strnlen_s (str1, sizeof (str1) - 1);
+ p2str = 0;
+ tok1 = strtok_s (str1, &len, ",", &p2str);
+ if ((tok1 == 0) || strcmp_s (tok1, strlen (tok1), str1, &indicator) != EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+
+ /* negative stuff */
+ tok = strtok_s (0, 0, 0, 0);
+ if (tok != 0)
+ return -1;
+
+ /* s1 and ptr contents are null */
+ ptr = 0;
+ tok = strtok_s (0, 0, 0, &ptr);
+ if (tok != 0)
+ return -1;
+
+ /* unterminate s1 */
+ p2str = 0;
+ len = strnlen_s (str1, sizeof (str1) - 1);
+ str1[strlen (str1)] = 0x2;
+ tok = strtok_s (str1, &len, ",", &p2str);
+ if (tok != 0)
+ return -1;
+
+ /*
+ * unterminated s2. This test case in not perfect because there is no
+ * argument for s2max. But s2 len is limited to 16 characters. If the API
+ * does not find the null character at s2[15], it declares the string s2
+ * as unterminated.
+ */
+ memset_s (str2, sizeof (str2), 0xfa, sizeof (str2));
+ tok = strtok_s (str1, &len, str2, &p2str);
+ if (tok != 0)
+ return -1;
+
+ /* OK, seems to work */
+ return 0;
+}
+
+static int
+test_clib_strtok (vlib_main_t * vm, unformat_input_t * input)
+{
+ int indicator;
+ char *tok, *ptr, *s1;
+ char str1[40];
+ char *p2str;
+ char *tok1, *tok2, *tok3, *tok4, *tok5, *tok6, *tok7;
+
+ vlib_cli_output (vm, "Test clib_strtok...");
+ strcpy_s (str1, sizeof (str1), "brevity is the soul of wit");
+ p2str = 0;
+ tok1 = clib_strtok (str1, " ", &p2str);
+ tok2 = clib_strtok (0, " ", &p2str);
+ tok3 = clib_strtok (0, " ", &p2str);
+ tok4 = clib_strtok (0, " ", &p2str);
+ tok5 = clib_strtok (0, " ", &p2str);
+ tok6 = clib_strtok (0, " ", &p2str);
+ tok7 = clib_strtok (0, " ", &p2str);
+ if ((tok1 == 0) ||
+ strcmp_s (tok1, strlen (tok1), "brevity", &indicator) != EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+ if ((tok2 == 0) || strcmp_s (tok2, strlen (tok2), "is", &indicator) != EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+ if ((tok3 == 0) || strcmp_s (tok3, strlen (tok3), "the", &indicator) != EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+ if ((tok4 == 0)
+ || strcmp_s (tok4, strlen (tok4), "soul", &indicator) != EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+ if ((tok5 == 0) || strcmp_s (tok5, strlen (tok5), "of", &indicator) != EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+ if ((tok6 == 0) || strcmp_s (tok6, strlen (tok6), "wit", &indicator) != EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+ if (tok7 != 0)
+ return -1;
+ /* verify it againest strtok_r */
+ strcpy_s (str1, sizeof (str1), "brevity is the soul of wit");
+ p2str = 0;
+ tok1 = strtok_r (str1, " ", &p2str);
+ tok2 = strtok_r (0, " ", &p2str);
+ tok3 = strtok_r (0, " ", &p2str);
+ tok4 = strtok_r (0, " ", &p2str);
+ tok5 = strtok_r (0, " ", &p2str);
+ tok6 = strtok_r (0, " ", &p2str);
+ tok7 = strtok_r (0, " ", &p2str);
+ if ((tok1 == 0) ||
+ strcmp_s (tok1, strlen (tok1), "brevity", &indicator) != EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+ if ((tok2 == 0) || strcmp_s (tok2, strlen (tok2), "is", &indicator) != EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+ if ((tok3 == 0) || strcmp_s (tok3, strlen (tok3), "the", &indicator) != EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+ if ((tok4 == 0)
+ || strcmp_s (tok4, strlen (tok4), "soul", &indicator) != EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+ if ((tok5 == 0) || strcmp_s (tok5, strlen (tok5), "of", &indicator) != EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+ if ((tok6 == 0) || strcmp_s (tok6, strlen (tok6), "wit", &indicator) != EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+ if (tok7 != 0)
+ return -1;
+
+ /* delimiter not present in the string -- the whole string is returned */
+ strcpy_s (str1, sizeof (str1), "brevity is the soul of wit");
+ p2str = 0;
+ tok1 = clib_strtok (str1, ",", &p2str);
+ if ((tok1 == 0) || strcmp_s (tok1, strlen (tok1), str1, &indicator) != EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+ /* verify it against strtok_r */
+ strcpy_s (str1, sizeof (str1), "brevity is the soul of wit");
+ p2str = 0;
+ tok1 = strtok_r (str1, ",", &p2str);
+ if ((tok1 == 0) || strcmp_s (tok1, strlen (tok1), str1, &indicator) != EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+
+ /* negative stuff */
+ s1 = 0;
+ ptr = 0;
+ tok = clib_strtok (s1, s1, (char **) 0);
+ if (tok != 0)
+ return -1;
+
+ /* s1 and ptr contents are null */
+ tok = clib_strtok (s1, s1, &ptr);
+ if (tok != 0)
+ return -1;
+ /* verify it against strtok_r */
+ /* No can do. This causes a crash in strtok_r */
+ // tok = strtok_r (s1, " ", &ptr);
+ // if (tok != 0)
+ // return -1;
+
+ /*
+ * Can't test unterminated string s1 and s2 becuase clib_strtok does not
+ * supply s1 and s2 max
+ */
+
+ /* OK, seems to work */
+ return 0;
+}
+
+static int
+test_strnlen_s (vlib_main_t * vm, unformat_input_t * input)
+{
+ const char s1[] = "Truth is incontrovertible";
+ size_t len;
+
+ vlib_cli_output (vm, "Test strnlen_s...");
+
+ len = strnlen_s (s1, sizeof (s1));
+ if (len != sizeof (s1) - 1)
+ return -1;
+
+ len = strnlen_s (s1, 5);
+ if (len != 5)
+ return -1;
+
+ /* negative stuff */
+ len = strnlen_s (0, 0);
+ if (len != 0)
+ return -1;
+
+ /* OK, seems to work */
+ return 0;
+}
+
+static int
+test_clib_strnlen (vlib_main_t * vm, unformat_input_t * input)
+{
+ const char s1[] = "Truth is incontrovertible";
+ size_t len;
+
+ vlib_cli_output (vm, "Test clib_strnlen...");
+
+ len = clib_strnlen (s1, sizeof (s1));
+ if (len != sizeof (s1) - 1)
+ return -1;
+
+ len = clib_strnlen (s1, 5);
+ if (len != 5)
+ return -1;
+
+ /* negative stuff */
+ len = clib_strnlen (0, 0);
+ if (len != 0)
+ return -1;
+
+ /* OK, seems to work */
+ return 0;
+}
+
+static int
+test_strstr_s (vlib_main_t * vm, unformat_input_t * input)
+{
+ errno_t err;
+ char *sub = 0;
+ char s1[64];
+ size_t s1len = sizeof (s1) - 1; // excluding null
+ int indicator;
+
+ vlib_cli_output (vm, "Test strstr_s...");
+
+ /* substring not present */
+ strcpy_s (s1, s1len, "success is not final, failure is not fatal.");
+ err = strstr_s (s1, s1len, "failures", sizeof ("failures"), &sub);;
+ if (err != ESRCH)
+ return -1;
+
+ /* substring present */
+ err = strstr_s (s1, s1len, "failure", sizeof ("failure"), &sub);
+ if (err != EOK)
+ return -1;
+
+ if ((sub == 0) ||
+ strcmp_s (sub, strlen (sub), "failure is not fatal.", &indicator)
+ != EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+
+ /* negative stuff */
+
+ /* Null pointers test */
+ err = strstr_s (0, 0, 0, 0, 0);
+ if (err != EINVAL)
+ return -1;
+
+ /* unterminated s1 and s2 */
+ memset_s (s1, ARRAY_LEN (s1), 0xfe, ARRAY_LEN (s1));
+ err = strstr_s (s1, s1len, s1, s1len, &sub);
+ if (err != EINVAL)
+ return -1;
+
+ /* OK, seems to work */
+ return 0;
+}
+
+static int
+test_clib_strstr (vlib_main_t * vm, unformat_input_t * input)
+{
+ char *sub, *s;
+ char s1[64];
+ size_t s1len = sizeof (s1) - 1; // excluding null
+ int indicator;
+
+ vlib_cli_output (vm, "Test clib_strstr...");
+
+ /* substring not present */
+ strcpy_s (s1, s1len, "success is not final, failure is not fatal.");
+ sub = clib_strstr (s1, "failures");
+ if (sub != 0)
+ return -1;
+ /* verify it against strstr */
+ sub = strstr (s1, "failures");
+ if (sub != 0)
+ return -1;
+
+ /* substring present */
+ sub = clib_strstr (s1, "failure");
+ if (sub == 0)
+ return -1;
+ if (strcmp_s (sub, strlen (sub), "failure is not fatal.", &indicator) !=
+ EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+ /* verify it against strstr */
+ sub = strstr (s1, "failure");
+ if (sub == 0)
+ return -1;
+ if (strcmp_s (sub, strlen (sub), "failure is not fatal.", &indicator) !=
+ EOK)
+ return -1;
+ if (indicator != 0)
+ return -1;
+
+ /* negative stuff */
+
+ /* Null pointers test */
+ s = 0;
+ sub = clib_strstr (s, s);
+ if (sub != 0)
+ return -1;
+ /*
+ * Can't verify it against strstr for this test. Null pointers cause strstr
+ * to crash. Go figure!
+ */
+
+ /* unterminated s1 and s2 */
+ memset_s (s1, ARRAY_LEN (s1), 0xfe, ARRAY_LEN (s1));
+ sub = clib_strstr (s1, s1);
+ if (sub == 0)
+ return -1;
+ /*
+ * Can't verify it against strstr for this test. Unterminated string causes
+ * strstr to crash. Go figure!
+ */
+
+ /* OK, seems to work */
+ return 0;
+}
+
+#define foreach_string_test \
+ _ (0, MEMCPY_S, "memcpy_s", memcpy_s) \
+ _ (1, CLIB_MEMCPY, "clib_memcpy", clib_memcpy) \
+ _ (2, MEMSET_S , "memset_s", memset_s) \
+ _ (3, CLIB_MEMSET , "clib_memset", clib_memset) \
+ _ (4, MEMCMP_S, "memcmp_s", memcmp_s) \
+ _ (5, CLIB_MEMCMP, "clib_memcmp", clib_memcmp) \
+ _ (6, STRCMP_S, "strcmp_s", strcmp_s) \
+ _ (7, CLIB_STRCMP, "clib_strcmp", clib_strcmp) \
+ _ (8, STRNCMP_S, "strncmp_s", strncmp_s) \
+ _ (9, CLIB_STRNCMP, "clib_strncmp", clib_strncmp) \
+ _ (10, STRCPY_S, "strcpy_s", strcpy_s) \
+ _ (11, CLIB_STRCPY, "clib_strcpy", clib_strcpy) \
+ _ (12, STRNCPY_S, "strncpy_s", strncpy_s) \
+ _ (13, CLIB_STRNCPY, "clib_strncpy", clib_strncpy) \
+ _ (14, STRCAT_S, "strcat_s", strcat_s) \
+ _ (15, CLIB_STRCAT, "clib_strcat", clib_strcat) \
+ _ (16, STRNCAT_S, "strncat_s", strncat_s) \
+ _ (17, CLIB_STRNCAT, "clib_strncat", clib_strncat) \
+ _ (18, STRTOK_S, "strtok_s", strtok_s) \
+ _ (19, CLIB_STRTOK, "clib_strtok", clib_strtok) \
+ _ (20, STRNLEN_S, "strnlen_s", strnlen_s) \
+ _ (21, CLIB_STRNLEN, "clib_strnlen", clib_strnlen) \
+ _ (22, STRSTR_S, "strstr_s", strstr_s) \
+ _ (23, CLIB_STRSTR, "clib_strstr", clib_strstr)
+
+typedef enum
+{
+#define _(v,f,s,p) STRING_TEST_##f = v,
+ foreach_string_test
+#undef _
+} string_test_t;
+
+static uword
+unformat_string_test (unformat_input_t * input, va_list * args)
+{
+ u8 *r = va_arg (*args, u8 *);
+
+ if (0)
+ ;
+#define _(v,f,s,p) else if (unformat (input, s)) *r = STRING_TEST_##f;
+ foreach_string_test
+#undef _
+ else
+ return 0;
+
+ return 1;
+}
+
+typedef int (*string_test_func) (vlib_main_t * vm, unformat_input_t * input);
+
+typedef struct
+{
+ string_test_func test;
+} string_test_func_t;
+
static clib_error_t *
string_test_command_fn (vlib_main_t * vm,
unformat_input_t * input,
vlib_cli_command_t * cmd_arg)
{
- int res = 0;
- u8 t_memcpy = 0;
- u8 t_clib_memset = 0;
- u8 specific_test;
+ string_test_func_t string_func[] = {
+#define _(v,f,s,p) { test_##p },
+ foreach_string_test
+#undef _
+ };
+ const char *string_table[] = {
+#define _(v,f,s,p) s,
+ foreach_string_test
+#undef _
+ };
+ int res = 0, ok;
+ i8 specific_test = ~0;
while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
{
- if (unformat (input, "memcpy_s"))
- t_memcpy = 1;
- else if (unformat (input, "memset_s"))
- t_clib_memset = 1;
- break;
+ if (unformat (input, "%U", unformat_string_test, &specific_test))
+ break;
+ else
+ return clib_error_return (0, "unknown input `%U'",
+ format_unformat_error, input);
}
- specific_test = t_memcpy + t_clib_memset;
-
- if (specific_test == 0)
+ if (specific_test == ~0)
{
- res = test_memcpy (vm, input);
- res += test_clib_memset (vm, input);
- goto done;
+ for (specific_test = STRING_TEST_MEMCPY_S;
+ specific_test <= STRING_TEST_CLIB_STRSTR; specific_test++)
+ {
+ ok = (string_func[specific_test]).test (vm, input);
+ res += ok;
+ if (ok != 0)
+ vlib_cli_output (vm, "test_%s failed",
+ string_table[specific_test]);
+ }
}
-
- if (t_memcpy)
- res = test_memcpy (vm, input);
- else if (t_clib_memset)
- res = test_clib_memset (vm, input);
-
-done:
+ else
+ res = (string_func[specific_test]).test (vm, input);
if (res)
vlib_cli_output (vm, "String unit test(s) failed...");
else
@@ -131,7 +1660,12 @@ done:
VLIB_CLI_COMMAND (string_test_command, static) =
{
.path = "test string",
- .short_help = "string library tests",
+ .short_help = "test string [memcpy_s | clib_memcpy | memset_s | "
+ "clib_memset | memcmp_s | clib_memcmp | strcmp_s | clib_strcmp | "
+ "strncmp_s | clib_strncmp | strcpy_s | clib_strcpy | strncpy_s | "
+ "clib_strncpy | strcat_s | clib_strcat | strncat_s | clib_strncat | "
+ "strtok_s | clib_strtok | strnlen_s | clib_strnlen | strstr_s | "
+ "clib_strstr]",
.function = string_test_command_fn,
};
/* *INDENT-ON* */
diff --git a/src/vppinfra/string.c b/src/vppinfra/string.c
index b90f432c0cc..feae5b9165c 100644
--- a/src/vppinfra/string.c
+++ b/src/vppinfra/string.c
@@ -38,6 +38,12 @@
#include <vppinfra/string.h>
#include <vppinfra/error.h>
+/**
+ * @file
+ * @brief String Handling routines, including a performant
+ * implementation of many c-11 "safe" string functions.
+ */
+
/* Exchanges source and destination. */
void
clib_memswap (void *_a, void *_b, uword bytes)
@@ -91,6 +97,25 @@ clib_c11_violation (const char *s)
_clib_error (CLIB_ERROR_WARNING, (char *) __FUNCTION__, 0, (char *) s);
}
+/**
+ * @brief copy src to dest, at most n bytes, up to dmax
+ *
+ * ISO/IEC 9899:2017(C11), Porgramming languages -- C
+ * Annex K; Bounds-checking interfaces
+ *
+ * @param *dest pointer to memory to copy to
+ * @param dmax maximum length of resulting dest
+ * @param *src pointer to memory to copy from
+ * @param n maximum number of characters to copy from src
+ *
+ * @constraints No null pointers
+ * n shall not be greater than dmax
+ * no memory overlap between src and dest
+ *
+ * @return EOK success
+ * EINVAL runtime constraint error
+ *
+ */
errno_t
memcpy_s (void *__restrict__ dest, rsize_t dmax,
const void *__restrict__ src, rsize_t n)
@@ -98,12 +123,356 @@ memcpy_s (void *__restrict__ dest, rsize_t dmax,
return memcpy_s_inline (dest, dmax, src, n);
}
+/**
+ * @brief set n bytes starting at s to the specified c value
+ *
+ * ISO/IEC 9899:2017(C11), Porgramming languages -- C
+ * Annex K; Bounds-checking interfaces
+ *
+ * @param *s pointer to memory to set the c value
+ * @param smax maximum length of resulting s
+ * @param c byte value
+ * @param n maximum number of characters to set in s
+ *
+ * @constraints No null pointers
+ * n shall not be greater than smax
+ *
+ * @return EOK success
+ * EINVAL runtime constraint error
+ *
+ */
errno_t
memset_s (void *s, rsize_t smax, int c, rsize_t n)
{
return memset_s_inline (s, smax, c, n);
}
+/**
+ * @brief compare memory until they differ, and their difference is returned in
+ * diff
+ *
+ * ISO/IEC 9899:2017(C11), Porgramming languages -- C
+ * Annex K; Bounds-checking interfaces
+ *
+ * @param *s1 pointer to memory to compare against
+ * @param s1max maximum length of s1
+ * @param *s2 pointer to memory to compare with s1
+ * @param s2max length of s2
+ * @param *diff pointer to the diff which is an integer greater than, equal to,
+ * or less than zero according to s1 is greater than, equal to,
+ * or less than s2.
+ *
+ * @constraints No null pointers
+ * s1max and s2max shall not be zero
+ * s2max shall not be greater than s1max
+ *
+ * @return EOK success
+ * diff when the return code is EOK
+ * >0 s1 greater s2
+ * 0 s1 == s2
+ * <0 s1 < s2
+ * EINVAL runtime constraint error
+ *
+ */
+errno_t
+memcmp_s (const void *s1, rsize_t s1max, const void *s2, rsize_t s2max,
+ int *diff)
+{
+ return memcmp_s_inline (s1, s1max, s2, s2max, diff);
+}
+
+/**
+ * @brief compare string s2 to string s1, and their difference is returned in
+ * indicator
+ *
+ * ISO/IEC 9899:2017(C11), Porgramming languages -- C
+ * Annex K; Bounds-checking interfaces
+ *
+ * @param *s1 pointer to string to compare against
+ * @param s1max maximum length of s1, excluding null
+ * @param *s2 pointer to string to compare with s1
+ * @param *indicator pointer to the comparison result, which is an integer
+ * greater than, equal to, or less than zero according to
+ * s1 is greater than, equal to, or less than s2.
+ *
+ * @constraints No null pointers
+ * s1max shall not be zero
+ * s1 shall be null terminated
+ * n shall not be greater than the smaller of s1max and strlen
+ * of s1
+ *
+ * @return EOK success
+ * indicator when the return code is EOK
+ * >0 s1 greater s2
+ * 0 s1 == s2
+ * <0 s1 < s2
+ * EINVAL runtime constraint error
+ *
+ */
+errno_t
+strcmp_s (const char *s1, rsize_t s1max, const char *s2, int *indicator)
+{
+ return strcmp_s_inline (s1, s1max, s2, indicator);
+}
+
+/**
+ * @brief compare string s2 to string s1, no more than n characters, and their
+ * difference is returned in indicator
+ *
+ * ISO/IEC 9899:2017(C11), Porgramming languages -- C
+ * Annex K; Bounds-checking interfaces
+ *
+ * @param *s1 pointer to string to compare against
+ * @param s1max maximum length of s1, excluding null
+ * @param *s2 pointer to string to compare with s1
+ * @param n maximum number of characters to compare
+ * @param *indicator pointer to the comparison result, which is an integer
+ * greater than, equal to, or less than zero according to
+ * s1 is greater than, equal to, or less than s2.
+ *
+ * @constraints No null pointers
+ * s1max shall not be zero
+ * s1 shall be null terminated
+ *
+ * @return EOK success
+ * indicator when the return code is EOK
+ * >0 s1 greater s2
+ * 0 s1 == s2
+ * <0 s1 < s2
+ * EINVAL runtime constraint error
+ *
+ */
+errno_t
+strncmp_s (const char *s1, rsize_t s1max, const char *s2, rsize_t n,
+ int *indicator)
+{
+ return strncmp_s_inline (s1, s1max, s2, n, indicator);
+}
+
+/**
+ * @brief copy src string to dest string
+ *
+ * ISO/IEC 9899:2017(C11), Porgramming languages -- C
+ * Annex K; Bounds-checking interfaces
+ *
+ * @param *dest pointer to string to copy to
+ * @param dmax maximum length of resulting dest string, including null
+ * @param *src pointer to string to copy from
+ *
+ * @constraints No null pointers
+ * dmax shall not be zero
+ * dmax shall be greater than string length of src
+ * no memory overlap between src and dest
+ *
+ * @return EOK success
+ * EINVAL runtime constraint error
+ *
+ */
+errno_t
+strcpy_s (char *__restrict__ dest, rsize_t dmax, const char *__restrict__ src)
+{
+ return strcpy_s_inline (dest, dmax, src);
+}
+
+/**
+ * @brief copy src string to dest string, no more than n characters
+ *
+ * ISO/IEC 9899:2017(C11), Porgramming languages -- C
+ * Annex K; Bounds-checking interfaces
+ *
+ * @param *dest pointer to string to copy to
+ * @param dmax maximum length of resulting dest string, including null
+ * @param *src pointer to string to copy from
+ * @param n maximum number of characters to copy from src, excluding null
+ *
+ * @constraints No null pointers
+ * dmax shall not be zero
+ * no memory overlap between src and dest
+ *
+ * @return EOK success
+ * EINVAL runtime constraint error
+ * EOVERFLOW truncated operation. dmax - 1 characters were copied.
+ * dest is null terminated.
+ *
+ */
+errno_t
+strncpy_s (char *__restrict__ dest, rsize_t dmax,
+ const char *__restrict__ src, rsize_t n)
+{
+ return strncpy_s_inline (dest, dmax, src, n);
+}
+
+/**
+ * @brief append src string to dest string, including null
+ *
+ * ISO/IEC 9899:2017(C11), Porgramming languages -- C
+ * Annex K; Bounds-checking interfaces
+ *
+ * @param *dest pointer to string to append to
+ * @param dmax maximum length of resulting dest string, including null
+ * @param *src pointer to string to append from
+ *
+ * @constraints No null pointers
+ * dmax shall not be zero
+ * dest shall be null terminated
+ * given m = dmax - strnlen (dest, dmax)
+ * n = strnlen (src, m)
+ * n shall not be >= m
+ * no memory overlap between src and dest
+ *
+ * @return EOK success
+ * EINVAL runtime constraint error
+ *
+ */
+errno_t
+strcat_s (char *__restrict__ dest, rsize_t dmax, const char *__restrict__ src)
+{
+ return strcat_s_inline (dest, dmax, src);
+}
+
+/**
+ * @brief append src string to dest string, including null, no more than n
+ * characters
+ *
+ * ISO/IEC 9899:2017(C11), Porgramming languages -- C
+ * Annex K; Bounds-checking interfaces
+ *
+ * @param *dest pointer to string to append to
+ * @param dmax maximum length of resulting dest string, including null
+ * @param *src pointer to string to append from
+ * @param n maximum characters to append (excluding null)
+ *
+ * @constraints No null pointers
+ * dmax shall not be zero
+ * dest shall be null terminated
+ * dmax - strnlen (dest, dmax) shall not be zero
+ * no memory overlap between src and dest
+ *
+ * @return EOK success
+ * EINVAL runtime constraint error
+ * EOVERFLOW truncated operation. dmax - 1 characters were appended.
+ * dest is null terminated.
+ *
+ */
+errno_t
+strncat_s (char *__restrict__ dest, rsize_t dmax,
+ const char *__restrict__ src, rsize_t n)
+{
+ return strncat_s_inline (dest, dmax, src, n);
+}
+
+/**
+ * @brief tokenize string s1 with delimiter specified in s2. This is a stateful
+ * API when it is iterately called, it returns the next token from s1
+ * which is delimited by s2. s1max and ptr maintain the stateful
+ * information for the same caller and must not be altered by the
+ * caller during the iteration for the correct result
+ *
+ * ISO/IEC 9899:2017(C11), Porgramming languages -- C
+ * Annex K; Bounds-checking interfaces
+ *
+ * @param *s1 pointer to string to be searched for substring
+ * @param *s1max restricted maximum length of s1
+ * @param *s2 pointer to substring to search (16 characters max,
+ * including null)
+ * @param **ptr in/out pointer which maintains the stateful information
+ *
+ * @constraints s2, s1max, and ptr shall not be null
+ * if s1 is null, contents of ptr shall not be null
+ * s1 and s2 shall be null terminated
+ *
+ * @return non-null pointer to the first character of a token
+ * s1max and ptr are modified to contain the state
+ * null runtime constraint error or token is not found
+ *
+ * @example
+ * char *str2 = " ";
+ * char str1[100];
+ * uword len;
+ * char *p2str = 0;
+ * char *tok1, *tok2, *tok3, *tok4, *tok5, *tok6, *tok7;
+ *
+ * strncpy (str1, "brevity is the soul of wit", sizeof (str1));
+ * len = strlen (str1);
+ * tok1 = strtok_s (str1, &len, str2, &p2str);
+ * tok2 = strtok_s (0, &len, str2, &p2str);
+ * tok3 = strtok_s (0, &len, str2, &p2str);
+ * tok4 = strtok_s (0, &len, str2, &p2str);
+ * tok5 = strtok_s (0, &len, str2, &p2str);
+ * tok6 = strtok_s (0, &len, str2, &p2str);
+ * tok7 = strtok_s (0, &len, str2, &p2str);
+ *
+ * After the above series of calls,
+ * tok1 = "brevity", tok2 = "is", tok3 = "the", tok4 = "soul", tok5 = "of",
+ * tok6 = "wit", tok7 = null
+ */
+char *
+strtok_s (char *__restrict__ s1, rsize_t * __restrict__ s1max,
+ const char *__restrict__ s2, char **__restrict__ ptr)
+{
+ return strtok_s_inline (s1, s1max, s2, ptr);
+}
+
+/**
+ * @brief compute the length in s, no more than maxsize
+ *
+ * ISO/IEC 9899:2017(C11), Porgramming languages -- C
+ * Annex K; Bounds-checking interfaces
+ *
+ * @param *s pointer to string
+ * @param maxsize restricted maximum length
+ *
+ * @constraints No null pointers
+ * maxsize shall not be zero
+ *
+ * @return size_t the string length in s, excluding null character, and no
+ * more than maxsize or 0 if there is a constraint error
+ *
+ */
+size_t
+strnlen_s (const char *s, size_t maxsize)
+{
+ return strnlen_s_inline (s, maxsize);
+}
+
+/**
+ * @brief locate the first occurrence of the substring s2 in s1
+ *
+ * ISO/IEC 9899:2017(C11), Porgramming languages -- C
+ * Annex K; Bounds-checking interfaces
+ *
+ * @param *s1 pointer to string to be searched for substring
+ * @param s1max restricted maximum length of s1
+ * @param *s2 pointer to substring to search
+ * @param s2max restricted maximum length of s2
+ * @param **substring pointer to pointer substring to be returned
+ *
+ * @constraints No null pointers
+ * s1max and s2max shall not be zero
+ * s1 and s2 shall be null terminated
+ *
+ * @return EOK success
+ * substring when the return code is EOK, it contains the pointer which
+ * points to s1 that matches s2
+ * EINVAL runtime constraint error
+ * ESRCH no match
+ *
+ * @example
+ * char *sub = 0;
+ * char *s1 = "success is not final, failure is not fatal.";
+ *
+ * strstr_s (s1, strlen (s1), "failure", strlen ("failure"), &sub);
+ *
+ * After the above call,
+ * sub = "failure is not fatal."
+ */
+errno_t
+strstr_s (char *s1, rsize_t s1max, const char *s2, rsize_t s2max,
+ char **substring)
+{
+ return strstr_s_inline (s1, s1max, s2, s2max, substring);
+}
+
/*
* fd.io coding-style-patch-verification: ON
*
diff --git a/src/vppinfra/string.h b/src/vppinfra/string.h
index b00c0cfbcc2..a25d461868b 100644
--- a/src/vppinfra/string.h
+++ b/src/vppinfra/string.h
@@ -92,6 +92,23 @@ void clib_memswap (void *_a, void *_b, uword bytes);
#ifndef EINVAL
#define EINVAL 22
#endif
+#ifndef ESRCH
+#define ESRCH 3
+#endif
+#ifndef EOVERFLOW
+#define EOVERFLOW 75
+#endif
+
+/*
+ * In order to provide smooth mapping from unsafe string API to the clib string
+ * macro, we often have to improvise s1max and s2max due to the additional
+ * arguments are required for implementing the safe API. This macro is used
+ * to provide the s1max/s2max. It is not perfect becuase the actual
+ * s1max/s2max may be greater than 4k and the mapping from the unsafe API to
+ * the macro would cause a regression. However, it is not terribly likely.
+ * So I bet against the odds.
+ */
+#define CLIB_STRING_MACRO_MAX 4096
typedef int errno_t;
typedef uword rsize_t;
@@ -693,6 +710,702 @@ clib_count_equal_u8 (u8 * data, uword max_count)
return count;
}
+/*
+ * This macro is to provide smooth mapping from memcmp to memcmp_s.
+ * memcmp has fewer parameters and fewer returns than memcmp_s.
+ * This macro is somewhat a crutch. When err != EOK is returned from memcmp_s,
+ * we return 0 and spit out a message in the console because there is
+ * no way to return the error code to the memcmp callers.
+ * This condition happens when s1 or s2 is null. Please note
+ * in the extant memcmp calls, if s1, s2, or both are null, memcmp returns 0
+ * anyway. So we are consistent in this case for the comparison return
+ * although we also spit out a C11 violation message in the console to
+ * warn that they pass null pointers for both s1 and s2.
+ * Applications are encouraged to use the cool C11 memcmp_s API to get the
+ * maximum benefit out of it.
+ */
+#define clib_memcmp(s1,s2,m1) \
+ ({ int __diff = 0; \
+ memcmp_s_inline (s1, m1, s2, m1, &__diff); \
+ __diff; \
+ })
+
+errno_t memcmp_s (const void *s1, rsize_t s1max, const void *s2,
+ rsize_t s2max, int *diff);
+
+always_inline errno_t
+memcmp_s_inline (const void *s1, rsize_t s1max, const void *s2, rsize_t s2max,
+ int *diff)
+{
+ u8 bad;
+
+ bad = (s1 == 0) + (s2 == 0) + (diff == 0) + (s2max > s1max) + (s2max == 0) +
+ (s1max == 0);
+
+ if (PREDICT_FALSE (bad != 0))
+ {
+ if (s1 == NULL)
+ clib_c11_violation ("s1 NULL");
+ if (s2 == NULL)
+ clib_c11_violation ("s2 NULL");
+ if (diff == NULL)
+ clib_c11_violation ("diff NULL");
+ if (s2max > s1max)
+ clib_c11_violation ("s2max > s1max");
+ if (s2max == 0)
+ clib_c11_violation ("s2max 0");
+ if (s1max == 0)
+ clib_c11_violation ("s1max 0");
+ return EINVAL;
+ }
+
+ if (PREDICT_FALSE (s1 == s2))
+ {
+ *diff = 0;
+ return EOK;
+ }
+
+ *diff = memcmp (s1, s2, s2max);
+ return EOK;
+}
+
+/*
+ * This macro is to provide smooth mapping from strnlen to strnlen_s
+ */
+#define clib_strnlen(s,m) strnlen_s_inline(s,m)
+
+size_t strnlen_s (const char *s, size_t maxsize);
+
+always_inline size_t
+strnlen_s_inline (const char *s, size_t maxsize)
+{
+ u8 bad;
+
+ bad = (s == 0) + (maxsize == 0);
+ if (PREDICT_FALSE (bad != 0))
+ {
+ if (s == 0)
+ clib_c11_violation ("s NULL");
+ if (maxsize == 0)
+ clib_c11_violation ("maxsize 0");
+ return 0;
+ }
+ return strnlen (s, maxsize);
+}
+
+/*
+ * This macro is to provide smooth mapping from strcmp to strcmp_s.
+ * strcmp has fewer parameters and fewer returns than strcmp_s.
+ * This macro is somewhat a crutch. When err != EOK is returned from strcmp_s,
+ * we return 0 and spit out a message in the console because
+ * there is no way to return the error to the strcmp callers.
+ * This condition happens when s1 or s2 is null. Please note in the extant
+ * strcmp call, they would end up crashing if one of them is null.
+ * So the new behavior is no crash, but an error is displayed in the
+ * console which I think is more user friendly. If both s1 and s2 are null,
+ * strcmp returns 0. Obviously, strcmp did the pointers comparison prior
+ * to actually accessing the pointer contents. We are still consistent
+ * in this case for the comparison return although we also spit out a
+ * C11 violation message in the console to warn that they pass null pointers
+ * for both s1 and s2. The other problem is strcmp does not provide s1max,
+ * we use CLIB_STRING_MACRO_MAX and hopefully, s1 is null terminated.
+ * If not, we may be accessing memory beyonf what is intended.
+ * Applications are encouraged to use the cool C11 strcmp_s API to get the
+ * maximum benefit out of it.
+ */
+#define clib_strcmp(s1,s2) \
+ ({ int __indicator = 0; \
+ strcmp_s_inline (s1, CLIB_STRING_MACRO_MAX, s2, &__indicator); \
+ __indicator; \
+ })
+
+errno_t strcmp_s (const char *s1, rsize_t s1max, const char *s2,
+ int *indicator);
+
+always_inline errno_t
+strcmp_s_inline (const char *s1, rsize_t s1max, const char *s2,
+ int *indicator)
+{
+ u8 bad;
+
+ bad = (indicator == 0) + (s1 == 0) + (s2 == 0) + (s1max == 0) +
+ (s1 && s1max && s1[clib_strnlen (s1, s1max)] != '\0');
+
+ if (PREDICT_FALSE (bad != 0))
+ {
+ if (indicator == NULL)
+ clib_c11_violation ("indicator NULL");
+ if (s1 == NULL)
+ clib_c11_violation ("s1 NULL");
+ if (s2 == NULL)
+ clib_c11_violation ("s2 NULL");
+ if (s1max == 0)
+ clib_c11_violation ("s1max 0");
+ if (s1 && s1max && s1[clib_strnlen (s1, s1max)] != '\0')
+ clib_c11_violation ("s1 unterminated");
+ return EINVAL;
+ }
+
+ *indicator = strcmp (s1, s2);
+ return EOK;
+}
+
+/*
+ * This macro is to provide smooth mapping from strncmp to strncmp_s.
+ * strncmp has fewer parameters and fewer returns than strncmp_s. That said,
+ * this macro is somewhat a crutch. When we get err != EOK from strncmp_s,
+ * we return 0 and spit out a message in the console because there is no
+ * means to return the error to the strncmp caller.
+ * This condition happens when s1 or s2 is null. In the extant strncmp call,
+ * they would end up crashing if one of them is null. So the new behavior is
+ * no crash, but error is displayed in the console which is more
+ * user friendly. If s1 and s2 are null, strncmp returns 0. Obviously,
+ * strncmp did the pointers comparison prior to actually accessing the
+ * pointer contents. We are still consistent in this case for the comparison
+ * return although we also spit out a C11 violation message in the console to
+ * warn that they pass null pointers for both s1 and s2.
+ * Applications are encouraged to use the cool C11 strncmp_s API to get the
+ * maximum benefit out of it.
+ */
+#define clib_strncmp(s1,s2,n) \
+ ({ int __indicator = 0; \
+ strncmp_s_inline (s1, CLIB_STRING_MACRO_MAX, s2, n, &__indicator); \
+ __indicator; \
+ })
+
+errno_t strncmp_s (const char *s1, rsize_t s1max, const char *s2, rsize_t n,
+ int *indicator);
+
+always_inline errno_t
+strncmp_s_inline (const char *s1, rsize_t s1max, const char *s2, rsize_t n,
+ int *indicator)
+{
+ u8 bad;
+ u8 s1_greater_s1max = (s1 && s1max && n > clib_strnlen (s1, s1max));
+
+ if (PREDICT_FALSE (s1_greater_s1max && indicator))
+ {
+ /*
+ * strcmp allows n > s1max. If indicator is non null, we can still
+ * do the compare without any harm and return EINVAL as well as the
+ * result in indicator.
+ */
+ clib_c11_violation ("n exceeds s1 length");
+ *indicator = strncmp (s1, s2, n);
+ return EINVAL;
+ }
+
+ bad = (s1 == 0) + (s2 == 0) + (indicator == 0) + (s1max == 0) +
+ (s1 && s1max && s1[clib_strnlen (s1, s1max)] != '\0') + s1_greater_s1max;
+
+ if (PREDICT_FALSE (bad != 0))
+ {
+ if (indicator == NULL)
+ clib_c11_violation ("indicator NULL");
+ if (s1 == NULL)
+ clib_c11_violation ("s1 NULL");
+ if (s2 == NULL)
+ clib_c11_violation ("s2 NULL");
+ if (s1max == 0)
+ clib_c11_violation ("s1max 0");
+ if (s1 && s1max && s1[clib_strnlen (s1, s1max)] != '\0')
+ clib_c11_violation ("s1 unterminated");
+ if (s1_greater_s1max)
+ clib_c11_violation ("n exceeds s1 length");
+ return EINVAL;
+ }
+
+ *indicator = strncmp (s1, s2, n);
+ return EOK;
+}
+
+/*
+ * This macro is provided for smooth migration from strcpy. It is not perfect
+ * because we don't know the size of the destination buffer to pass to strcpy_s.
+ * We improvise dmax with CLIB_STRING_MACRO_MAX.
+ * Applications are encouraged to move to the C11 strcpy_s API.
+ */
+#define clib_strcpy(d,s) strcpy_s_inline(d,CLIB_STRING_MACRO_MAX,s)
+
+errno_t strcpy_s (char *__restrict__ dest, rsize_t dmax,
+ const char *__restrict__ src);
+
+always_inline errno_t
+strcpy_s_inline (char *__restrict__ dest, rsize_t dmax,
+ const char *__restrict__ src)
+{
+ u8 bad;
+ uword low, hi;
+ size_t n;
+
+ bad = (dest == 0) + (dmax == 0) + (src == 0);
+ if (PREDICT_FALSE (bad != 0))
+ {
+ if (dest == 0)
+ clib_c11_violation ("dest NULL");
+ if (src == 0)
+ clib_c11_violation ("src NULL");
+ if (dmax == 0)
+ clib_c11_violation ("dmax 0");
+ return EINVAL;
+ }
+
+ n = clib_strnlen (src, dmax);
+ if (PREDICT_FALSE (n >= dmax))
+ {
+ clib_c11_violation ("not enough space for dest");
+ return (EINVAL);
+ }
+ /* Not actually trying to copy anything is OK */
+ if (PREDICT_FALSE (n == 0))
+ return EOK;
+
+ /* Check for src/dst overlap, which is not allowed */
+ low = (uword) (src < dest ? src : dest);
+ hi = (uword) (src < dest ? dest : src);
+
+ if (PREDICT_FALSE (low + (n - 1) >= hi))
+ {
+ clib_c11_violation ("src/dest overlap");
+ return EINVAL;
+ }
+
+ clib_memcpy_fast (dest, src, n);
+ dest[n] = '\0';
+ return EOK;
+}
+
+/*
+ * This macro is provided for smooth migration from strncpy. It is not perfect
+ * because we don't know the size of the destination buffer to pass to
+ * strncpy_s. We improvise dmax with CLIB_STRING_MACRO_MAX.
+ * Applications are encouraged to move to the C11 strncpy_s API and provide
+ * the correct dmax for better error checking.
+ */
+#define clib_strncpy(d,s,n) strncpy_s_inline(d,CLIB_STRING_MACRO_MAX,s,n)
+
+errno_t
+strncpy_s (char *__restrict__ dest, rsize_t dmax,
+ const char *__restrict__ src, rsize_t n);
+
+always_inline errno_t
+strncpy_s_inline (char *__restrict__ dest, rsize_t dmax,
+ const char *__restrict__ src, rsize_t n)
+{
+ u8 bad;
+ uword low, hi;
+ rsize_t m;
+ errno_t status = EOK;
+
+ bad = (dest == 0) + (dmax == 0) + (src == 0) + (n == 0);
+ if (PREDICT_FALSE (bad != 0))
+ {
+ /* Not actually trying to copy anything is OK */
+ if (n == 0)
+ return EOK;
+ if (dest == 0)
+ clib_c11_violation ("dest NULL");
+ if (src == 0)
+ clib_c11_violation ("src NULL");
+ if (dmax == 0)
+ clib_c11_violation ("dmax 0");
+ return EINVAL;
+ }
+
+ if (PREDICT_FALSE (n >= dmax))
+ {
+ /* Relax and use strnlen of src */
+ clib_c11_violation ("n >= dmax");
+ m = clib_strnlen (src, dmax);
+ if (m >= dmax)
+ {
+ /* Truncate, adjust copy length to fit dest */
+ m = dmax - 1;
+ status = EOVERFLOW;
+ }
+ }
+ else
+ m = n;
+
+ /* Check for src/dst overlap, which is not allowed */
+ low = (uword) (src < dest ? src : dest);
+ hi = (uword) (src < dest ? dest : src);
+
+ if (PREDICT_FALSE (low + (m - 1) >= hi))
+ {
+ clib_c11_violation ("src/dest overlap");
+ return EINVAL;
+ }
+
+ clib_memcpy_fast (dest, src, m);
+ dest[m] = '\0';
+ return status;
+}
+
+/*
+ * This macro is to provide smooth migration from strcat to strcat_s.
+ * Because there is no dmax in strcat, we improvise it with
+ * CLIB_STRING_MACRO_MAX. Please note there may be a chance to overwrite dest
+ * with too many bytes from src.
+ * Applications are encouraged to use C11 API to provide the actual dmax
+ * for proper checking and protection.
+ */
+#define clib_strcat(d,s) strcat_s_inline(d,CLIB_STRING_MACRO_MAX,s)
+
+errno_t strcat_s (char *__restrict__ dest, rsize_t dmax,
+ const char *__restrict__ src);
+
+always_inline errno_t
+strcat_s_inline (char *__restrict__ dest, rsize_t dmax,
+ const char *__restrict__ src)
+{
+ u8 bad;
+ uword low, hi;
+ size_t m, n, dest_size;
+
+ bad = (dest == 0) + (dmax == 0) + (src == 0);
+ if (PREDICT_FALSE (bad != 0))
+ {
+ if (dest == 0)
+ clib_c11_violation ("dest NULL");
+ if (src == 0)
+ clib_c11_violation ("src NULL");
+ if (dmax == 0)
+ clib_c11_violation ("dmax 0");
+ return EINVAL;
+ }
+
+ dest_size = clib_strnlen (dest, dmax);
+ m = dmax - dest_size;
+ n = clib_strnlen (src, m);
+ if (PREDICT_FALSE (n >= m))
+ {
+ clib_c11_violation ("not enough space for dest");
+ return EINVAL;
+ }
+
+ /* Not actually trying to concatenate anything is OK */
+ if (PREDICT_FALSE (n == 0))
+ return EOK;
+
+ /* Check for src/dst overlap, which is not allowed */
+ low = (uword) (src < dest ? src : dest);
+ hi = (uword) (src < dest ? dest : src);
+
+ if (PREDICT_FALSE (low + (n - 1) >= hi))
+ {
+ clib_c11_violation ("src/dest overlap");
+ return EINVAL;
+ }
+
+ clib_memcpy_fast (dest + dest_size, src, n);
+ dest[dest_size + n] = '\0';
+ return EOK;
+}
+
+/*
+ * This macro is to provide smooth migration from strncat to strncat_s.
+ * The unsafe strncat does not have s1max. We improvise it with
+ * CLIB_STRING_MACRO_MAX. Please note there may be a chance to overwrite
+ * dest with too many bytes from src.
+ * Applications are encouraged to move to C11 strncat_s which requires dmax
+ * from the caller and provides checking to safeguard the memory corruption.
+ */
+#define clib_strncat(d,s,n) strncat_s_inline(d,CLIB_STRING_MACRO_MAX,s,n)
+
+errno_t strncat_s (char *__restrict__ dest, rsize_t dmax,
+ const char *__restrict__ src, rsize_t n);
+
+always_inline errno_t
+strncat_s_inline (char *__restrict__ dest, rsize_t dmax,
+ const char *__restrict__ src, rsize_t n)
+{
+ u8 bad;
+ uword low, hi;
+ size_t m, dest_size, allowed_size;
+ errno_t status = EOK;
+
+ bad = (dest == 0) + (src == 0) + (dmax == 0) + (n == 0);
+ if (PREDICT_FALSE (bad != 0))
+ {
+ /* Not actually trying to concatenate anything is OK */
+ if (n == 0)
+ return EOK;
+ if (dest == 0)
+ clib_c11_violation ("dest NULL");
+ if (src == 0)
+ clib_c11_violation ("src NULL");
+ if (dmax == 0)
+ clib_c11_violation ("dmax 0");
+ return EINVAL;
+ }
+
+ /* Check for src/dst overlap, which is not allowed */
+ low = (uword) (src < dest ? src : dest);
+ hi = (uword) (src < dest ? dest : src);
+
+ if (PREDICT_FALSE (low + (n - 1) >= hi))
+ {
+ clib_c11_violation ("src/dest overlap");
+ return EINVAL;
+ }
+
+ dest_size = clib_strnlen (dest, dmax);
+ allowed_size = dmax - dest_size;
+
+ if (PREDICT_FALSE (allowed_size == 0))
+ {
+ clib_c11_violation ("no space left in dest");
+ return (EINVAL);
+ }
+
+ if (PREDICT_FALSE (n >= allowed_size))
+ {
+ /*
+ * unlike strcat_s, strncat_s will do the concatenation anyway when
+ * there is not enough space in dest. But it will do the truncation and
+ * null terminate dest
+ */
+ m = clib_strnlen (src, allowed_size);
+ if (m >= allowed_size)
+ {
+ m = allowed_size - 1;
+ status = EOVERFLOW;
+ }
+ }
+ else
+ m = clib_strnlen (src, n);
+
+ clib_memcpy_fast (dest + dest_size, src, m);
+ dest[dest_size + m] = '\0';
+ return status;
+}
+
+/*
+ * This macro is to provide smooth mapping from strtok_r to strtok_s.
+ * To map strtok to this macro, the caller would have to supply an additional
+ * argument. strtokr_s requires s1max which the unsafe API does not have. So
+ * we have to improvise it with CLIB_STRING_MACRO_MAX. Unlike strtok_s,
+ * this macro cannot catch unterminated s1 and s2.
+ * Applications are encouraged to use the cool C11 strtok_s API to avoid
+ * these problems.
+ */
+#define clib_strtok(s1,s2,p) \
+ ({ rsize_t __s1max = CLIB_STRING_MACRO_MAX; \
+ strtok_s_inline (s1, &__s1max, s2, p); \
+ })
+
+char *strtok_s (char *__restrict__ s1, rsize_t * __restrict__ s1max,
+ const char *__restrict__ s2, char **__restrict__ ptr);
+
+always_inline char *
+strtok_s_inline (char *__restrict__ s1, rsize_t * __restrict__ s1max,
+ const char *__restrict__ s2, char **__restrict__ ptr)
+{
+#define STRTOK_DELIM_MAX_LEN 16
+ u8 bad;
+ const char *pt;
+ char *ptoken;
+ uword dlen, slen;
+
+ bad = (s1max == 0) + (s2 == 0) + (ptr == 0) +
+ ((s1 == 0) && ptr && (*ptr == 0));
+ if (PREDICT_FALSE (bad != 0))
+ {
+ if (s2 == NULL)
+ clib_c11_violation ("s2 NULL");
+ if (s1max == NULL)
+ clib_c11_violation ("s1max is NULL");
+ if (ptr == NULL)
+ clib_c11_violation ("ptr is NULL");
+ /* s1 == 0 and *ptr == null is no good */
+ if ((s1 == 0) && ptr && (*ptr == 0))
+ clib_c11_violation ("s1 and ptr contents are NULL");
+ return 0;
+ }
+
+ if (s1 == 0)
+ s1 = *ptr;
+
+ /*
+ * scan s1 for a delimiter
+ */
+ dlen = *s1max;
+ ptoken = 0;
+ while (*s1 != '\0' && !ptoken)
+ {
+ if (PREDICT_FALSE (dlen == 0))
+ {
+ *ptr = 0;
+ clib_c11_violation ("s1 unterminated");
+ return 0;
+ }
+
+ /*
+ * must scan the entire delimiter list
+ * ISO should have included a delimiter string limit!!
+ */
+ slen = STRTOK_DELIM_MAX_LEN;
+ pt = s2;
+ while (*pt != '\0')
+ {
+ if (PREDICT_FALSE (slen == 0))
+ {
+ *ptr = 0;
+ clib_c11_violation ("s2 unterminated");
+ return 0;
+ }
+ slen--;
+ if (*s1 == *pt)
+ {
+ ptoken = 0;
+ break;
+ }
+ else
+ {
+ pt++;
+ ptoken = s1;
+ }
+ }
+ s1++;
+ dlen--;
+ }
+
+ /*
+ * if the beginning of a token was not found, then no
+ * need to continue the scan.
+ */
+ if (ptoken == 0)
+ {
+ *s1max = dlen;
+ return (ptoken);
+ }
+
+ /*
+ * Now we need to locate the end of the token
+ */
+ while (*s1 != '\0')
+ {
+ if (dlen == 0)
+ {
+ *ptr = 0;
+ clib_c11_violation ("s1 unterminated");
+ return 0;
+ }
+
+ slen = STRTOK_DELIM_MAX_LEN;
+ pt = s2;
+ while (*pt != '\0')
+ {
+ if (slen == 0)
+ {
+ *ptr = 0;
+ clib_c11_violation ("s2 unterminated");
+ return 0;
+ }
+ slen--;
+ if (*s1 == *pt)
+ {
+ /*
+ * found a delimiter, set to null
+ * and return context ptr to next char
+ */
+ *s1 = '\0';
+ *ptr = (s1 + 1); /* return pointer for next scan */
+ *s1max = dlen - 1; /* account for the nulled delimiter */
+ return (ptoken);
+ }
+ else
+ {
+ /*
+ * simply scanning through the delimiter string
+ */
+ pt++;
+ }
+ }
+ s1++;
+ dlen--;
+ }
+
+ *ptr = s1;
+ *s1max = dlen;
+ return (ptoken);
+}
+
+/*
+ * This macro is to provide smooth mapping from strstr to strstr_s.
+ * strstr_s requires s1max and s2max which the unsafe API does not have. So
+ * we have to improvise them with CLIB_STRING_MACRO_MAX which may cause us
+ * to access memory beyond it is intended if s1 or s2 is unterminated.
+ * For the record, strstr crashes if s1 or s2 is unterminated. But this macro
+ * does not.
+ * Applications are encouraged to use the cool C11 strstr_s API to avoid
+ * this problem.
+ */
+#define clib_strstr(s1,s2) \
+ ({ char * __substring = 0; \
+ strstr_s_inline (s1, CLIB_STRING_MACRO_MAX, s2, CLIB_STRING_MACRO_MAX, \
+ &__substring); \
+ __substring; \
+ })
+
+errno_t strstr_s (char *s1, rsize_t s1max, const char *s2, rsize_t s2max,
+ char **substring);
+
+always_inline errno_t
+strstr_s_inline (char *s1, rsize_t s1max, const char *s2, rsize_t s2max,
+ char **substring)
+{
+ u8 bad;
+ size_t s1_size, s2_size;
+
+ bad =
+ (s1 == 0) + (s2 == 0) + (substring == 0) + (s1max == 0) + (s2max == 0) +
+ (s1 && s1max && (s1[clib_strnlen (s1, s1max)] != '\0')) +
+ (s2 && s2max && (s2[clib_strnlen (s2, s2max)] != '\0'));
+ if (PREDICT_FALSE (bad != 0))
+ {
+ if (s1 == 0)
+ clib_c11_violation ("s1 NULL");
+ if (s2 == 0)
+ clib_c11_violation ("s2 NULL");
+ if (s1max == 0)
+ clib_c11_violation ("s1max 0");
+ if (s2max == 0)
+ clib_c11_violation ("s2max 0");
+ if (substring == 0)
+ clib_c11_violation ("substring NULL");
+ if (s1 && s1max && (s1[clib_strnlen (s1, s1max)] != '\0'))
+ clib_c11_violation ("s1 unterminated");
+ if (s2 && s2max && (s2[clib_strnlen (s2, s1max)] != '\0'))
+ clib_c11_violation ("s2 unterminated");
+ return EINVAL;
+ }
+
+ /*
+ * s2 points to a string with zero length, or s2 equals s1, return s1
+ */
+ if (PREDICT_FALSE (*s2 == '\0' || s1 == s2))
+ {
+ *substring = s1;
+ return EOK;
+ }
+
+ /*
+ * s2_size > s1_size, it won't find match.
+ */
+ s1_size = clib_strnlen (s1, s1max);
+ s2_size = clib_strnlen (s2, s2max);
+ if (PREDICT_FALSE (s2_size > s1_size))
+ return ESRCH;
+
+ *substring = strstr (s1, s2);
+ if (*substring == 0)
+ return ESRCH;
+
+ return EOK;
+}
+
#endif /* included_clib_string_h */
/*
diff --git a/test/test_string.py b/test/test_string.py
new file mode 100644
index 00000000000..b44489e3038
--- /dev/null
+++ b/test/test_string.py
@@ -0,0 +1,41 @@
+#!/usr/bin/env python
+
+import unittest
+
+from framework import VppTestCase, VppTestRunner
+from vpp_ip_route import VppIpTable, VppIpRoute, VppRoutePath
+
+
+class TestString(VppTestCase):
+ """ String Test Cases """
+
+ @classmethod
+ def setUpClass(cls):
+ super(TestString, cls).setUpClass()
+
+ def setUp(self):
+ super(TestString, self).setUp()
+
+ def tearDown(self):
+ super(TestString, self).tearDown()
+
+ def test_string_unittest(self):
+ """ String unit tests """
+ names = ["memcpy_s",
+ "clib_memcmp", "clib_memcpy", "clib_memset",
+ "clib_strcat", "clib_strcmp", "clib_strcpy",
+ "clib_strncat", "clib_strncmp", "clib_strncpy",
+ "clib_strnlen", "clib_strstr", "clib_strtok",
+ "memcmp_s", "memcpy_s", "memset_s ",
+ "strcat_s", "strcmp_s", "strcpy_s",
+ "strncat_s", "strncmp_s", "strncpy_s",
+ "strnlen_s", "strstr_s", "strtok_s"]
+
+ for name in names:
+ error = self.vapi.cli("test string " + name)
+ if error.find("failed") != -1:
+ self.logger.critical("FAILURE in the " + name + " test")
+ self.assertEqual(error.find("failed"), -1)
+
+if __name__ == '__main__':
+ unittest.main(testRunner=VppTestRunner)