kvargs: support multiple lists
authorXueming Li <xuemingl@nvidia.com>
Thu, 11 Mar 2021 13:13:28 +0000 (13:13 +0000)
committerFerruh Yigit <ferruh.yigit@intel.com>
Tue, 16 Mar 2021 19:15:29 +0000 (20:15 +0100)
This patch updates kvargs parser to support value of multiple lists or
ranges:
  k1=v[1,2]v[3-5]

Signed-off-by: Xueming Li <xuemingl@nvidia.com>
Acked-by: Viacheslav Ovsiienko <viacheslavo@nvidia.com>
Acked-by: Thomas Monjalon <thomas@monjalon.net>
app/test/test_kvargs.c
lib/librte_kvargs/rte_kvargs.c

index 2a2dae4..a91ea8d 100644 (file)
@@ -35,6 +35,25 @@ static int check_handler(const char *key, const char *value,
        return 0;
 }
 
+/* test parsing. */
+static int test_kvargs_parsing(const char *args, unsigned int n)
+{
+       struct rte_kvargs *kvlist;
+
+       kvlist = rte_kvargs_parse(args, NULL);
+       if (kvlist == NULL) {
+               printf("rte_kvargs_parse() error: %s\n", args);
+               return -1;
+       }
+       if (kvlist->count != n) {
+               printf("invalid count value %d: %s\n", kvlist->count, args);
+               rte_kvargs_free(kvlist);
+               return -1;
+       }
+       rte_kvargs_free(kvlist);
+       return 0;
+}
+
 /* test a valid case */
 static int test_valid_kvargs(void)
 {
@@ -42,6 +61,19 @@ static int test_valid_kvargs(void)
        const char *args;
        const char *valid_keys_list[] = { "foo", "check", NULL };
        const char **valid_keys;
+       static const struct {
+               unsigned int expected;
+               const char *input;
+       } valid_inputs[] = {
+               { 2, "foo=1,foo=" },
+               { 2, "foo=1,foo=" },
+               { 2, "foo=1,foo" },
+               { 2, "foo=1,=2" },
+               { 1, "foo=[1,2" },
+               { 1, ",=" },
+               { 1, "foo=[" },
+       };
+       unsigned int i;
 
        /* empty args is valid */
        args = "";
@@ -191,6 +223,14 @@ static int test_valid_kvargs(void)
        }
        rte_kvargs_free(kvlist);
 
+       valid_keys = NULL;
+
+       for (i = 0; i < RTE_DIM(valid_inputs); ++i) {
+               args = valid_inputs[i].input;
+               if (test_kvargs_parsing(args, valid_inputs[i].expected))
+                       goto fail;
+       }
+
        return 0;
 
  fail:
@@ -212,12 +252,6 @@ static int test_invalid_kvargs(void)
        /* list of argument that should fail */
        const char *args_list[] = {
                "wrong-key=x",     /* key not in valid_keys_list */
-               "foo=1,foo=",      /* empty value */
-               "foo=1,foo",       /* no value */
-               "foo=1,=2",        /* no key */
-               "foo=[1,2",        /* no closing bracket in value */
-               ",=",              /* also test with a smiley */
-               "foo=[",           /* no value in list and no closing bracket */
                NULL };
        const char **args;
        const char *valid_keys_list[] = { "foo", "check", NULL };
index 285081c..ffae891 100644 (file)
@@ -5,6 +5,7 @@
 
 #include <string.h>
 #include <stdlib.h>
+#include <stdbool.h>
 
 #include <rte_string_fns.h>
 
 /*
  * Receive a string with a list of arguments following the pattern
  * key=value,key=value,... and insert them into the list.
- * strtok() is used so the params string will be copied to be modified.
+ * Params string will be copied to be modified.
+ * list "[]" and list element splitter ",", "-" is treated as value.
+ * Supported examples:
+ *   k1=v1,k2=v2
+ *   k1
+ *   k1=x[0-1]y[1,3-5,9]z
  */
 static int
 rte_kvargs_tokenize(struct rte_kvargs *kvlist, const char *params)
 {
-       unsigned i;
-       char *str;
-       char *ctx1 = NULL;
-       char *ctx2 = NULL;
+       char *str, *start;
+       bool in_list = false, end_key = false, end_value = false;
+       bool save = false, end_pair = false;
 
        /* Copy the const char *params to a modifiable string
         * to pass to rte_strsplit
@@ -32,36 +37,74 @@ rte_kvargs_tokenize(struct rte_kvargs *kvlist, const char *params)
 
        /* browse each key/value pair and add it in kvlist */
        str = kvlist->str;
-       while ((str = strtok_r(str, RTE_KVARGS_PAIRS_DELIM, &ctx1)) != NULL) {
+       start = str; /* start of current key or value */
+       while (1) {
+               switch (*str) {
+               case '=': /* End of key. */
+                       end_key = true;
+                       save = true;
+                       break;
+               case ',':
+                       /* End of value, skip comma in middle of range */
+                       if (!in_list) {
+                               if (end_key)
+                                       end_value = true;
+                               else
+                                       end_key = true;
+                               save = true;
+                               end_pair = true;
+                       }
+                       break;
+               case '[': /* Start of list. */
+                       in_list = true;
+                       break;
+               case ']': /* End of list.  */
+                       if (in_list)
+                               in_list = false;
+                       break;
+               case '\0': /* End of string */
+                       if (end_key)
+                               end_value = true;
+                       else
+                               end_key = true;
+                       save = true;
+                       end_pair = true;
+                       break;
+               default:
+                       break;
+               }
 
-               i = kvlist->count;
-               if (i >= RTE_KVARGS_MAX)
-                       return -1;
+               if (!save) {
+                       /* Continue if not end of key or value. */
+                       str++;
+                       continue;
+               }
 
-               kvlist->pairs[i].key = strtok_r(str, RTE_KVARGS_KV_DELIM, &ctx2);
-               kvlist->pairs[i].value = strtok_r(NULL, RTE_KVARGS_KV_DELIM, &ctx2);
-               if (kvlist->pairs[i].key == NULL ||
-                   kvlist->pairs[i].value == NULL)
+               if (kvlist->count >= RTE_KVARGS_MAX)
                        return -1;
 
-               /* Detect list [a,b] to skip comma delimiter in list. */
-               str = kvlist->pairs[i].value;
-               if (str[0] == '[') {
-                       /* Find the end of the list. */
-                       while (str[strlen(str) - 1] != ']') {
-                               /* Restore the comma erased by strtok_r(). */
-                               if (ctx1 == NULL || ctx1[0] == '\0')
-                                       return -1; /* no closing bracket */
-                               str[strlen(str)] = ',';
-                               /* Parse until next comma. */
-                               str = strtok_r(NULL, RTE_KVARGS_PAIRS_DELIM, &ctx1);
-                               if (str == NULL)
-                                       return -1; /* no closing bracket */
-                       }
+               if (end_value)
+                       /* Value parsed */
+                       kvlist->pairs[kvlist->count].value = start;
+               else if (end_key)
+                       /* Key parsed. */
+                       kvlist->pairs[kvlist->count].key = start;
+
+               if (end_pair) {
+                       if (end_value || str != start)
+                               /* Ignore empty pair. */
+                               kvlist->count++;
+                       end_key = false;
+                       end_value = false;
+                       end_pair = false;
                }
 
-               kvlist->count++;
-               str = NULL;
+               if (*str == '\0') /* End of string. */
+                       break;
+               *str = '\0';
+               str++;
+               start = str;
+               save = false;
        }
 
        return 0;