From patchwork Tue Nov 21 12:26:51 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: fengchengwen X-Patchwork-Id: 134517 X-Patchwork-Delegate: thomas@monjalon.net Return-Path: X-Original-To: patchwork@inbox.dpdk.org Delivered-To: patchwork@inbox.dpdk.org Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id C01744338E; Tue, 21 Nov 2023 13:29:47 +0100 (CET) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id AC71042EA6; Tue, 21 Nov 2023 13:29:47 +0100 (CET) Received: from szxga01-in.huawei.com (szxga01-in.huawei.com [45.249.212.187]) by mails.dpdk.org (Postfix) with ESMTP id 52E8B42E9D for ; Tue, 21 Nov 2023 13:29:45 +0100 (CET) Received: from dggpeml100024.china.huawei.com (unknown [172.30.72.53]) by szxga01-in.huawei.com (SkyGuard) with ESMTP id 4SZNwc4lQgzvR1n; Tue, 21 Nov 2023 20:29:20 +0800 (CST) Received: from localhost.localdomain (10.50.165.33) by dggpeml100024.china.huawei.com (7.185.36.115) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.35; Tue, 21 Nov 2023 20:29:43 +0800 From: Chengwen Feng To: , , Subject: [24.03 RFC] argparse: add argparse library Date: Tue, 21 Nov 2023 12:26:51 +0000 Message-ID: <20231121122651.7078-1-fengchengwen@huawei.com> X-Mailer: git-send-email 2.17.1 MIME-Version: 1.0 X-Originating-IP: [10.50.165.33] X-ClientProxiedBy: dggems706-chm.china.huawei.com (10.3.19.183) To dggpeml100024.china.huawei.com (7.185.36.115) X-CFilter-Loop: Reflected X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Introduce argparse library (which was inspired by the thread [1]), compared with getopt, the argparse has following advantages: 1) Set the help information when defining parameters. 2) Support positional parameters. The parameters parsing according following: 1) positional: use callback to parse (passed the long-name as the key for callback). 2) optional: In addition to callback to parse, but also support: 2.1) no-val: support set default value to saver. 2.2) has-val: support set value to saver, the value must be conform RTE_ARGPARSE_ARG_VAL_xxx. 2.3) opt-val: if current without value then treat as no-val, else could treat as has-val. Examples: (take dmafwd as example): 1) If parse with callback: static int func(const char *key, const char *value, const char *opaque) { if (!strcmp("--mac-updating", key)) { mac_updating = 1; } else if (!strcmp("--no-mac-updating", key)) { mac_updating = 0; } else if (!strcmp("--portmask", key)) { dma_enabled_port_mask = dma_parse_portmask(optarg); if (dma_enabled_port_mask & ~default_port_mask || dma_enabled_port_mask <= 0) { ... } } else { ... } } static int dma_parse_args(int argc, char **argv, unsigned int nb_ports) { static struct rte_argparse opts[] = { .prog = "dma", .usage = NULL, .descriptor = "dma and nic fwd example", .epilog = NULL, .exit_on_error = true, .opt = { { "--mac-updating", NULL, "Enable MAC addresses updating", func, 0, NULL, NULL, RTE_ARGPARSE_ARG_NO_VAL }, { "--no-mac-updating", NULL, "disable MAC addresses updating", func, 0, NULL, NULL, RTE_ARGPARSE_ARG_NO_VAL }, { "--portmask", "-p", "hexadecimal bitmask of ports to configure", func, 0, NULL, NULL, RTE_ARGPARSE_ARG_HAS_VAL }, { NULL, NULL, NULL, NULL, 0, NULL, NULL, 0} } }; return rte_argparse_parse(opts, argc, argv); } 2) If parse with value: static int dma_parse_args(int argc, char **argv, unsigned int nb_ports) { static struct rte_argparse opts[] = { .prog = "dma", .usage = NULL, .descriptor = "dma and nic fwd example", .epilog = NULL, .exit_on_error = true, .opt = { { "--mac-updating", NULL, "Enable MAC addresses updating", NULL, 0, &mac_updating, (void *)1, RTE_ARGPARSE_ARG_NO_VAL }, { "--no-mac-updating", NULL, "disable MAC addresses updating", NULL, 0, &mac_updating, (void *)0, RTE_ARGPARSE_ARG_NO_VAL }, { "--portmask", "-p", "hexadecimal bitmask of ports to configure", NULL, 0, &dma_enabled_port_mask, NULL, RTE_ARGPARSE_ARG_HAS_VAL }, { NULL, NULL, NULL, NULL, 0, NULL, NULL, 0} } }; int ret; ret = rte_argparse_parse(opts, argc, argv); if (ret != 0) return ret; if (dma_enabled_port_mask & ~default_port_mask || dma_enabled_port_mask <= 0) { ... } } 3) Also could mix parse with func and with value. [1] https://patchwork.dpdk.org/project/dpdk/patch/20231105054539.22303-2-fengchengwen@huawei.com/ Signed-off-by: Chengwen Feng --- lib/argparse/meson.build | 6 ++ lib/argparse/rte_argparse.h | 139 ++++++++++++++++++++++++++++++++++++ lib/argparse/version.map | 7 ++ lib/meson.build | 1 + 4 files changed, 153 insertions(+) create mode 100644 lib/argparse/meson.build create mode 100644 lib/argparse/rte_argparse.h create mode 100644 lib/argparse/version.map diff --git a/lib/argparse/meson.build b/lib/argparse/meson.build new file mode 100644 index 0000000000..ac4a883b8d --- /dev/null +++ b/lib/argparse/meson.build @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2023 HiSilicon Limited. + +headers = files('rte_argparse.h') + +deps += ['kvargs'] diff --git a/lib/argparse/rte_argparse.h b/lib/argparse/rte_argparse.h new file mode 100644 index 0000000000..21157a9436 --- /dev/null +++ b/lib/argparse/rte_argparse.h @@ -0,0 +1,139 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 HiSilicon Limited + */ + +#ifndef RTE_ARGPARSE_H +#define RTE_ARGPARSE_H + +/** + * @file rte_argparse.h + * + * This API provides argparse function. + * + * Compare with getopt, the argparse has following advantages: + * 1) Set the help information when defining parameters. + * 2) Support positional parameters. + * + * The parameters parsing according following: + * 1) positional: use callback to parse (passed the long-name as the key for + * callback). + * 2) optional: + * In addition to callback to parse, but also support: + * 2.1) no-val: support set default value to saver. + * 2.2) has-val: support set value to saver, the value must be conform + * RTE_ARGPARSE_ARG_VAL_xxx. + * 2.3) opt-val: if current without value then treat as no-val, else could + * treat as has-val. + * + */ + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +enum rte_argparse_arg_flags { + /** + * Bit0-1 represent whether has value + */ + RTE_ARGPARSE_ARG_NO_VAL = 1u << 0, /**< The arg has no value. */ + RTE_ARGPARSE_ARG_HAS_VAL = 2u << 0, /**< The arg has value. */ + RTE_ARGPARSE_ARG_OPT_VAL = 3u << 0, /**< The arg has optional value. */ + + /** + * Bit2-4 represent the value type + */ + RTE_ARGPARSE_ARG_VAL_INT = 1u << 2, /**< The arg's value is int type. */ + RTE_ARGPARSE_ARG_VAL_FLOAT = 2u << 2, /**< The arg's value is float type. */ + RTE_ARGPARSE_ARG_VAL_BOOL = 3u << 2, /**< The arg's value is bool type. */ + RTE_ARGPARSE_ARG_VAL_STRING = 4u << 2, /**< The arg's value is string type. */ +}; + +/** + * A structure used to hold opt config. + */ +struct rte_argparse_arg { + /** + * The long name of arg: + * 1) If the arg is optional, it must start with '--', + * 2) If it is a positional arg, it must not start with '-'. + * Note: only one '-' will treat as error. + */ + const char *long_name; + /** + * The short name of arg: + * 1) This field could be set only if long_name is optional, and must + * start with only one '-' and one letter, + * 2) Other case it should be set NULL. + */ + const char *short_name; + /** The help info of arg. */ + const char *help; + + /* + * Parse the arg's callback, it will be used to parse the arg if + * it is not NULL. + */ + arg_handler_t callback; + /** The opaque which used to invoke callback */ + void *opaque; + + /* + * The saver for the arg. If not NULL, set value to default_val or + * parse from input. + * Note: the flags of the arg must be RTE_ARGPARSE_VAL_* if this value + * is not NULL. + */ + void *saver; + /* + * Default value for the arg, cover following case: + * 1) The arg don't require value, the saver will set to default_val + * when option found. + * 2) The arg has option value but don't take value this time, the + * saver will set to default_val when option found. + */ + void *default_val; + + /** @see rte_argparse_arg_flags. */ + uint32_t flags; +}; + +/** + * A structure used to hold argparse basic info. + */ +struct rte_argparse { + const char *prog; /**< Program name */ + const char *usage; /**< How to use the program */ + const char *descriptor; /**< Explain what the program does */ + const char *epilog; /**< Text at the bottom of help */ + bool exit_on_error; /**< Whether exit when error */ + struct rte_argparse_arg opt[]; /**< */ +}; + +/** + * @warning + * @b EXPERIMENTAL: this API may change without prior notice. + * + * Parse parameters + * + * @param self + * Parser handler. + * @param argc + * Parameters count + * @param argv + * Array of parameters points. + * + * @return + * 0 on success. Otherwise negative value is returned. + */ +__rte_experimental +int rte_argparse_parse(struct rte_argparse *self, int argc, char **argv); + +#ifdef __cplusplus +} +#endif + +#endif /* RTE_ARGPARSE_H */ diff --git a/lib/argparse/version.map b/lib/argparse/version.map new file mode 100644 index 0000000000..36b0902167 --- /dev/null +++ b/lib/argparse/version.map @@ -0,0 +1,7 @@ +EXPERIMENTAL { + global: + + rte_argparse_parse; + + local: *; +}; diff --git a/lib/meson.build b/lib/meson.build index 6c143ce5a6..cdd2d3c536 100644 --- a/lib/meson.build +++ b/lib/meson.build @@ -11,6 +11,7 @@ libraries = [ 'log', 'kvargs', # eal depends on kvargs + 'argparse', 'telemetry', # basic info querying 'eal', # everything depends on eal 'ring', From patchwork Mon Dec 4 07:50:44 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: fengchengwen X-Patchwork-Id: 134782 Return-Path: X-Original-To: patchwork@inbox.dpdk.org Delivered-To: patchwork@inbox.dpdk.org Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id EAF954365D; Mon, 4 Dec 2023 08:53:59 +0100 (CET) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id C542540693; Mon, 4 Dec 2023 08:53:59 +0100 (CET) Received: from szxga03-in.huawei.com (szxga03-in.huawei.com [45.249.212.189]) by mails.dpdk.org (Postfix) with ESMTP id 0A61D402AE for ; Mon, 4 Dec 2023 08:53:57 +0100 (CET) Received: from dggpeml500024.china.huawei.com (unknown [172.30.72.57]) by szxga03-in.huawei.com (SkyGuard) with ESMTP id 4SkG574qbwz14L6p; Mon, 4 Dec 2023 15:48:59 +0800 (CST) Received: from localhost.localdomain (10.50.165.33) by dggpeml500024.china.huawei.com (7.185.36.10) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.35; Mon, 4 Dec 2023 15:53:55 +0800 From: Chengwen Feng To: , , , CC: Subject: [RFC v2 2/6] argparse: support verify argument config Date: Mon, 4 Dec 2023 07:50:44 +0000 Message-ID: <20231204075048.894-3-fengchengwen@huawei.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20231204075048.894-1-fengchengwen@huawei.com> References: <20231121122651.7078-1-fengchengwen@huawei.com> <20231204075048.894-1-fengchengwen@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.50.165.33] X-ClientProxiedBy: dggems701-chm.china.huawei.com (10.3.19.178) To dggpeml500024.china.huawei.com (7.185.36.10) X-CFilter-Loop: Reflected X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org This commit supports verify argument config. Signed-off-by: Chengwen Feng --- lib/argparse/rte_argparse.c | 310 +++++++++++++++++++++++++++++++++++- 1 file changed, 309 insertions(+), 1 deletion(-) diff --git a/lib/argparse/rte_argparse.c b/lib/argparse/rte_argparse.c index bf14c56858..eff504a778 100644 --- a/lib/argparse/rte_argparse.c +++ b/lib/argparse/rte_argparse.c @@ -2,13 +2,321 @@ * Copyright(c) 2023 HiSilicon Limited */ +#include +#include +#include + +#include + #include "rte_argparse.h" +RTE_LOG_REGISTER_DEFAULT(rte_argparse_logtype, INFO); +#define ARGPARSE_LOG(level, ...) \ + rte_log(RTE_LOG_ ## level, rte_argparse_logtype, RTE_FMT("argparse: " \ + RTE_FMT_HEAD(__VA_ARGS__,) "\n", RTE_FMT_TAIL(__VA_ARGS__,))) + +#define BIT(x) (1ul << (x)) +#define GENMASK_U32(h, l) \ + (((~0u) << (l)) & (~0u >> (31 - (h)))) +#define BF_SHF(x) (__builtin_ffsll(x) - 1) +#define FIELD_GET(mask, reg) \ + ((typeof(mask))(((reg) & (mask)) >> BF_SHF(mask))) + +#define ARG_ATTR_HAS_VAL_MASK GENMASK_U32(1, 0) +#define ARG_ATTR_HAS_VAL_SHIFT 0 +#define ARG_ATTR_VAL_TYPE_MASK GENMASK_U32(10, 2) +#define ARG_ATTR_VAL_TYPE_SHIFT 2 +#define ARG_ATTR_SUPPORT_MULTI_MASK BIT(16) +#define ARG_ATTR_SUPPORT_MULTI_SHIFT 16 +#define ARG_ATTR_FLAG_PARSED_MASK BIT(31) +#define ARG_ATTR_FLAG_PARSED_SHIFT 31 + +static inline bool +is_arg_optional(const struct rte_argparse_arg *arg) +{ + return arg->name_long[0] == '-'; +} + +static inline bool +is_arg_positional(const struct rte_argparse_arg *arg) +{ + return arg->name_long[0] != '-'; +} + +static inline uint32_t +arg_attr_has_val(const struct rte_argparse_arg *arg) +{ + return FIELD_GET(ARG_ATTR_HAS_VAL_MASK, arg->flags); +} + +static inline uint32_t +arg_attr_val_type(const struct rte_argparse_arg *arg) +{ + return FIELD_GET(ARG_ATTR_VAL_TYPE_MASK, arg->flags); +} + +static inline bool +arg_attr_flag_multi(const struct rte_argparse_arg *arg) +{ + return FIELD_GET(ARG_ATTR_SUPPORT_MULTI_MASK, arg->flags); +} + +static inline uint32_t +arg_attr_unused_bits(const struct rte_argparse_arg *arg) +{ +#define USED_BIT_MASK (ARG_ATTR_HAS_VAL_MASK | ARG_ATTR_VAL_TYPE_MASK | \ + ARG_ATTR_SUPPORT_MULTI_MASK) + return arg->flags & ~USED_BIT_MASK; +} + +static int +verify_arg_name(const struct rte_argparse_arg *arg) +{ + if (is_arg_optional(arg)) { + if (strlen(arg->name_long) <= 3) { + ARGPARSE_LOG(ERR, "optional long name %s too short!", arg->name_long); + return -EINVAL; + } + if (arg->name_long[1] != '-') { + ARGPARSE_LOG(ERR, "optional long name %s must only start with '--'", + arg->name_long); + return -EINVAL; + } + if (arg->name_long[2] == '-') { + ARGPARSE_LOG(ERR, "optional long name %s should not start with '---'", + arg->name_long); + return -EINVAL; + } + } + + if (arg->name_short == NULL) + return 0; + + if (!is_arg_optional(arg)) { + ARGPARSE_LOG(ERR, "short name %s corresponding long name must be optional!", + arg->name_short); + return -EINVAL; + } + + if (strlen(arg->name_short) != 2 || arg->name_short[0] != '-' || + arg->name_short[1] == '-') { + ARGPARSE_LOG(ERR, "short name %s must start with a hyphen (-) followed by an English letter", + arg->name_short); + return -EINVAL; + } + + return 0; +} + +static int +verify_arg_help(const struct rte_argparse_arg *arg) +{ + if (arg->help == NULL) { + ARGPARSE_LOG(ERR, "argument %s must have help info!", arg->name_long); + return -EINVAL; + } + + return 0; +} + +static int +verify_arg_has_val(const struct rte_argparse_arg *arg) +{ + uint32_t has_val = arg_attr_has_val(arg); + + if (is_arg_positional(arg)) { + if (has_val == RTE_ARGPARSE_ARG_REQUIRED_VALUE) + return 0; + ARGPARSE_LOG(ERR, "argument %s is positional, should has zero or required-val!", + arg->name_long); + return -EINVAL; + } + + if (has_val == 0) { + ARGPARSE_LOG(ERR, "argument %s is optional, has-val config wrong!", + arg->name_long); + return -EINVAL; + } + + return 0; +} + +static int +verify_arg_saver(const struct rte_argparse *obj, uint32_t index) +{ + uint32_t cmp_max = FIELD_GET(ARG_ATTR_VAL_TYPE_MASK, RTE_ARGPARSE_ARG_VALUE_MAX); + const struct rte_argparse_arg *arg = &obj->args[index]; + uint32_t val_type = arg_attr_val_type(arg); + uint32_t has_val = arg_attr_has_val(arg); + + if (arg->val_saver == NULL) { + if (val_type != 0) { + ARGPARSE_LOG(ERR, "argument %s parse by callback, val-type must be zero!", + arg->name_long); + return -EINVAL; + } + + if (obj->callback == NULL) { + ARGPARSE_LOG(ERR, "argument %s parse by callback, but callback is NULL!", + arg->name_long); + return -EINVAL; + } + + return 0; + } + + if (val_type == 0 || val_type >= cmp_max) { + ARGPARSE_LOG(ERR, "argument %s val-type config wrong!", arg->name_long); + return -EINVAL; + } + + if (has_val == RTE_ARGPARSE_ARG_REQUIRED_VALUE && arg->val_set != NULL) { + ARGPARSE_LOG(ERR, "argument %s has required value, val-set should be NULL!", + arg->name_long); + return -EINVAL; + } + + return 0; +} + +static int +verify_arg_flags(const struct rte_argparse *obj, uint32_t index) +{ + const struct rte_argparse_arg *arg = &obj->args[index]; + uint32_t unused_bits = arg_attr_unused_bits(arg); + + if (unused_bits != 0) { + ARGPARSE_LOG(ERR, "argument %s flags set wrong!", arg->name_long); + return -EINVAL; + } + + if (!(arg->flags & RTE_ARGPARSE_ARG_SUPPORT_MULTI)) + return 0; + + if (is_arg_positional(arg)) { + ARGPARSE_LOG(ERR, "argument %s is positional, don't support multiple times!", + arg->name_long); + return -EINVAL; + } + + if (arg->val_saver != NULL) { + ARGPARSE_LOG(ERR, "argument %s could occur multiple times, should use callback to parse!", + arg->name_long); + return -EINVAL; + } + + if (obj->callback == NULL) { + ARGPARSE_LOG(ERR, "argument %s should use callback to parse, but callback is NULL!", + arg->name_long); + return -EINVAL; + } + + return 0; +} + +static int +verify_arg_repeat(const struct rte_argparse *self, uint32_t index) +{ + const struct rte_argparse_arg *arg = &self->args[index]; + uint32_t i; + + for (i = 0; i < index; i++) { + if (!strcmp(arg->name_long, self->args[i].name_long)) { + ARGPARSE_LOG(ERR, "argument %s repeat!", arg->name_long); + return -EINVAL; + } + } + + if (arg->name_short == NULL) + return 0; + + for (i = 0; i < index; i++) { + if (self->args[i].name_short == NULL) + continue; + if (!strcmp(arg->name_short, self->args[i].name_short)) { + ARGPARSE_LOG(ERR, "argument %s repeat!", arg->name_short); + return -EINVAL; + } + } + + return 0; +} + +static int +verify_argparse_arg(const struct rte_argparse *obj, uint32_t index) +{ + const struct rte_argparse_arg *arg = &obj->args[index]; + int ret; + + ret = verify_arg_name(arg); + if (ret != 0) + return ret; + + ret = verify_arg_help(arg); + if (ret != 0) + return ret; + + ret = verify_arg_has_val(arg); + if (ret != 0) + return ret; + + ret = verify_arg_saver(obj, index); + if (ret != 0) + return ret; + + ret = verify_arg_flags(obj, index); + if (ret != 0) + return ret; + + ret = verify_arg_repeat(obj, index); + if (ret != 0) + return ret; + + return 0; +} + +static int +verify_argparse(const struct rte_argparse *obj) +{ + uint32_t idx = 0; + int ret; + + if (obj->prog_name == NULL) { + ARGPARSE_LOG(ERR, "program name is NULL!"); + return -EINVAL; + } + + if (obj->usage == NULL) { + ARGPARSE_LOG(ERR, "usage is NULL!"); + return -EINVAL; + } + + while (obj->args[idx].name_long != NULL) { + ret = verify_argparse_arg(obj, idx); + if (ret != 0) + return ret; + idx++; + } + + return 0; +} + int rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv) { - (void)obj; + int ret; + (void)argc; (void)argv; + + ret = verify_argparse(obj); + if (ret != 0) + goto error; + return 0; + +error: + if (obj->exit_on_error) + exit(ret); + return ret; } From patchwork Mon Dec 11 09:51:01 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: fengchengwen X-Patchwork-Id: 135008 Return-Path: X-Original-To: patchwork@inbox.dpdk.org Delivered-To: patchwork@inbox.dpdk.org Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id 4B44D436C8; Mon, 11 Dec 2023 10:54:26 +0100 (CET) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 3939540E6E; Mon, 11 Dec 2023 10:54:26 +0100 (CET) Received: from szxga08-in.huawei.com (szxga08-in.huawei.com [45.249.212.255]) by mails.dpdk.org (Postfix) with ESMTP id C052F402DC for ; Mon, 11 Dec 2023 10:54:23 +0100 (CET) Received: from mail.maildlp.com (unknown [172.19.163.48]) by szxga08-in.huawei.com (SkyGuard) with ESMTP id 4SpcXT3PFgz1Q6Vv; Mon, 11 Dec 2023 17:54:17 +0800 (CST) Received: from dggpeml500024.china.huawei.com (unknown [7.185.36.10]) by mail.maildlp.com (Postfix) with ESMTPS id 81C151800B8; Mon, 11 Dec 2023 17:54:22 +0800 (CST) Received: from localhost.localdomain (10.50.165.33) by dggpeml500024.china.huawei.com (7.185.36.10) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.35; Mon, 11 Dec 2023 17:54:22 +0800 From: Chengwen Feng To: , , , CC: Subject: [RFC v3 03/12] argparse: support verify argument config Date: Mon, 11 Dec 2023 09:51:01 +0000 Message-ID: <20231211095110.18946-4-fengchengwen@huawei.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20231211095110.18946-1-fengchengwen@huawei.com> References: <20231121122651.7078-1-fengchengwen@huawei.com> <20231211095110.18946-1-fengchengwen@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.50.165.33] X-ClientProxiedBy: dggems703-chm.china.huawei.com (10.3.19.180) To dggpeml500024.china.huawei.com (7.185.36.10) X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org This commit supports verify argument config. Signed-off-by: Chengwen Feng --- lib/argparse/rte_argparse.c | 307 +++++++++++++++++++++++++++++++++++- 1 file changed, 306 insertions(+), 1 deletion(-) diff --git a/lib/argparse/rte_argparse.c b/lib/argparse/rte_argparse.c index bf14c56858..6fdcf4f07b 100644 --- a/lib/argparse/rte_argparse.c +++ b/lib/argparse/rte_argparse.c @@ -2,13 +2,318 @@ * Copyright(c) 2023 HiSilicon Limited */ +#include +#include +#include + +#include + #include "rte_argparse.h" +RTE_LOG_REGISTER_DEFAULT(rte_argparse_logtype, INFO); +#define ARGPARSE_LOG(level, ...) \ + rte_log(RTE_LOG_ ## level, rte_argparse_logtype, RTE_FMT("argparse: " \ + RTE_FMT_HEAD(__VA_ARGS__,) "\n", RTE_FMT_TAIL(__VA_ARGS__,))) + +#define ARG_ATTR_HAS_VAL_MASK RTE_GENMASK64(1, 0) +#define ARG_ATTR_VAL_TYPE_MASK RTE_GENMASK64(9, 2) +#define ARG_ATTR_SUPPORT_MULTI_MASK RTE_BIT64(10) +#define ARG_ATTR_FLAG_PARSED_MASK RTE_BIT64(63) + +static inline bool +is_arg_optional(const struct rte_argparse_arg *arg) +{ + return arg->name_long[0] == '-'; +} + +static inline bool +is_arg_positional(const struct rte_argparse_arg *arg) +{ + return arg->name_long[0] != '-'; +} + +static inline uint32_t +arg_attr_has_val(const struct rte_argparse_arg *arg) +{ + return RTE_FIELD_GET64(ARG_ATTR_HAS_VAL_MASK, arg->flags); +} + +static inline uint32_t +arg_attr_val_type(const struct rte_argparse_arg *arg) +{ + return RTE_FIELD_GET64(ARG_ATTR_VAL_TYPE_MASK, arg->flags); +} + +static inline bool +arg_attr_flag_multi(const struct rte_argparse_arg *arg) +{ + return RTE_FIELD_GET64(ARG_ATTR_SUPPORT_MULTI_MASK, arg->flags); +} + +static inline uint32_t +arg_attr_unused_bits(const struct rte_argparse_arg *arg) +{ +#define USED_BIT_MASK (ARG_ATTR_HAS_VAL_MASK | ARG_ATTR_VAL_TYPE_MASK | \ + ARG_ATTR_SUPPORT_MULTI_MASK) + return arg->flags & ~USED_BIT_MASK; +} + +static int +verify_arg_name(const struct rte_argparse_arg *arg) +{ + if (is_arg_optional(arg)) { + if (strlen(arg->name_long) <= 3) { + ARGPARSE_LOG(ERR, "optional long name %s too short!", arg->name_long); + return -EINVAL; + } + if (arg->name_long[1] != '-') { + ARGPARSE_LOG(ERR, "optional long name %s must only start with '--'", + arg->name_long); + return -EINVAL; + } + if (arg->name_long[2] == '-') { + ARGPARSE_LOG(ERR, "optional long name %s should not start with '---'", + arg->name_long); + return -EINVAL; + } + } + + if (arg->name_short == NULL) + return 0; + + if (!is_arg_optional(arg)) { + ARGPARSE_LOG(ERR, "short name %s corresponding long name must be optional!", + arg->name_short); + return -EINVAL; + } + + if (strlen(arg->name_short) != 2 || arg->name_short[0] != '-' || + arg->name_short[1] == '-') { + ARGPARSE_LOG(ERR, "short name %s must start with a hyphen (-) followed by an English letter", + arg->name_short); + return -EINVAL; + } + + return 0; +} + +static int +verify_arg_help(const struct rte_argparse_arg *arg) +{ + if (arg->help == NULL) { + ARGPARSE_LOG(ERR, "argument %s must have help info!", arg->name_long); + return -EINVAL; + } + + return 0; +} + +static int +verify_arg_has_val(const struct rte_argparse_arg *arg) +{ + uint32_t has_val = arg_attr_has_val(arg); + + if (is_arg_positional(arg)) { + if (has_val == RTE_ARGPARSE_ARG_REQUIRED_VALUE) + return 0; + ARGPARSE_LOG(ERR, "argument %s is positional, should has zero or required-val!", + arg->name_long); + return -EINVAL; + } + + if (has_val == 0) { + ARGPARSE_LOG(ERR, "argument %s is optional, has-val config wrong!", + arg->name_long); + return -EINVAL; + } + + return 0; +} + +static int +verify_arg_saver(const struct rte_argparse *obj, uint32_t index) +{ + uint32_t cmp_max = RTE_FIELD_GET64(ARG_ATTR_VAL_TYPE_MASK, RTE_ARGPARSE_ARG_VALUE_MAX); + const struct rte_argparse_arg *arg = &obj->args[index]; + uint32_t val_type = arg_attr_val_type(arg); + uint32_t has_val = arg_attr_has_val(arg); + + if (arg->val_saver == NULL) { + if (val_type != 0) { + ARGPARSE_LOG(ERR, "argument %s parse by callback, val-type must be zero!", + arg->name_long); + return -EINVAL; + } + + if (obj->callback == NULL) { + ARGPARSE_LOG(ERR, "argument %s parse by callback, but callback is NULL!", + arg->name_long); + return -EINVAL; + } + + return 0; + } + + if (val_type == 0 || val_type >= cmp_max) { + ARGPARSE_LOG(ERR, "argument %s val-type config wrong!", arg->name_long); + return -EINVAL; + } + + if (has_val == RTE_ARGPARSE_ARG_REQUIRED_VALUE && arg->val_set != NULL) { + ARGPARSE_LOG(ERR, "argument %s has required value, val-set should be NULL!", + arg->name_long); + return -EINVAL; + } + + return 0; +} + +static int +verify_arg_flags(const struct rte_argparse *obj, uint32_t index) +{ + const struct rte_argparse_arg *arg = &obj->args[index]; + uint32_t unused_bits = arg_attr_unused_bits(arg); + + if (unused_bits != 0) { + ARGPARSE_LOG(ERR, "argument %s flags set wrong!", arg->name_long); + return -EINVAL; + } + + if (!(arg->flags & RTE_ARGPARSE_ARG_SUPPORT_MULTI)) + return 0; + + if (is_arg_positional(arg)) { + ARGPARSE_LOG(ERR, "argument %s is positional, don't support multiple times!", + arg->name_long); + return -EINVAL; + } + + if (arg->val_saver != NULL) { + ARGPARSE_LOG(ERR, "argument %s could occur multiple times, should use callback to parse!", + arg->name_long); + return -EINVAL; + } + + if (obj->callback == NULL) { + ARGPARSE_LOG(ERR, "argument %s should use callback to parse, but callback is NULL!", + arg->name_long); + return -EINVAL; + } + + return 0; +} + +static int +verify_arg_repeat(const struct rte_argparse *self, uint32_t index) +{ + const struct rte_argparse_arg *arg = &self->args[index]; + uint32_t i; + + for (i = 0; i < index; i++) { + if (!strcmp(arg->name_long, self->args[i].name_long)) { + ARGPARSE_LOG(ERR, "argument %s repeat!", arg->name_long); + return -EINVAL; + } + } + + if (arg->name_short == NULL) + return 0; + + for (i = 0; i < index; i++) { + if (self->args[i].name_short == NULL) + continue; + if (!strcmp(arg->name_short, self->args[i].name_short)) { + ARGPARSE_LOG(ERR, "argument %s repeat!", arg->name_short); + return -EINVAL; + } + } + + return 0; +} + +static int +verify_argparse_arg(const struct rte_argparse *obj, uint32_t index) +{ + const struct rte_argparse_arg *arg = &obj->args[index]; + int ret; + + ret = verify_arg_name(arg); + if (ret != 0) + return ret; + + ret = verify_arg_help(arg); + if (ret != 0) + return ret; + + ret = verify_arg_has_val(arg); + if (ret != 0) + return ret; + + ret = verify_arg_saver(obj, index); + if (ret != 0) + return ret; + + ret = verify_arg_flags(obj, index); + if (ret != 0) + return ret; + + ret = verify_arg_repeat(obj, index); + if (ret != 0) + return ret; + + return 0; +} + +static int +verify_argparse(const struct rte_argparse *obj) +{ + uint32_t idx; + int ret; + + if (obj->prog_name == NULL) { + ARGPARSE_LOG(ERR, "program name is NULL!"); + return -EINVAL; + } + + if (obj->usage == NULL) { + ARGPARSE_LOG(ERR, "usage is NULL!"); + return -EINVAL; + } + + for (idx = 0; idx < RTE_DIM(obj->reserved); idx++) { + if (obj->reserved[idx] != 0) { + ARGPARSE_LOG(ERR, "reserved field must be zero!"); + return -EINVAL; + } + } + + idx = 0; + while (obj->args[idx].name_long != NULL) { + ret = verify_argparse_arg(obj, idx); + if (ret != 0) + return ret; + idx++; + } + + return 0; +} + int rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv) { - (void)obj; + int ret; + (void)argc; (void)argv; + + ret = verify_argparse(obj); + if (ret != 0) + goto error; + return 0; + +error: + if (obj->exit_on_error) + exit(ret); + return ret; } From patchwork Mon Dec 11 09:51:05 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: fengchengwen X-Patchwork-Id: 135009 Return-Path: X-Original-To: patchwork@inbox.dpdk.org Delivered-To: patchwork@inbox.dpdk.org Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id DB158436C8; Mon, 11 Dec 2023 10:54:30 +0100 (CET) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 4DAD340EDF; Mon, 11 Dec 2023 10:54:27 +0100 (CET) Received: from szxga02-in.huawei.com (szxga02-in.huawei.com [45.249.212.188]) by mails.dpdk.org (Postfix) with ESMTP id 38D1240ED3 for ; Mon, 11 Dec 2023 10:54:24 +0100 (CET) Received: from mail.maildlp.com (unknown [172.19.163.174]) by szxga02-in.huawei.com (SkyGuard) with ESMTP id 4SpcXW6hGdzZcmB; Mon, 11 Dec 2023 17:54:19 +0800 (CST) Received: from dggpeml500024.china.huawei.com (unknown [7.185.36.10]) by mail.maildlp.com (Postfix) with ESMTPS id 11B3A14040F; Mon, 11 Dec 2023 17:54:23 +0800 (CST) Received: from localhost.localdomain (10.50.165.33) by dggpeml500024.china.huawei.com (7.185.36.10) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.35; Mon, 11 Dec 2023 17:54:22 +0800 From: Chengwen Feng To: , , , CC: Subject: [RFC v3 07/12] argparse: provide parsing known type API Date: Mon, 11 Dec 2023 09:51:05 +0000 Message-ID: <20231211095110.18946-8-fengchengwen@huawei.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20231211095110.18946-1-fengchengwen@huawei.com> References: <20231121122651.7078-1-fengchengwen@huawei.com> <20231211095110.18946-1-fengchengwen@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.50.165.33] X-ClientProxiedBy: dggems703-chm.china.huawei.com (10.3.19.180) To dggpeml500024.china.huawei.com (7.185.36.10) X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Provide API which could parsing the value from the input string based on the value type. This API could used in user callback when parsing string by argparse or kvargs library. Signed-off-by: Chengwen Feng --- lib/argparse/rte_argparse.c | 19 +++++++++++++++++++ lib/argparse/rte_argparse.h | 19 +++++++++++++++++++ lib/argparse/version.map | 1 + 3 files changed, 39 insertions(+) diff --git a/lib/argparse/rte_argparse.c b/lib/argparse/rte_argparse.c index cc5493c6be..6b82f58eaf 100644 --- a/lib/argparse/rte_argparse.c +++ b/lib/argparse/rte_argparse.c @@ -600,3 +600,22 @@ rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv) exit(ret); return ret; } + +int +rte_argparse_parse_type(const char *str, uint64_t val_type, void *val) +{ + uint32_t cmp_max = RTE_FIELD_GET64(ARG_ATTR_VAL_TYPE_MASK, RTE_ARGPARSE_ARG_VALUE_MAX); + struct rte_argparse_arg arg = { + .name_long = str, + .name_short = NULL, + .val_saver = val, + .val_set = NULL, + .flags = val_type, + }; + uint32_t value_type = arg_attr_val_type(&arg); + + if (value_type == 0 || value_type >= cmp_max) + return -EINVAL; + + return parse_arg_autosave(&arg, str); +} diff --git a/lib/argparse/rte_argparse.h b/lib/argparse/rte_argparse.h index 72eea7cf87..5e40431e5b 100644 --- a/lib/argparse/rte_argparse.h +++ b/lib/argparse/rte_argparse.h @@ -184,6 +184,25 @@ struct rte_argparse { __rte_experimental int rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv); +/** + * @warning + * @b EXPERIMENTAL: this API may change without prior notice. + * + * Parse the value from the input string based on the value type. + * + * @param str + * Input string. + * @param val_type + * The value type, @see RTE_ARGPARSE_ARG_VALUE_INT or other type. + * @param val + * Saver for the value. + * + * @return + * 0 on success. Otherwise negative value is returned. + */ +__rte_experimental +int rte_argparse_parse_type(const char *str, uint64_t val_type, void *val); + #ifdef __cplusplus } #endif diff --git a/lib/argparse/version.map b/lib/argparse/version.map index 1c176f69e9..9b68464600 100644 --- a/lib/argparse/version.map +++ b/lib/argparse/version.map @@ -2,6 +2,7 @@ EXPERIMENTAL { global: rte_argparse_parse; + rte_argparse_parse_type; local: *; };