* [PATCH v1 1/1] app/testpmd: add sleep command
@ 2025-05-02 12:27 Anatoly Burakov
2025-05-02 12:37 ` Bruce Richardson
` (5 more replies)
0 siblings, 6 replies; 23+ messages in thread
From: Anatoly Burakov @ 2025-05-02 12:27 UTC (permalink / raw)
To: dev, Aman Singh
Test-pmd already has a way to run a list of commands from file, but there
is no way to pause execution for a specified amount of time between two
commands. This may be necessary for simple automation, particularly for
waiting on some asynchronous operation such as link status update.
Add a simple sleep command to wait until certain number of milliseconds has
passed.
Signed-off-by: Anatoly Burakov <anatoly.burakov@intel.com>
---
app/test-pmd/cmdline.c | 35 +++++++++++++++++++++++++++++++++++
1 file changed, 35 insertions(+)
diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c
index d4bb3ec998..1e429e6d0a 100644
--- a/app/test-pmd/cmdline.c
+++ b/app/test-pmd/cmdline.c
@@ -151,6 +151,9 @@ static void cmd_help_long_parsed(void *parsed_result,
"quit\n"
" Quit to prompt.\n\n"
+
+ "sleep ms\n"
+ " Sleep for ms milliseconds.\n\n"
);
}
@@ -7768,6 +7771,37 @@ static cmdline_parse_inst_t cmd_quit = {
},
};
+/* *** SLEEP *** */
+struct cmd_sleep_result {
+ cmdline_fixed_string_t sleep;
+ uint32_t ms;
+};
+
+static void cmd_sleep_parsed(void *parsed_result,
+ __rte_unused struct cmdline *cl,
+ __rte_unused void *data)
+{
+ struct cmd_sleep_result *res = parsed_result;
+
+ rte_delay_us_sleep(res->ms * 1000);
+}
+
+static cmdline_parse_token_string_t cmd_sleep_sleep =
+ TOKEN_STRING_INITIALIZER(struct cmd_sleep_result, sleep, "sleep");
+static cmdline_parse_token_num_t cmd_sleep_seconds =
+ TOKEN_NUM_INITIALIZER(struct cmd_sleep_result, ms, RTE_UINT32);
+
+static cmdline_parse_inst_t cmd_sleep = {
+ .f = cmd_sleep_parsed,
+ .data = NULL,
+ .help_str = "sleep <ms>: Sleep for a specified number of milliseconds",
+ .tokens = {
+ (void *)&cmd_sleep_sleep,
+ (void *)&cmd_sleep_seconds,
+ NULL,
+ },
+};
+
/* *** ADD/REMOVE MAC ADDRESS FROM A PORT *** */
struct cmd_mac_addr_result {
cmdline_fixed_string_t mac_addr_cmd;
@@ -13701,6 +13735,7 @@ static cmdline_parse_ctx_t builtin_ctx[] = {
&cmd_help_brief,
&cmd_help_long,
&cmd_quit,
+ &cmd_sleep,
&cmd_load_from_file,
&cmd_showport,
&cmd_showqueue,
--
2.47.1
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v1 1/1] app/testpmd: add sleep command
2025-05-02 12:27 [PATCH v1 1/1] app/testpmd: add sleep command Anatoly Burakov
@ 2025-05-02 12:37 ` Bruce Richardson
2025-05-02 14:35 ` Burakov, Anatoly
2025-05-02 15:42 ` Stephen Hemminger
` (4 subsequent siblings)
5 siblings, 1 reply; 23+ messages in thread
From: Bruce Richardson @ 2025-05-02 12:37 UTC (permalink / raw)
To: Anatoly Burakov; +Cc: dev, Aman Singh
On Fri, May 02, 2025 at 01:27:29PM +0100, Anatoly Burakov wrote:
> Test-pmd already has a way to run a list of commands from file, but there
> is no way to pause execution for a specified amount of time between two
> commands. This may be necessary for simple automation, particularly for
> waiting on some asynchronous operation such as link status update.
>
> Add a simple sleep command to wait until certain number of milliseconds has
> passed.
>
> Signed-off-by: Anatoly Burakov <anatoly.burakov@intel.com>
> ---
> app/test-pmd/cmdline.c | 35 +++++++++++++++++++++++++++++++++++
> 1 file changed, 35 insertions(+)
>
> diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c
> index d4bb3ec998..1e429e6d0a 100644
> --- a/app/test-pmd/cmdline.c
> +++ b/app/test-pmd/cmdline.c
> @@ -151,6 +151,9 @@ static void cmd_help_long_parsed(void *parsed_result,
>
> "quit\n"
> " Quit to prompt.\n\n"
> +
> + "sleep ms\n"
> + " Sleep for ms milliseconds.\n\n"
> );
> }
>
A "sleep" command, I would expect to sleep for a certain number of seconds,
for compatibility e.g. with terminal "sleep" command.
To keep this as "ms" granularity, how about making it explicit as a
"sleep_ms" command. Alternatively, how about adding a usleep command? [Not
sure "msleep" works, which is why I suggested sleep_ms instead]
/Bruce
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v1 1/1] app/testpmd: add sleep command
2025-05-02 12:37 ` Bruce Richardson
@ 2025-05-02 14:35 ` Burakov, Anatoly
2025-05-02 14:43 ` Bruce Richardson
0 siblings, 1 reply; 23+ messages in thread
From: Burakov, Anatoly @ 2025-05-02 14:35 UTC (permalink / raw)
To: Bruce Richardson; +Cc: dev, Aman Singh
On 5/2/2025 2:37 PM, Bruce Richardson wrote:
> On Fri, May 02, 2025 at 01:27:29PM +0100, Anatoly Burakov wrote:
>> Test-pmd already has a way to run a list of commands from file, but there
>> is no way to pause execution for a specified amount of time between two
>> commands. This may be necessary for simple automation, particularly for
>> waiting on some asynchronous operation such as link status update.
>>
>> Add a simple sleep command to wait until certain number of milliseconds has
>> passed.
>>
>> Signed-off-by: Anatoly Burakov <anatoly.burakov@intel.com>
>> ---
>> app/test-pmd/cmdline.c | 35 +++++++++++++++++++++++++++++++++++
>> 1 file changed, 35 insertions(+)
>>
>> diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c
>> index d4bb3ec998..1e429e6d0a 100644
>> --- a/app/test-pmd/cmdline.c
>> +++ b/app/test-pmd/cmdline.c
>> @@ -151,6 +151,9 @@ static void cmd_help_long_parsed(void *parsed_result,
>>
>> "quit\n"
>> " Quit to prompt.\n\n"
>> +
>> + "sleep ms\n"
>> + " Sleep for ms milliseconds.\n\n"
>> );
>> }
>>
>
> A "sleep" command, I would expect to sleep for a certain number of seconds,
> for compatibility e.g. with terminal "sleep" command.
> To keep this as "ms" granularity, how about making it explicit as a
> "sleep_ms" command. Alternatively, how about adding a usleep command? [Not
> sure "msleep" works, which is why I suggested sleep_ms instead]
>
> /Bruce
I have no strong opinions on what it should do. My initial version *was*
a "sleep in seconds" command, I just thought that maybe someone would
want it more flexible. I suspect that actually "sleep" and second
granularity is just fine.
--
Thanks,
Anatoly
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v1 1/1] app/testpmd: add sleep command
2025-05-02 14:35 ` Burakov, Anatoly
@ 2025-05-02 14:43 ` Bruce Richardson
2025-05-02 15:33 ` Morten Brørup
0 siblings, 1 reply; 23+ messages in thread
From: Bruce Richardson @ 2025-05-02 14:43 UTC (permalink / raw)
To: Burakov, Anatoly; +Cc: dev, Aman Singh
On Fri, May 02, 2025 at 04:35:08PM +0200, Burakov, Anatoly wrote:
> On 5/2/2025 2:37 PM, Bruce Richardson wrote:
> > On Fri, May 02, 2025 at 01:27:29PM +0100, Anatoly Burakov wrote:
> > > Test-pmd already has a way to run a list of commands from file, but there
> > > is no way to pause execution for a specified amount of time between two
> > > commands. This may be necessary for simple automation, particularly for
> > > waiting on some asynchronous operation such as link status update.
> > >
> > > Add a simple sleep command to wait until certain number of milliseconds has
> > > passed.
> > >
> > > Signed-off-by: Anatoly Burakov <anatoly.burakov@intel.com>
> > > ---
> > > app/test-pmd/cmdline.c | 35 +++++++++++++++++++++++++++++++++++
> > > 1 file changed, 35 insertions(+)
> > >
> > > diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c
> > > index d4bb3ec998..1e429e6d0a 100644
> > > --- a/app/test-pmd/cmdline.c
> > > +++ b/app/test-pmd/cmdline.c
> > > @@ -151,6 +151,9 @@ static void cmd_help_long_parsed(void *parsed_result,
> > > "quit\n"
> > > " Quit to prompt.\n\n"
> > > +
> > > + "sleep ms\n"
> > > + " Sleep for ms milliseconds.\n\n"
> > > );
> > > }
> >
> > A "sleep" command, I would expect to sleep for a certain number of seconds,
> > for compatibility e.g. with terminal "sleep" command.
> > To keep this as "ms" granularity, how about making it explicit as a
> > "sleep_ms" command. Alternatively, how about adding a usleep command? [Not
> > sure "msleep" works, which is why I suggested sleep_ms instead]
> >
> > /Bruce
>
> I have no strong opinions on what it should do. My initial version *was* a
> "sleep in seconds" command, I just thought that maybe someone would want it
> more flexible. I suspect that actually "sleep" and second granularity is
> just fine.
>
I think we do need to support sub-second sleep granularity, though. If we
only add "sleep" with time specified in seconds, I think we'd also need to
add in usleep with micro-sec granularity too.
Other alternatives:
- have sleep take an option 3rd parameter of time in
minisecond. So "sleep 1" is to sleep for one second, but "sleep 0 500" is
to sleep for 1/2 sec, and "sleep 2 750" is to sleep for 2.75 seconds.
- add floating point support to the cmdline library, and then allow sleep time
specified in seconds using that.
/Bruce
^ permalink raw reply [flat|nested] 23+ messages in thread
* RE: [PATCH v1 1/1] app/testpmd: add sleep command
2025-05-02 14:43 ` Bruce Richardson
@ 2025-05-02 15:33 ` Morten Brørup
0 siblings, 0 replies; 23+ messages in thread
From: Morten Brørup @ 2025-05-02 15:33 UTC (permalink / raw)
To: Bruce Richardson, Burakov, Anatoly; +Cc: dev, Aman Singh
> From: Bruce Richardson [mailto:bruce.richardson@intel.com]
> Sent: Friday, 2 May 2025 16.44
>
> On Fri, May 02, 2025 at 04:35:08PM +0200, Burakov, Anatoly wrote:
> > On 5/2/2025 2:37 PM, Bruce Richardson wrote:
> > > On Fri, May 02, 2025 at 01:27:29PM +0100, Anatoly Burakov wrote:
> > > > Test-pmd already has a way to run a list of commands from file,
> but there
> > > > is no way to pause execution for a specified amount of time
> between two
> > > > commands. This may be necessary for simple automation,
> particularly for
> > > > waiting on some asynchronous operation such as link status
> update.
> > > >
> > > > Add a simple sleep command to wait until certain number of
> milliseconds has
> > > > passed.
> > > >
> > > > Signed-off-by: Anatoly Burakov <anatoly.burakov@intel.com>
> > > > ---
> > > > app/test-pmd/cmdline.c | 35 +++++++++++++++++++++++++++++++++++
> > > > 1 file changed, 35 insertions(+)
> > > >
> > > > diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c
> > > > index d4bb3ec998..1e429e6d0a 100644
> > > > --- a/app/test-pmd/cmdline.c
> > > > +++ b/app/test-pmd/cmdline.c
> > > > @@ -151,6 +151,9 @@ static void cmd_help_long_parsed(void
> *parsed_result,
> > > > "quit\n"
> > > > " Quit to prompt.\n\n"
> > > > +
> > > > + "sleep ms\n"
> > > > + " Sleep for ms milliseconds.\n\n"
> > > > );
> > > > }
> > >
> > > A "sleep" command, I would expect to sleep for a certain number of
> seconds,
> > > for compatibility e.g. with terminal "sleep" command.
> > > To keep this as "ms" granularity, how about making it explicit as a
> > > "sleep_ms" command. Alternatively, how about adding a usleep
> command? [Not
> > > sure "msleep" works, which is why I suggested sleep_ms instead]
> > >
> > > /Bruce
> >
> > I have no strong opinions on what it should do. My initial version
> *was* a
> > "sleep in seconds" command, I just thought that maybe someone would
> want it
> > more flexible. I suspect that actually "sleep" and second granularity
> is
> > just fine.
> >
>
> I think we do need to support sub-second sleep granularity, though. If
> we
> only add "sleep" with time specified in seconds, I think we'd also need
> to
> add in usleep with micro-sec granularity too.
>
> Other alternatives:
> - have sleep take an option 3rd parameter of time in
> minisecond. So "sleep 1" is to sleep for one second, but "sleep 0
> 500" is
> to sleep for 1/2 sec, and "sleep 2 750" is to sleep for 2.75 seconds.
> - add floating point support to the cmdline library, and then allow
> sleep time
> specified in seconds using that.
>
> /Bruce
"sleep" should take one parameter: seconds.
It can be float, or a separate "usleep" command taking a microseconds parameter can be added. This change (using float instead of int) or addition (usleep command) can be added later, if you like.
Initially, I would lean towards a float parameter, but maybe it's easier for scripts to use "usleep" with an integer.
In real life, the duration will probably be either "N" (i.e. a natural number) or "0.N" (i.e. less than one second), so an "usleep" might be just as good.
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v1 1/1] app/testpmd: add sleep command
2025-05-02 12:27 [PATCH v1 1/1] app/testpmd: add sleep command Anatoly Burakov
2025-05-02 12:37 ` Bruce Richardson
@ 2025-05-02 15:42 ` Stephen Hemminger
2025-05-06 12:36 ` Burakov, Anatoly
2025-05-06 13:08 ` [PATCH v2 1/2] cmdline: add floating point support Anatoly Burakov
` (3 subsequent siblings)
5 siblings, 1 reply; 23+ messages in thread
From: Stephen Hemminger @ 2025-05-02 15:42 UTC (permalink / raw)
To: Anatoly Burakov; +Cc: dev, Aman Singh
On Fri, 2 May 2025 13:27:29 +0100
Anatoly Burakov <anatoly.burakov@intel.com> wrote:
> @@ -13701,6 +13735,7 @@ static cmdline_parse_ctx_t builtin_ctx[] = {
> &cmd_help_brief,
> &cmd_help_long,
> &cmd_quit,
> + &cmd_sleep,
> &cmd_load_from_file,
> &cmd_showport,
> &cmd_showqueue,
> --
Looks like original list was in alphabetic order.
Please preserve that
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v1 1/1] app/testpmd: add sleep command
2025-05-02 15:42 ` Stephen Hemminger
@ 2025-05-06 12:36 ` Burakov, Anatoly
0 siblings, 0 replies; 23+ messages in thread
From: Burakov, Anatoly @ 2025-05-06 12:36 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: dev, Aman Singh
On 5/2/2025 5:42 PM, Stephen Hemminger wrote:
> On Fri, 2 May 2025 13:27:29 +0100
> Anatoly Burakov <anatoly.burakov@intel.com> wrote:
>
>> @@ -13701,6 +13735,7 @@ static cmdline_parse_ctx_t builtin_ctx[] = {
>> &cmd_help_brief,
>> &cmd_help_long,
>> &cmd_quit,
>> + &cmd_sleep,
>> &cmd_load_from_file,
>> &cmd_showport,
>> &cmd_showqueue,
>> --
>
> Looks like original list was in alphabetic order.
> Please preserve that
It really wasn't, but I've moved it further down the list to reflect
help message ordering.
--
Thanks,
Anatoly
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH v2 1/2] cmdline: add floating point support
2025-05-02 12:27 [PATCH v1 1/1] app/testpmd: add sleep command Anatoly Burakov
2025-05-02 12:37 ` Bruce Richardson
2025-05-02 15:42 ` Stephen Hemminger
@ 2025-05-06 13:08 ` Anatoly Burakov
2025-05-06 13:08 ` [PATCH v2 2/2] app/testpmd: add sleep command Anatoly Burakov
2025-05-06 13:38 ` [PATCH v2 1/2] cmdline: add floating point support Bruce Richardson
2025-05-07 9:50 ` [PATCH v3 " Anatoly Burakov
` (2 subsequent siblings)
5 siblings, 2 replies; 23+ messages in thread
From: Anatoly Burakov @ 2025-05-06 13:08 UTC (permalink / raw)
To: dev
Add support for parsing floating point numbers in cmdline library, as well
as unit tests for the new functionality. The parser supports single and
double precision floats, and will understand decimal fractions as well as
scientific notation.
Signed-off-by: Anatoly Burakov <anatoly.burakov@intel.com>
---
app/test/test_cmdline_num.c | 201 +++++++++++++++++++++++-
lib/cmdline/cmdline_parse_num.c | 261 ++++++++++++++++++++++++++++++++
lib/cmdline/cmdline_parse_num.h | 4 +-
3 files changed, 462 insertions(+), 4 deletions(-)
diff --git a/app/test/test_cmdline_num.c b/app/test/test_cmdline_num.c
index 9276de59bd..827ac06d9b 100644
--- a/app/test/test_cmdline_num.c
+++ b/app/test/test_cmdline_num.c
@@ -5,6 +5,8 @@
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
+#include <float.h>
+#include <math.h>
#include <rte_string_fns.h>
@@ -23,6 +25,11 @@ struct num_signed_str {
int64_t result;
};
+struct num_float_str {
+ const char * str;
+ double result;
+};
+
const struct num_unsigned_str num_valid_positive_strs[] = {
/* decimal positive */
{"0", 0 },
@@ -141,6 +148,63 @@ const struct num_signed_str num_valid_negative_strs[] = {
{"-9223372036854775808", INT64_MIN },
};
+const struct num_float_str num_valid_float_strs[] = {
+ /* zero */
+ {"0", 0},
+ /* parse int as float */
+ {"1", 1},
+ {"-1", -1},
+ /* fractional */
+ {"1.23", 1.23},
+ {"-1.23", -1.23},
+ {"0.123", 0.123},
+ {"-0.123", -0.123},
+ {"123.456", 123.456},
+ {"-123.456", -123.456},
+ /* positive exponent */
+ {"1e2", 1e2},
+ {"-1e2", -1e2},
+ {"1E2", 1E2},
+ {"-1E2", -1E2},
+ {"0.12e3", 0.12e3},
+ {"-0.12e3", -0.12e3},
+ {"1.23e4", 1.23e4},
+ {"-1.23e4", -1.23e4},
+ {"1.23E4", 1.23E4},
+ {"-1.23E4", -1.23E4},
+ {"123.456e7", 123.456e7},
+ {"-123.456e7", -123.456e7},
+ {"123.456E7", 123.456E7},
+ {"-123.456E7", -123.456E7},
+ /* negative exponent */
+ {"1e-2", 1e-2},
+ {"-1e-2", -1e-2},
+ {"1E-2", 1E-2},
+ {"-1E-2", -1E-2},
+ {"0.12e-3", 0.12e-3},
+ {"-0.12e-3", -0.12e-3},
+ {"1.23e-4", 1.23e-4},
+ {"-1.23e-4", -1.23e-4},
+ {"1.23E-4", 1.23E-4},
+ {"-1.23E-4", -1.23E-4},
+ {"123.456e-7", 123.456e-7},
+ {"-123.456e-7", -123.456e-7},
+ {"123.456E-7", 123.456E-7},
+ {"-123.456E-7", -123.456E-7},
+ /* try overflowing float */
+ {"2e63", 2e63},
+ {"-2e63", -2e63},
+ {"2E63", 2E63},
+ {"-2E63", -2E63},
+ {"18446744073709551615", (double) UINT64_MAX},
+ {"-9223372036854775808", (double) INT64_MIN},
+ /* try overflowing double */
+ {"2e308", HUGE_VAL},
+ {"-2e308", -HUGE_VAL},
+ {"2E308", HUGE_VAL},
+ {"-2E308", HUGE_VAL},
+};
+
const struct num_unsigned_str num_garbage_positive_strs[] = {
/* valid strings with garbage on the end, should still be valid */
/* decimal */
@@ -183,6 +247,28 @@ const struct num_signed_str num_garbage_negative_strs[] = {
{"-9223372036854775808 garbage", INT64_MIN },
};
+const char *float_invalid_strs[] = {
+ "0.",
+ ".1",
+ "1.1.",
+ "1.1.1",
+ "-0.",
+ "-.1",
+ "-1.1.",
+ "-1.1.1",
+ "e",
+ "1e",
+ "-1e",
+ "0.1e",
+ "-0.1e",
+ "1.e",
+ "-1.e",
+ "1.23e3.4",
+ "-1.23e3.4",
+ "1e1e",
+ "1e1e1"
+};
+
const char * num_invalid_strs[] = {
"18446744073709551616", /* out of range unsigned */
"-9223372036854775809", /* out of range negative signed */
@@ -202,7 +288,16 @@ const char * num_invalid_strs[] = {
/* too long (128+ chars) */
("0b1111000011110000111100001111000011110000111100001111000011110000"
"1111000011110000111100001111000011110000111100001111000011110000"),
+ /* valid float values but should fail to parse as ints */
"1E3",
+ "-1E3",
+ "1.23",
+ "-1.23",
+ "1E-3",
+ "-1E-3",
+ "1.23E4",
+ "-1.23E4",
+ /* misc invalid values */
"0A",
"-B",
"+4",
@@ -216,6 +311,47 @@ const char * num_invalid_strs[] = {
"\0",
};
+static int
+float_cmp(double expected, void *actual_p, enum cmdline_numtype type)
+{
+ double eps;
+ double actual_d;
+
+ if (type == RTE_FLOAT_SINGLE) {
+ /* read as float, convert to double */
+ actual_d = (double)*(float *)actual_p;
+ /* FLT_EPSILON is too small for some tests */
+ eps = 1e-5f;
+ } else {
+ /* read as double */
+ actual_d = *(double *)actual_p;
+ eps = DBL_EPSILON;
+ }
+ /* compare using epsilon value */
+ if (fabs(expected - actual_d) < eps)
+ return 0;
+ /* not equal */
+ return expected < actual_d ? -1 : 1;
+}
+
+static int
+can_parse_float(double expected_result, enum cmdline_numtype type)
+{
+ switch (type) {
+ case RTE_FLOAT_SINGLE:
+ if (expected_result > FLT_MAX || expected_result < -FLT_MAX)
+ return 0;
+ break;
+ case RTE_FLOAT_DOUBLE:
+ if (expected_result > DBL_MAX || expected_result < -DBL_MAX)
+ return 0;
+ break;
+ default:
+ return 1;
+ }
+ return 1;
+}
+
static int
can_parse_unsigned(uint64_t expected_result, enum cmdline_numtype type)
{
@@ -371,11 +507,11 @@ test_parse_num_invalid_data(void)
int ret = 0;
unsigned i;
char buf[CMDLINE_TEST_BUFSIZE];
- uint64_t result; /* pick largest buffer */
cmdline_parse_token_num_t token;
- /* cycle through all possible parsed types */
+ /* cycle through all possible integer types */
for (type = RTE_UINT8; type <= RTE_INT64; type++) {
+ uint64_t result; /* pick largest buffer */
token.num_data.type = type;
/* test full strings */
@@ -397,6 +533,31 @@ test_parse_num_invalid_data(void)
}
}
}
+
+ /* cycle through all possible float types */
+ for (type = RTE_FLOAT_SINGLE; type <= RTE_FLOAT_DOUBLE; type++) {
+ double result; /* pick largest buffer */
+ token.num_data.type = type;
+
+ /* test full strings */
+ for (i = 0; i < RTE_DIM(float_invalid_strs); i++) {
+
+ memset(&result, 0, sizeof(double));
+ memset(&buf, 0, sizeof(buf));
+
+ ret = cmdline_parse_num((cmdline_parse_token_hdr_t*)&token,
+ float_invalid_strs[i], (void*)&result, sizeof(result));
+ if (ret != -1) {
+ /* get some info about what we are trying to parse */
+ cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token,
+ buf, sizeof(buf));
+
+ printf("Error: parsing %s as %s succeeded!\n",
+ float_invalid_strs[i], buf);
+ return -1;
+ }
+ }
+ }
return 0;
}
@@ -408,13 +569,13 @@ test_parse_num_valid(void)
enum cmdline_numtype type;
unsigned i;
char buf[CMDLINE_TEST_BUFSIZE];
- uint64_t result;
cmdline_parse_token_num_t token;
/** valid strings **/
/* cycle through all possible parsed types */
for (type = RTE_UINT8; type <= RTE_INT64; type++) {
+ uint64_t result;
token.num_data.type = type;
/* test positive strings */
@@ -489,10 +650,44 @@ test_parse_num_valid(void)
}
}
+ /* float values */
+ for (type = RTE_FLOAT_SINGLE; type <= RTE_FLOAT_DOUBLE; type++) {
+ double result;
+ token.num_data.type = type;
+
+ /* test all valid strings */
+ for (i = 0; i < RTE_DIM(num_valid_float_strs); i++) {
+ result = 0;
+ memset(&buf, 0, sizeof(buf));
+
+
+ cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token,
+ buf, sizeof(buf));
+
+ ret = cmdline_parse_num((cmdline_parse_token_hdr_t*) &token,
+ num_valid_float_strs[i].str,
+ (void*)&result, sizeof(result));
+
+ /* if it should have passed but didn't, or if it should have failed but didn't */
+ if ((ret < 0) == (can_parse_float(num_valid_float_strs[i].result, type) > 0)) {
+ printf("Error: parser behaves unexpectedly when parsing %s as %s!\n",
+ num_valid_float_strs[i].str, buf);
+ return -1;
+ }
+ /* check if result matches */
+ if (ret > 0 && float_cmp(num_valid_float_strs[i].result, &result, type) != 0) {
+ printf("Error: parsing %s as %s failed: result mismatch!\n",
+ num_valid_float_strs[i].str, buf);
+ return -1;
+ }
+ }
+ }
+
/** garbage strings **/
/* cycle through all possible parsed types */
for (type = RTE_UINT8; type <= RTE_INT64; type++) {
+ uint64_t result;
token.num_data.type = type;
/* test positive garbage strings */
diff --git a/lib/cmdline/cmdline_parse_num.c b/lib/cmdline/cmdline_parse_num.c
index f21796bedb..1799d10ce5 100644
--- a/lib/cmdline/cmdline_parse_num.c
+++ b/lib/cmdline/cmdline_parse_num.c
@@ -7,6 +7,8 @@
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
+#include <float.h>
+#include <math.h>
#include <string.h>
#include <eal_export.h>
#include <rte_string_fns.h>
@@ -34,6 +36,9 @@ enum num_parse_state_t {
DEC_NEG,
BIN,
HEX,
+ FLOAT_POS,
+ FLOAT_NEG,
+ FLOAT_EXP,
ERROR,
@@ -44,12 +49,27 @@ enum num_parse_state_t {
BIN_OK,
DEC_NEG_OK,
DEC_POS_OK,
+ FLOAT_POS_OK,
+ FLOAT_NEG_OK,
+ FLOAT_EXP_POS_OK,
+ FLOAT_EXP_NEG_OK,
+};
+
+struct float_parse_state {
+ uint64_t dec;
+ uint64_t frac;
+ uint64_t frac_exp;
+ uint64_t exp;
+#define FLOAT_FLAG_NEG_RES (1 << 0)
+#define FLOAT_FLAG_NEG_EXP (1 << 1)
+ int flags;
};
/* Keep it sync with enum in .h */
static const char * num_help[] = {
"UINT8", "UINT16", "UINT32", "UINT64",
"INT8", "INT16", "INT32", "INT64",
+ "SINGLE", "DOUBLE"
};
static inline int
@@ -63,6 +83,50 @@ add_to_res(unsigned int c, uint64_t *res, unsigned int base)
return 0;
}
+static inline int
+check_float_result(enum cmdline_numtype res_type, struct float_parse_state *fps,
+ void *res)
+{
+ double dec, frac, exp, result;
+
+ /* extract parts */
+ dec = (double) fps->dec;
+ frac = (double) fps->frac * pow(10.0, -(double)fps->frac_exp);
+ exp = (double) fps->exp;
+
+ /* exponent might be negative */
+ if (fps->flags & FLOAT_FLAG_NEG_EXP)
+ exp = pow(10.0, -exp);
+ else
+ exp = pow(10.0, exp);
+
+ /* combine decimal, fractional, and exponent parts */
+ result = (dec + frac) * exp;
+
+ /* check for any overflows */
+ if (isinf(frac) || isinf(exp) || isinf(result))
+ return -1;
+
+ /* result is a valid double */
+
+ /* check if result needs to be negative */
+ if (fps->flags & FLOAT_FLAG_NEG_RES)
+ result = -result;
+
+ if (res_type == RTE_FLOAT_SINGLE) {
+ /* float can overflow from conversion */
+ float flt = (float)result;
+ if (isinf(flt))
+ return -1;
+ if (res) *(float *)res = flt;
+ } else if (res_type == RTE_FLOAT_DOUBLE) {
+ if (res) *(double *)res = result;
+ } else {
+ return -1;
+ }
+ return 0;
+}
+
static int
check_res_size(struct cmdline_token_num_data *nd, unsigned ressize)
{
@@ -87,6 +151,14 @@ check_res_size(struct cmdline_token_num_data *nd, unsigned ressize)
if (ressize < sizeof(int64_t))
return -1;
break;
+ case RTE_FLOAT_SINGLE:
+ if (ressize < sizeof(float))
+ return -1;
+ break;
+ case RTE_FLOAT_DOUBLE:
+ if (ressize < sizeof(double))
+ return -1;
+ break;
default:
return -1;
}
@@ -104,6 +176,7 @@ cmdline_parse_num(cmdline_parse_token_hdr_t *tk, const char *srcbuf, void *res,
const char * buf;
char c;
uint64_t res1 = 0;
+ struct float_parse_state fps = {};
if (!tk)
return -1;
@@ -156,6 +229,10 @@ cmdline_parse_num(cmdline_parse_token_hdr_t *tk, const char *srcbuf, void *res,
else
st = OCTAL_OK;
}
+ else if (c == '.') {
+ st = FLOAT_POS;
+ break;
+ }
else {
st = ERROR;
}
@@ -173,11 +250,68 @@ cmdline_parse_num(cmdline_parse_token_hdr_t *tk, const char *srcbuf, void *res,
}
break;
+ case FLOAT_POS:
+ if (c >= '0' && c <= '9') {
+ if (add_to_res(c - '0', &res1, 10) < 0)
+ st = ERROR;
+ else {
+ st = FLOAT_POS_OK;
+ fps.frac_exp++;
+ }
+ }
+ else {
+ st = ERROR;
+ }
+ break;
+
+ case FLOAT_NEG:
+ if (c >= '0' && c <= '9') {
+ if (add_to_res(c - '0', &res1, 10) < 0)
+ st = ERROR;
+ else {
+ st = FLOAT_NEG_OK;
+ fps.frac_exp++;
+ }
+ }
+ else {
+ st = ERROR;
+ }
+ break;
+
+ case FLOAT_EXP:
+ if (c >= '0' && c <= '9') {
+ if (add_to_res(c - '0', &res1, 10) < 0)
+ st = ERROR;
+ else
+ st = FLOAT_EXP_POS_OK;
+ }
+ else if (c == '-') {
+ st = FLOAT_EXP_NEG_OK;
+ fps.flags |= FLOAT_FLAG_NEG_EXP;
+ }
+ else {
+ st = ERROR;
+ }
+ break;
+
case DEC_NEG_OK:
if (c >= '0' && c <= '9') {
if (add_to_res(c - '0', &res1, 10) < 0)
st = ERROR;
}
+ else if (c == '.') {
+ fps.dec = res1;
+ fps.flags |= FLOAT_FLAG_NEG_RES;
+ st = FLOAT_NEG;
+ /* erase result */
+ res1 = 0;
+ } else if (c == 'e' || c == 'E') {
+ fps.dec = res1;
+ fps.flags |= FLOAT_FLAG_NEG_RES;
+ st = FLOAT_EXP;
+ /* erase result */
+ res1 = 0;
+ }
else {
st = ERROR;
}
@@ -188,11 +322,75 @@ cmdline_parse_num(cmdline_parse_token_hdr_t *tk, const char *srcbuf, void *res,
if (add_to_res(c - '0', &res1, 10) < 0)
st = ERROR;
}
+ else if (c == '.') {
+ fps.dec = res1;
+ st = FLOAT_POS;
+ /* erase result */
+ res1 = 0;
+ }
+ else if (c == 'e' || c == 'E') {
+ fps.dec = res1;
+ st = FLOAT_EXP;
+ /* erase result */
+ res1 = 0;
+ }
else {
st = ERROR;
}
break;
+ case FLOAT_POS_OK:
+ if (c >= '0' && c <= '9') {
+ if (add_to_res(c - '0', &res1, 10) < 0)
+ st = ERROR;
+ else
+ fps.frac_exp++;
+ } else if (c == 'e' || c == 'E') {
+ fps.frac = res1;
+ st = FLOAT_EXP;
+ /* erase result */
+ res1 = 0;
+ } else {
+ st = ERROR;
+ }
+ break;
+
+ case FLOAT_NEG_OK:
+ if (c >= '0' && c <= '9') {
+ if (add_to_res(c - '0', &res1, 10) < 0)
+ st = ERROR;
+ else
+ fps.frac_exp++;
+ } else if (c == 'e' || c == 'E') {
+ fps.frac = res1;
+ st = FLOAT_EXP;
+ /* erase result */
+ res1 = 0;
+ } else {
+ st = ERROR;
+ }
+ break;
+
+ case FLOAT_EXP_POS_OK:
+ /* exponent is always whole */
+ if (c >= '0' && c <= '9') {
+ if (add_to_res(c - '0', &res1, 10) < 0)
+ st = ERROR;
+ } else {
+ st = ERROR;
+ }
+ break;
+
+ case FLOAT_EXP_NEG_OK:
+ /* exponent is always whole */
+ if (c >= '0' && c <= '9') {
+ if (add_to_res(c - '0', &res1, 10) < 0)
+ st = ERROR;
+ } else {
+ st = ERROR;
+ }
+ break;
+
case HEX:
st = HEX_OK;
/* fall-through */
@@ -282,6 +480,12 @@ cmdline_parse_num(cmdline_parse_token_hdr_t *tk, const char *srcbuf, void *res,
} else if (nd.type == RTE_UINT64) {
if (res) *(uint64_t *)res = res1;
return buf-srcbuf;
+ } else if (nd.type == RTE_FLOAT_SINGLE || nd.type == RTE_FLOAT_DOUBLE) {
+ /* parsed double from integer */
+ fps.dec = res1;
+ if (check_float_result(nd.type, &fps, res) < 0)
+ return -1;
+ return buf-srcbuf;
} else {
return -1;
}
@@ -304,6 +508,63 @@ cmdline_parse_num(cmdline_parse_token_hdr_t *tk, const char *srcbuf, void *res,
res1 <= (uint64_t)INT64_MAX + 1) {
if (res) *(int64_t *)res = (int64_t) (-res1);
return buf-srcbuf;
+ } else if ((nd.type == RTE_FLOAT_SINGLE || nd.type == RTE_FLOAT_DOUBLE) &&
+ res1 <= (uint64_t)INT64_MAX + 1) {
+ /* parsed double from negative integer */
+ fps.dec = res1;
+ fps.flags |= FLOAT_FLAG_NEG_RES;
+ if (check_float_result(nd.type, &fps, res) < 0)
+ return -1;
+ return buf-srcbuf;
+ } else {
+ return -1;
+ }
+ break;
+
+ case FLOAT_POS_OK:
+ if (nd.type == RTE_FLOAT_SINGLE || nd.type == RTE_FLOAT_DOUBLE) {
+ fps.frac = res1;
+
+ if (check_float_result(nd.type, &fps, res) < 0)
+ return -1;
+ return buf-srcbuf;
+ } else {
+ return -1;
+ }
+ break;
+
+ case FLOAT_NEG_OK:
+ if (nd.type == RTE_FLOAT_SINGLE || nd.type == RTE_FLOAT_DOUBLE) {
+ fps.frac = res1;
+
+ if (check_float_result(nd.type, &fps, res) < 0)
+ return -1;
+ return buf-srcbuf;
+ } else {
+ return -1;
+ }
+ break;
+
+ case FLOAT_EXP_POS_OK:
+ /* watch for overflow in the exponent */
+ if (nd.type == RTE_FLOAT_SINGLE || nd.type == RTE_FLOAT_DOUBLE) {
+ fps.exp = res1;
+
+ if (check_float_result(nd.type, &fps, res) < 0)
+ return -1;
+ return buf-srcbuf;
+ } else {
+ return -1;
+ }
+ break;
+
+ case FLOAT_EXP_NEG_OK:
+ if (nd.type == RTE_FLOAT_SINGLE || nd.type == RTE_FLOAT_DOUBLE) {
+ fps.exp = res1;
+
+ if (check_float_result(nd.type, &fps, res) < 0)
+ return -1;
+ return buf-srcbuf;
} else {
return -1;
}
diff --git a/lib/cmdline/cmdline_parse_num.h b/lib/cmdline/cmdline_parse_num.h
index bdd0267612..b2792a2d11 100644
--- a/lib/cmdline/cmdline_parse_num.h
+++ b/lib/cmdline/cmdline_parse_num.h
@@ -22,7 +22,9 @@ enum cmdline_numtype {
RTE_INT8,
RTE_INT16,
RTE_INT32,
- RTE_INT64
+ RTE_INT64,
+ RTE_FLOAT_SINGLE,
+ RTE_FLOAT_DOUBLE,
};
struct cmdline_token_num_data {
--
2.47.1
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH v2 2/2] app/testpmd: add sleep command
2025-05-06 13:08 ` [PATCH v2 1/2] cmdline: add floating point support Anatoly Burakov
@ 2025-05-06 13:08 ` Anatoly Burakov
2025-05-06 13:38 ` [PATCH v2 1/2] cmdline: add floating point support Bruce Richardson
1 sibling, 0 replies; 23+ messages in thread
From: Anatoly Burakov @ 2025-05-06 13:08 UTC (permalink / raw)
To: dev, Aman Singh
Test-pmd already has a way to run a list of commands from file, but there
is no way to pause execution for a specified amount of time between two
commands. This may be necessary for simple automation, particularly for
waiting on some asynchronous operation such as link status update.
Add a simple sleep command to wait until certain number of seconds has
passed, using the newly added cmdline library floating point support.
Signed-off-by: Anatoly Burakov <anatoly.burakov@intel.com>
---
Notes:
v1 -> v2:
- Add floating point support to cmdline
- Use floating point format for pause command
app/test-pmd/cmdline.c | 35 +++++++++++++++++++++++++++++++++++
1 file changed, 35 insertions(+)
diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c
index d4bb3ec998..b6152c07e6 100644
--- a/app/test-pmd/cmdline.c
+++ b/app/test-pmd/cmdline.c
@@ -151,6 +151,9 @@ static void cmd_help_long_parsed(void *parsed_result,
"quit\n"
" Quit to prompt.\n\n"
+
+ "sleep secs\n"
+ " Sleep for secs seconds (can be fractional).\n\n"
);
}
@@ -7768,6 +7771,37 @@ static cmdline_parse_inst_t cmd_quit = {
},
};
+/* *** SLEEP *** */
+struct cmd_sleep_result {
+ cmdline_fixed_string_t sleep;
+ double secs;
+};
+
+static void cmd_sleep_parsed(void *parsed_result,
+ __rte_unused struct cmdline *cl,
+ __rte_unused void *data)
+{
+ struct cmd_sleep_result *res = parsed_result;
+
+ rte_delay_us_sleep(res->secs * 1E6);
+}
+
+static cmdline_parse_token_string_t cmd_sleep_sleep =
+ TOKEN_STRING_INITIALIZER(struct cmd_sleep_result, sleep, "sleep");
+static cmdline_parse_token_num_t cmd_sleep_seconds =
+ TOKEN_NUM_INITIALIZER(struct cmd_sleep_result, secs, RTE_FLOAT_DOUBLE);
+
+static cmdline_parse_inst_t cmd_sleep = {
+ .f = cmd_sleep_parsed,
+ .data = NULL,
+ .help_str = "sleep <secs>: Sleep for a specified number of seconds",
+ .tokens = {
+ (void *)&cmd_sleep_sleep,
+ (void *)&cmd_sleep_seconds,
+ NULL,
+ },
+};
+
/* *** ADD/REMOVE MAC ADDRESS FROM A PORT *** */
struct cmd_mac_addr_result {
cmdline_fixed_string_t mac_addr_cmd;
@@ -13711,6 +13745,7 @@ static cmdline_parse_ctx_t builtin_ctx[] = {
&cmd_showdevice,
&cmd_showcfg,
&cmd_showfwdall,
+ &cmd_sleep,
&cmd_start,
&cmd_start_tx_first,
&cmd_start_tx_first_n,
--
2.47.1
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 1/2] cmdline: add floating point support
2025-05-06 13:08 ` [PATCH v2 1/2] cmdline: add floating point support Anatoly Burakov
2025-05-06 13:08 ` [PATCH v2 2/2] app/testpmd: add sleep command Anatoly Burakov
@ 2025-05-06 13:38 ` Bruce Richardson
2025-05-07 9:02 ` Burakov, Anatoly
1 sibling, 1 reply; 23+ messages in thread
From: Bruce Richardson @ 2025-05-06 13:38 UTC (permalink / raw)
To: Anatoly Burakov; +Cc: dev
On Tue, May 06, 2025 at 02:08:18PM +0100, Anatoly Burakov wrote:
> Add support for parsing floating point numbers in cmdline library, as well
> as unit tests for the new functionality. The parser supports single and
> double precision floats, and will understand decimal fractions as well as
> scientific notation.
>
> Signed-off-by: Anatoly Burakov <anatoly.burakov@intel.com>
> ---
> app/test/test_cmdline_num.c | 201 +++++++++++++++++++++++-
> lib/cmdline/cmdline_parse_num.c | 261 ++++++++++++++++++++++++++++++++
> lib/cmdline/cmdline_parse_num.h | 4 +-
> 3 files changed, 462 insertions(+), 4 deletions(-)
>
This is great to see, thanks. Can you also look to add support to
buildtools/dpdk-cmdline-gen.py script, to make it possible to use this from
a cmdline list file as with many of our sample apps?
/Bruce
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 1/2] cmdline: add floating point support
2025-05-06 13:38 ` [PATCH v2 1/2] cmdline: add floating point support Bruce Richardson
@ 2025-05-07 9:02 ` Burakov, Anatoly
0 siblings, 0 replies; 23+ messages in thread
From: Burakov, Anatoly @ 2025-05-07 9:02 UTC (permalink / raw)
To: Bruce Richardson; +Cc: dev
On 5/6/2025 3:38 PM, Bruce Richardson wrote:
> On Tue, May 06, 2025 at 02:08:18PM +0100, Anatoly Burakov wrote:
>> Add support for parsing floating point numbers in cmdline library, as well
>> as unit tests for the new functionality. The parser supports single and
>> double precision floats, and will understand decimal fractions as well as
>> scientific notation.
>>
>> Signed-off-by: Anatoly Burakov <anatoly.burakov@intel.com>
>> ---
>> app/test/test_cmdline_num.c | 201 +++++++++++++++++++++++-
>> lib/cmdline/cmdline_parse_num.c | 261 ++++++++++++++++++++++++++++++++
>> lib/cmdline/cmdline_parse_num.h | 4 +-
>> 3 files changed, 462 insertions(+), 4 deletions(-)
>>
> This is great to see, thanks. Can you also look to add support to
> buildtools/dpdk-cmdline-gen.py script, to make it possible to use this from
> a cmdline list file as with many of our sample apps?
>
> /Bruce
Good call, will do!
(also probably needs a doc update somewhere, so i'll see if I can add
that too)
--
Thanks,
Anatoly
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH v3 1/2] cmdline: add floating point support
2025-05-02 12:27 [PATCH v1 1/1] app/testpmd: add sleep command Anatoly Burakov
` (2 preceding siblings ...)
2025-05-06 13:08 ` [PATCH v2 1/2] cmdline: add floating point support Anatoly Burakov
@ 2025-05-07 9:50 ` Anatoly Burakov
2025-05-07 9:50 ` [PATCH v3 2/2] app/testpmd: add sleep command Anatoly Burakov
2025-05-07 9:53 ` [PATCH v3 1/2] cmdline: add floating point support Burakov, Anatoly
2025-05-07 10:01 ` [PATCH v4 " Anatoly Burakov
2025-05-07 15:22 ` [PATCH v5 1/3] cmdline: use C standard library as number parser Anatoly Burakov
5 siblings, 2 replies; 23+ messages in thread
From: Anatoly Burakov @ 2025-05-07 9:50 UTC (permalink / raw)
To: dev
Add support for parsing floating point numbers in cmdline library, as well
as unit tests for the new functionality. The parser supports single and
double precision floats, and will understand decimal fractions as well as
scientific notation.
Signed-off-by: Anatoly Burakov <anatoly.burakov@intel.com>
---
app/test/test_cmdline_num.c | 203 +++++++++++++++++-
buildtools/dpdk-cmdline-gen.py | 24 ++-
doc/guides/prog_guide/cmdline.rst | 3 +
doc/guides/rel_notes/release_25_07.rst | 5 +
lib/cmdline/cmdline_parse_num.c | 274 +++++++++++++++++++++++++
lib/cmdline/cmdline_parse_num.h | 4 +-
6 files changed, 498 insertions(+), 15 deletions(-)
diff --git a/app/test/test_cmdline_num.c b/app/test/test_cmdline_num.c
index 9276de59bd..e4038271c9 100644
--- a/app/test/test_cmdline_num.c
+++ b/app/test/test_cmdline_num.c
@@ -5,6 +5,8 @@
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
+#include <float.h>
+#include <math.h>
#include <rte_string_fns.h>
@@ -23,6 +25,11 @@ struct num_signed_str {
int64_t result;
};
+struct num_float_str {
+ const char * str;
+ double result;
+};
+
const struct num_unsigned_str num_valid_positive_strs[] = {
/* decimal positive */
{"0", 0 },
@@ -141,6 +148,63 @@ const struct num_signed_str num_valid_negative_strs[] = {
{"-9223372036854775808", INT64_MIN },
};
+const struct num_float_str num_valid_float_strs[] = {
+ /* zero */
+ {"0", 0},
+ /* parse int as float */
+ {"1", 1},
+ {"-1", -1},
+ /* fractional */
+ {"1.23", 1.23},
+ {"-1.23", -1.23},
+ {"0.123", 0.123},
+ {"-0.123", -0.123},
+ {"123.456", 123.456},
+ {"-123.456", -123.456},
+ /* positive exponent */
+ {"1e2", 1e2},
+ {"-1e2", -1e2},
+ {"1E2", 1E2},
+ {"-1E2", -1E2},
+ {"0.12e3", 0.12e3},
+ {"-0.12e3", -0.12e3},
+ {"1.23e4", 1.23e4},
+ {"-1.23e4", -1.23e4},
+ {"1.23E4", 1.23E4},
+ {"-1.23E4", -1.23E4},
+ {"123.456e7", 123.456e7},
+ {"-123.456e7", -123.456e7},
+ {"123.456E7", 123.456E7},
+ {"-123.456E7", -123.456E7},
+ /* negative exponent */
+ {"1e-2", 1e-2},
+ {"-1e-2", -1e-2},
+ {"1E-2", 1E-2},
+ {"-1E-2", -1E-2},
+ {"0.12e-3", 0.12e-3},
+ {"-0.12e-3", -0.12e-3},
+ {"1.23e-4", 1.23e-4},
+ {"-1.23e-4", -1.23e-4},
+ {"1.23E-4", 1.23E-4},
+ {"-1.23E-4", -1.23E-4},
+ {"123.456e-7", 123.456e-7},
+ {"-123.456e-7", -123.456e-7},
+ {"123.456E-7", 123.456E-7},
+ {"-123.456E-7", -123.456E-7},
+ /* try overflowing float */
+ {"2e63", 2e63},
+ {"-2e63", -2e63},
+ {"2E63", 2E63},
+ {"-2E63", -2E63},
+ {"18446744073709551615", (double) UINT64_MAX},
+ {"-9223372036854775808", (double) INT64_MIN},
+ /* try overflowing double */
+ {"2e308", HUGE_VAL},
+ {"-2e308", -HUGE_VAL},
+ {"2E308", HUGE_VAL},
+ {"-2E308", HUGE_VAL},
+};
+
const struct num_unsigned_str num_garbage_positive_strs[] = {
/* valid strings with garbage on the end, should still be valid */
/* decimal */
@@ -183,6 +247,30 @@ const struct num_signed_str num_garbage_negative_strs[] = {
{"-9223372036854775808 garbage", INT64_MIN },
};
+const char *float_invalid_strs[] = {
+ "0.",
+ ".1",
+ "1.1.",
+ "1.1.1",
+ "-0.",
+ "-.1",
+ "-1.1.",
+ "-1.1.1",
+ "e",
+ "1e",
+ "-1e",
+ "0.1e",
+ "-0.1e",
+ "1.e",
+ "-1.e",
+ "1.23e3.4",
+ "-1.23e3.4",
+ "1e1e",
+ "1e1e1",
+ "1e-",
+ "-1e-"
+};
+
const char * num_invalid_strs[] = {
"18446744073709551616", /* out of range unsigned */
"-9223372036854775809", /* out of range negative signed */
@@ -202,7 +290,16 @@ const char * num_invalid_strs[] = {
/* too long (128+ chars) */
("0b1111000011110000111100001111000011110000111100001111000011110000"
"1111000011110000111100001111000011110000111100001111000011110000"),
+ /* valid float values but should fail to parse as ints */
"1E3",
+ "-1E3",
+ "1.23",
+ "-1.23",
+ "1E-3",
+ "-1E-3",
+ "1.23E4",
+ "-1.23E4",
+ /* misc invalid values */
"0A",
"-B",
"+4",
@@ -216,6 +313,47 @@ const char * num_invalid_strs[] = {
"\0",
};
+static int
+float_cmp(double expected, void *actual_p, enum cmdline_numtype type)
+{
+ double eps;
+ double actual_d;
+
+ if (type == RTE_FLOAT_SINGLE) {
+ /* read as float, convert to double */
+ actual_d = (double)*(float *)actual_p;
+ /* FLT_EPSILON is too small for some tests */
+ eps = 1e-5f;
+ } else {
+ /* read as double */
+ actual_d = *(double *)actual_p;
+ eps = DBL_EPSILON;
+ }
+ /* compare using epsilon value */
+ if (fabs(expected - actual_d) < eps)
+ return 0;
+ /* not equal */
+ return expected < actual_d ? -1 : 1;
+}
+
+static int
+can_parse_float(double expected_result, enum cmdline_numtype type)
+{
+ switch (type) {
+ case RTE_FLOAT_SINGLE:
+ if (expected_result > FLT_MAX || expected_result < -FLT_MAX)
+ return 0;
+ break;
+ case RTE_FLOAT_DOUBLE:
+ if (expected_result > DBL_MAX || expected_result < -DBL_MAX)
+ return 0;
+ break;
+ default:
+ return 1;
+ }
+ return 1;
+}
+
static int
can_parse_unsigned(uint64_t expected_result, enum cmdline_numtype type)
{
@@ -371,11 +509,11 @@ test_parse_num_invalid_data(void)
int ret = 0;
unsigned i;
char buf[CMDLINE_TEST_BUFSIZE];
- uint64_t result; /* pick largest buffer */
cmdline_parse_token_num_t token;
- /* cycle through all possible parsed types */
+ /* cycle through all possible integer types */
for (type = RTE_UINT8; type <= RTE_INT64; type++) {
+ uint64_t result; /* pick largest buffer */
token.num_data.type = type;
/* test full strings */
@@ -397,6 +535,31 @@ test_parse_num_invalid_data(void)
}
}
}
+
+ /* cycle through all possible float types */
+ for (type = RTE_FLOAT_SINGLE; type <= RTE_FLOAT_DOUBLE; type++) {
+ double result; /* pick largest buffer */
+ token.num_data.type = type;
+
+ /* test full strings */
+ for (i = 0; i < RTE_DIM(float_invalid_strs); i++) {
+
+ memset(&result, 0, sizeof(double));
+ memset(&buf, 0, sizeof(buf));
+
+ ret = cmdline_parse_num((cmdline_parse_token_hdr_t*)&token,
+ float_invalid_strs[i], (void*)&result, sizeof(result));
+ if (ret != -1) {
+ /* get some info about what we are trying to parse */
+ cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token,
+ buf, sizeof(buf));
+
+ printf("Error: parsing %s as %s succeeded!\n",
+ float_invalid_strs[i], buf);
+ return -1;
+ }
+ }
+ }
return 0;
}
@@ -408,13 +571,13 @@ test_parse_num_valid(void)
enum cmdline_numtype type;
unsigned i;
char buf[CMDLINE_TEST_BUFSIZE];
- uint64_t result;
cmdline_parse_token_num_t token;
/** valid strings **/
/* cycle through all possible parsed types */
for (type = RTE_UINT8; type <= RTE_INT64; type++) {
+ uint64_t result;
token.num_data.type = type;
/* test positive strings */
@@ -489,10 +652,44 @@ test_parse_num_valid(void)
}
}
+ /* float values */
+ for (type = RTE_FLOAT_SINGLE; type <= RTE_FLOAT_DOUBLE; type++) {
+ double result;
+ token.num_data.type = type;
+
+ /* test all valid strings */
+ for (i = 0; i < RTE_DIM(num_valid_float_strs); i++) {
+ result = 0;
+ memset(&buf, 0, sizeof(buf));
+
+
+ cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token,
+ buf, sizeof(buf));
+
+ ret = cmdline_parse_num((cmdline_parse_token_hdr_t*) &token,
+ num_valid_float_strs[i].str,
+ (void*)&result, sizeof(result));
+
+ /* if it should have passed but didn't, or if it should have failed but didn't */
+ if ((ret < 0) == (can_parse_float(num_valid_float_strs[i].result, type) > 0)) {
+ printf("Error: parser behaves unexpectedly when parsing %s as %s!\n",
+ num_valid_float_strs[i].str, buf);
+ return -1;
+ }
+ /* check if result matches */
+ if (ret > 0 && float_cmp(num_valid_float_strs[i].result, &result, type) != 0) {
+ printf("Error: parsing %s as %s failed: result mismatch!\n",
+ num_valid_float_strs[i].str, buf);
+ return -1;
+ }
+ }
+ }
+
/** garbage strings **/
/* cycle through all possible parsed types */
for (type = RTE_UINT8; type <= RTE_INT64; type++) {
+ uint64_t result;
token.num_data.type = type;
/* test positive garbage strings */
diff --git a/buildtools/dpdk-cmdline-gen.py b/buildtools/dpdk-cmdline-gen.py
index 7dadded783..6c76d7116a 100755
--- a/buildtools/dpdk-cmdline-gen.py
+++ b/buildtools/dpdk-cmdline-gen.py
@@ -17,16 +17,18 @@
RTE_SET_USED(cl);
RTE_SET_USED(data);
"""
-NUMERIC_TYPES = [
- "UINT8",
- "UINT16",
- "UINT32",
- "UINT64",
- "INT8",
- "INT16",
- "INT32",
- "INT64",
-]
+NUMERIC_TYPES = {
+ "UINT8": "uint8_t",
+ "UINT16": "uint16_t",
+ "UINT32": "uint32_t",
+ "UINT64": "uint64_t",
+ "INT8": "int8_t",
+ "INT16": "int16_t",
+ "INT32": "int32_t",
+ "INT64": "int64_t",
+ "FLOAT_SINGLE": "float",
+ "FLOAT_DOUBLE": "double",
+}
def process_command(lineno, tokens, comment):
@@ -70,7 +72,7 @@ def process_command(lineno, tokens, comment):
f"\tTOKEN_STRING_INITIALIZER(struct cmd_{name}_result, {t_name}, {t_val});"
)
elif t_type in NUMERIC_TYPES:
- result_struct.append(f"\t{t_type.lower()}_t {t_name};")
+ result_struct.append(f"\t{NUMERIC_TYPES[t_type]} {t_name};")
initializers.append(
f"static cmdline_parse_token_num_t cmd_{name}_{t_name}_tok =\n"
f"\tTOKEN_NUM_INITIALIZER(struct cmd_{name}_result, {t_name}, RTE_{t_type});"
diff --git a/doc/guides/prog_guide/cmdline.rst b/doc/guides/prog_guide/cmdline.rst
index e20281ceb5..447a90e32e 100644
--- a/doc/guides/prog_guide/cmdline.rst
+++ b/doc/guides/prog_guide/cmdline.rst
@@ -22,6 +22,7 @@ The DPDK command-line library supports the following features:
* Strings
* Signed/unsigned 16/32/64-bit integers
+ * Single/double precision floats
* IP Addresses
* Ethernet Addresses
@@ -68,6 +69,8 @@ The format of the list file must be:
* ``<UINT16>port_id``
+ * ``<FLOAT_SINGLE>ratio``
+
* ``<IP>src_ip``
* ``<IPv4>dst_ip4``
diff --git a/doc/guides/rel_notes/release_25_07.rst b/doc/guides/rel_notes/release_25_07.rst
index 093b85d206..54bc545110 100644
--- a/doc/guides/rel_notes/release_25_07.rst
+++ b/doc/guides/rel_notes/release_25_07.rst
@@ -55,6 +55,11 @@ New Features
Also, make sure to start the actual text at the margin.
=======================================================
+* **Added floating point numbers support to cmdline library.**
+
+ The cmdline library now supports parsing single- and double-precision
+ floating point numbers in interactive user commands.
+
Removed Items
-------------
diff --git a/lib/cmdline/cmdline_parse_num.c b/lib/cmdline/cmdline_parse_num.c
index f21796bedb..687727b6cc 100644
--- a/lib/cmdline/cmdline_parse_num.c
+++ b/lib/cmdline/cmdline_parse_num.c
@@ -7,6 +7,8 @@
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
+#include <float.h>
+#include <math.h>
#include <string.h>
#include <eal_export.h>
#include <rte_string_fns.h>
@@ -34,6 +36,10 @@ enum num_parse_state_t {
DEC_NEG,
BIN,
HEX,
+ FLOAT_POS,
+ FLOAT_NEG,
+ FLOAT_EXP,
+ FLOAT_NEG_EXP,
ERROR,
@@ -44,12 +50,27 @@ enum num_parse_state_t {
BIN_OK,
DEC_NEG_OK,
DEC_POS_OK,
+ FLOAT_POS_OK,
+ FLOAT_NEG_OK,
+ FLOAT_EXP_POS_OK,
+ FLOAT_EXP_NEG_OK,
+};
+
+struct float_parse_state {
+ uint64_t dec;
+ uint64_t frac;
+ uint64_t frac_exp;
+ uint64_t exp;
+#define FLOAT_FLAG_NEG_RES (1 << 0)
+#define FLOAT_FLAG_NEG_EXP (1 << 1)
+ int flags;
};
/* Keep it sync with enum in .h */
static const char * num_help[] = {
"UINT8", "UINT16", "UINT32", "UINT64",
"INT8", "INT16", "INT32", "INT64",
+ "SINGLE", "DOUBLE"
};
static inline int
@@ -63,6 +84,50 @@ add_to_res(unsigned int c, uint64_t *res, unsigned int base)
return 0;
}
+static inline int
+check_float_result(enum cmdline_numtype res_type, struct float_parse_state *fps,
+ void *res)
+{
+ double dec, frac, exp, result;
+
+ /* extract parts */
+ dec = (double) fps->dec;
+ frac = (double) fps->frac * pow(10.0, -(double)fps->frac_exp);
+ exp = (double) fps->exp;
+
+ /* exponent might be negative */
+ if (fps->flags & FLOAT_FLAG_NEG_EXP)
+ exp = pow(10.0, -exp);
+ else
+ exp = pow(10.0, exp);
+
+ /* combine decimal, fractional, and exponent parts */
+ result = (dec + frac) * exp;
+
+ /* check for any overflows */
+ if (isinf(frac) || isinf(exp) || isinf(result))
+ return -1;
+
+ /* result is a valid double */
+
+ /* check if result needs to be negative */
+ if (fps->flags & FLOAT_FLAG_NEG_RES)
+ result = -result;
+
+ if (res_type == RTE_FLOAT_SINGLE) {
+ /* float can overflow from conversion */
+ float flt = (float)result;
+ if (isinf(flt))
+ return -1;
+ if (res) *(float *)res = flt;
+ } else if (res_type == RTE_FLOAT_DOUBLE) {
+ if (res) *(double *)res = result;
+ } else {
+ return -1;
+ }
+ return 0;
+}
+
static int
check_res_size(struct cmdline_token_num_data *nd, unsigned ressize)
{
@@ -87,6 +152,14 @@ check_res_size(struct cmdline_token_num_data *nd, unsigned ressize)
if (ressize < sizeof(int64_t))
return -1;
break;
+ case RTE_FLOAT_SINGLE:
+ if (ressize < sizeof(float))
+ return -1;
+ break;
+ case RTE_FLOAT_DOUBLE:
+ if (ressize < sizeof(double))
+ return -1;
+ break;
default:
return -1;
}
@@ -104,6 +177,7 @@ cmdline_parse_num(cmdline_parse_token_hdr_t *tk, const char *srcbuf, void *res,
const char * buf;
char c;
uint64_t res1 = 0;
+ struct float_parse_state fps = {};
if (!tk)
return -1;
@@ -156,6 +230,10 @@ cmdline_parse_num(cmdline_parse_token_hdr_t *tk, const char *srcbuf, void *res,
else
st = OCTAL_OK;
}
+ else if (c == '.') {
+ st = FLOAT_POS;
+ break;
+ }
else {
st = ERROR;
}
@@ -173,11 +251,80 @@ cmdline_parse_num(cmdline_parse_token_hdr_t *tk, const char *srcbuf, void *res,
}
break;
+ case FLOAT_POS:
+ if (c >= '0' && c <= '9') {
+ if (add_to_res(c - '0', &res1, 10) < 0)
+ st = ERROR;
+ else {
+ st = FLOAT_POS_OK;
+ fps.frac_exp++;
+ }
+ }
+ else {
+ st = ERROR;
+ }
+ break;
+
+ case FLOAT_NEG:
+ if (c >= '0' && c <= '9') {
+ if (add_to_res(c - '0', &res1, 10) < 0)
+ st = ERROR;
+ else {
+ st = FLOAT_NEG_OK;
+ fps.frac_exp++;
+ }
+ }
+ else {
+ st = ERROR;
+ }
+ break;
+
+ case FLOAT_EXP:
+ if (c >= '0' && c <= '9') {
+ if (add_to_res(c - '0', &res1, 10) < 0)
+ st = ERROR;
+ else
+ st = FLOAT_EXP_POS_OK;
+ }
+ else if (c == '-') {
+ st = FLOAT_NEG_EXP;
+ fps.flags |= FLOAT_FLAG_NEG_EXP;
+ }
+ else {
+ st = ERROR;
+ }
+ break;
+
+ case FLOAT_NEG_EXP:
+ if (c >= '0' && c <= '9') {
+ if (add_to_res(c - '0', &res1, 10) < 0)
+ st = ERROR;
+ else
+ st = FLOAT_EXP_NEG_OK;
+ }
+ else {
+ st = ERROR;
+ }
+ break;
+
case DEC_NEG_OK:
if (c >= '0' && c <= '9') {
if (add_to_res(c - '0', &res1, 10) < 0)
st = ERROR;
}
+ else if (c == '.') {
+ fps.dec = res1;
+ fps.flags |= FLOAT_FLAG_NEG_RES;
+ st = FLOAT_NEG;
+ /* erase result */
+ res1 = 0;
+ } else if (c == 'e' || c == 'E') {
+ fps.dec = res1;
+ fps.flags |= FLOAT_FLAG_NEG_RES;
+ st = FLOAT_EXP;
+ /* erase result */
+ res1 = 0;
+ }
else {
st = ERROR;
}
@@ -188,11 +335,75 @@ cmdline_parse_num(cmdline_parse_token_hdr_t *tk, const char *srcbuf, void *res,
if (add_to_res(c - '0', &res1, 10) < 0)
st = ERROR;
}
+ else if (c == '.') {
+ fps.dec = res1;
+ st = FLOAT_POS;
+ /* erase result */
+ res1 = 0;
+ }
+ else if (c == 'e' || c == 'E') {
+ fps.dec = res1;
+ st = FLOAT_EXP;
+ /* erase result */
+ res1 = 0;
+ }
else {
st = ERROR;
}
break;
+ case FLOAT_POS_OK:
+ if (c >= '0' && c <= '9') {
+ if (add_to_res(c - '0', &res1, 10) < 0)
+ st = ERROR;
+ else
+ fps.frac_exp++;
+ } else if (c == 'e' || c == 'E') {
+ fps.frac = res1;
+ st = FLOAT_EXP;
+ /* erase result */
+ res1 = 0;
+ } else {
+ st = ERROR;
+ }
+ break;
+
+ case FLOAT_NEG_OK:
+ if (c >= '0' && c <= '9') {
+ if (add_to_res(c - '0', &res1, 10) < 0)
+ st = ERROR;
+ else
+ fps.frac_exp++;
+ } else if (c == 'e' || c == 'E') {
+ fps.frac = res1;
+ st = FLOAT_EXP;
+ /* erase result */
+ res1 = 0;
+ } else {
+ st = ERROR;
+ }
+ break;
+
+ case FLOAT_EXP_POS_OK:
+ /* exponent is always whole */
+ if (c >= '0' && c <= '9') {
+ if (add_to_res(c - '0', &res1, 10) < 0)
+ st = ERROR;
+ } else {
+ st = ERROR;
+ }
+ break;
+
+ case FLOAT_EXP_NEG_OK:
+ /* exponent is always whole */
+ if (c >= '0' && c <= '9') {
+ if (add_to_res(c - '0', &res1, 10) < 0)
+ st = ERROR;
+ } else {
+ st = ERROR;
+ }
+ break;
+
case HEX:
st = HEX_OK;
/* fall-through */
@@ -282,6 +493,12 @@ cmdline_parse_num(cmdline_parse_token_hdr_t *tk, const char *srcbuf, void *res,
} else if (nd.type == RTE_UINT64) {
if (res) *(uint64_t *)res = res1;
return buf-srcbuf;
+ } else if (nd.type == RTE_FLOAT_SINGLE || nd.type == RTE_FLOAT_DOUBLE) {
+ /* parsed double from integer */
+ fps.dec = res1;
+ if (check_float_result(nd.type, &fps, res) < 0)
+ return -1;
+ return buf-srcbuf;
} else {
return -1;
}
@@ -304,6 +521,63 @@ cmdline_parse_num(cmdline_parse_token_hdr_t *tk, const char *srcbuf, void *res,
res1 <= (uint64_t)INT64_MAX + 1) {
if (res) *(int64_t *)res = (int64_t) (-res1);
return buf-srcbuf;
+ } else if ((nd.type == RTE_FLOAT_SINGLE || nd.type == RTE_FLOAT_DOUBLE) &&
+ res1 <= (uint64_t)INT64_MAX + 1) {
+ /* parsed double from negative integer */
+ fps.dec = res1;
+ fps.flags |= FLOAT_FLAG_NEG_RES;
+ if (check_float_result(nd.type, &fps, res) < 0)
+ return -1;
+ return buf-srcbuf;
+ } else {
+ return -1;
+ }
+ break;
+
+ case FLOAT_POS_OK:
+ if (nd.type == RTE_FLOAT_SINGLE || nd.type == RTE_FLOAT_DOUBLE) {
+ fps.frac = res1;
+
+ if (check_float_result(nd.type, &fps, res) < 0)
+ return -1;
+ return buf-srcbuf;
+ } else {
+ return -1;
+ }
+ break;
+
+ case FLOAT_NEG_OK:
+ if (nd.type == RTE_FLOAT_SINGLE || nd.type == RTE_FLOAT_DOUBLE) {
+ fps.frac = res1;
+
+ if (check_float_result(nd.type, &fps, res) < 0)
+ return -1;
+ return buf-srcbuf;
+ } else {
+ return -1;
+ }
+ break;
+
+ case FLOAT_EXP_POS_OK:
+ /* watch for overflow in the exponent */
+ if (nd.type == RTE_FLOAT_SINGLE || nd.type == RTE_FLOAT_DOUBLE) {
+ fps.exp = res1;
+
+ if (check_float_result(nd.type, &fps, res) < 0)
+ return -1;
+ return buf-srcbuf;
+ } else {
+ return -1;
+ }
+ break;
+
+ case FLOAT_EXP_NEG_OK:
+ if (nd.type == RTE_FLOAT_SINGLE || nd.type == RTE_FLOAT_DOUBLE) {
+ fps.exp = res1;
+
+ if (check_float_result(nd.type, &fps, res) < 0)
+ return -1;
+ return buf-srcbuf;
} else {
return -1;
}
diff --git a/lib/cmdline/cmdline_parse_num.h b/lib/cmdline/cmdline_parse_num.h
index bdd0267612..b2792a2d11 100644
--- a/lib/cmdline/cmdline_parse_num.h
+++ b/lib/cmdline/cmdline_parse_num.h
@@ -22,7 +22,9 @@ enum cmdline_numtype {
RTE_INT8,
RTE_INT16,
RTE_INT32,
- RTE_INT64
+ RTE_INT64,
+ RTE_FLOAT_SINGLE,
+ RTE_FLOAT_DOUBLE,
};
struct cmdline_token_num_data {
--
2.47.1
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH v3 2/2] app/testpmd: add sleep command
2025-05-07 9:50 ` [PATCH v3 " Anatoly Burakov
@ 2025-05-07 9:50 ` Anatoly Burakov
2025-05-07 9:53 ` [PATCH v3 1/2] cmdline: add floating point support Burakov, Anatoly
1 sibling, 0 replies; 23+ messages in thread
From: Anatoly Burakov @ 2025-05-07 9:50 UTC (permalink / raw)
To: dev, Aman Singh
Test-pmd already has a way to run a list of commands from file, but there
is no way to pause execution for a specified amount of time between two
commands. This may be necessary for simple automation, particularly for
waiting on some asynchronous operation such as link status update.
Add a simple sleep command to wait until certain number of seconds has
passed, using the newly added cmdline library floating point support.
Signed-off-by: Anatoly Burakov <anatoly.burakov@intel.com>
---
Notes:
v1 -> v2:
- Add floating point support to cmdline
- Use floating point format for pause command
app/test-pmd/cmdline.c | 35 +++++++++++++++++++++++++++++++++++
1 file changed, 35 insertions(+)
diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c
index d4bb3ec998..b6152c07e6 100644
--- a/app/test-pmd/cmdline.c
+++ b/app/test-pmd/cmdline.c
@@ -151,6 +151,9 @@ static void cmd_help_long_parsed(void *parsed_result,
"quit\n"
" Quit to prompt.\n\n"
+
+ "sleep secs\n"
+ " Sleep for secs seconds (can be fractional).\n\n"
);
}
@@ -7768,6 +7771,37 @@ static cmdline_parse_inst_t cmd_quit = {
},
};
+/* *** SLEEP *** */
+struct cmd_sleep_result {
+ cmdline_fixed_string_t sleep;
+ double secs;
+};
+
+static void cmd_sleep_parsed(void *parsed_result,
+ __rte_unused struct cmdline *cl,
+ __rte_unused void *data)
+{
+ struct cmd_sleep_result *res = parsed_result;
+
+ rte_delay_us_sleep(res->secs * 1E6);
+}
+
+static cmdline_parse_token_string_t cmd_sleep_sleep =
+ TOKEN_STRING_INITIALIZER(struct cmd_sleep_result, sleep, "sleep");
+static cmdline_parse_token_num_t cmd_sleep_seconds =
+ TOKEN_NUM_INITIALIZER(struct cmd_sleep_result, secs, RTE_FLOAT_DOUBLE);
+
+static cmdline_parse_inst_t cmd_sleep = {
+ .f = cmd_sleep_parsed,
+ .data = NULL,
+ .help_str = "sleep <secs>: Sleep for a specified number of seconds",
+ .tokens = {
+ (void *)&cmd_sleep_sleep,
+ (void *)&cmd_sleep_seconds,
+ NULL,
+ },
+};
+
/* *** ADD/REMOVE MAC ADDRESS FROM A PORT *** */
struct cmd_mac_addr_result {
cmdline_fixed_string_t mac_addr_cmd;
@@ -13711,6 +13745,7 @@ static cmdline_parse_ctx_t builtin_ctx[] = {
&cmd_showdevice,
&cmd_showcfg,
&cmd_showfwdall,
+ &cmd_sleep,
&cmd_start,
&cmd_start_tx_first,
&cmd_start_tx_first_n,
--
2.47.1
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v3 1/2] cmdline: add floating point support
2025-05-07 9:50 ` [PATCH v3 " Anatoly Burakov
2025-05-07 9:50 ` [PATCH v3 2/2] app/testpmd: add sleep command Anatoly Burakov
@ 2025-05-07 9:53 ` Burakov, Anatoly
1 sibling, 0 replies; 23+ messages in thread
From: Burakov, Anatoly @ 2025-05-07 9:53 UTC (permalink / raw)
To: dev
On 5/7/2025 11:50 AM, Anatoly Burakov wrote:
> Add support for parsing floating point numbers in cmdline library, as well
> as unit tests for the new functionality. The parser supports single and
> double precision floats, and will understand decimal fractions as well as
> scientific notation.
>
> Signed-off-by: Anatoly Burakov <anatoly.burakov@intel.com>
> ---
Missed patchnotes:
v2 -> v3:
- Fixed a bug where a free-standing negative exponent ("1e-") would
attempt to be
parsed, and added unit tests for this case
- Added support for floats in dpdk-cmdline-gen script
- Added documentation updates to call out float support
--
Thanks,
Anatoly
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH v4 1/2] cmdline: add floating point support
2025-05-02 12:27 [PATCH v1 1/1] app/testpmd: add sleep command Anatoly Burakov
` (3 preceding siblings ...)
2025-05-07 9:50 ` [PATCH v3 " Anatoly Burakov
@ 2025-05-07 10:01 ` Anatoly Burakov
2025-05-07 10:01 ` [PATCH v4 2/2] app/testpmd: add sleep command Anatoly Burakov
2025-05-07 10:35 ` [PATCH v4 1/2] cmdline: add floating point support Konstantin Ananyev
2025-05-07 15:22 ` [PATCH v5 1/3] cmdline: use C standard library as number parser Anatoly Burakov
5 siblings, 2 replies; 23+ messages in thread
From: Anatoly Burakov @ 2025-05-07 10:01 UTC (permalink / raw)
To: dev
Add support for parsing floating point numbers in cmdline library, as well
as unit tests for the new functionality. The parser supports single and
double precision floats, and will understand decimal fractions as well as
scientific notation.
Signed-off-by: Anatoly Burakov <anatoly.burakov@intel.com>
---
Notes:
v3 -> v4:
- Removed unnecessary check for integer overflow when parsing negative
floats (as we convert to double before changing sign)
- Make naming of float exponent states more consistent
v2 -> v3:
- Fixed a bug where a free-standing negative exponent ("1e-") would attempt to be
parsed, and added unit tests for this case
- Added support for floats in dpdk-cmdline-gen script
- Added documentation updates to call out float support
app/test/test_cmdline_num.c | 203 +++++++++++++++++-
buildtools/dpdk-cmdline-gen.py | 24 ++-
doc/guides/prog_guide/cmdline.rst | 3 +
doc/guides/rel_notes/release_25_07.rst | 5 +
lib/cmdline/cmdline_parse_num.c | 273 +++++++++++++++++++++++++
lib/cmdline/cmdline_parse_num.h | 4 +-
6 files changed, 497 insertions(+), 15 deletions(-)
diff --git a/app/test/test_cmdline_num.c b/app/test/test_cmdline_num.c
index 9276de59bd..e4038271c9 100644
--- a/app/test/test_cmdline_num.c
+++ b/app/test/test_cmdline_num.c
@@ -5,6 +5,8 @@
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
+#include <float.h>
+#include <math.h>
#include <rte_string_fns.h>
@@ -23,6 +25,11 @@ struct num_signed_str {
int64_t result;
};
+struct num_float_str {
+ const char * str;
+ double result;
+};
+
const struct num_unsigned_str num_valid_positive_strs[] = {
/* decimal positive */
{"0", 0 },
@@ -141,6 +148,63 @@ const struct num_signed_str num_valid_negative_strs[] = {
{"-9223372036854775808", INT64_MIN },
};
+const struct num_float_str num_valid_float_strs[] = {
+ /* zero */
+ {"0", 0},
+ /* parse int as float */
+ {"1", 1},
+ {"-1", -1},
+ /* fractional */
+ {"1.23", 1.23},
+ {"-1.23", -1.23},
+ {"0.123", 0.123},
+ {"-0.123", -0.123},
+ {"123.456", 123.456},
+ {"-123.456", -123.456},
+ /* positive exponent */
+ {"1e2", 1e2},
+ {"-1e2", -1e2},
+ {"1E2", 1E2},
+ {"-1E2", -1E2},
+ {"0.12e3", 0.12e3},
+ {"-0.12e3", -0.12e3},
+ {"1.23e4", 1.23e4},
+ {"-1.23e4", -1.23e4},
+ {"1.23E4", 1.23E4},
+ {"-1.23E4", -1.23E4},
+ {"123.456e7", 123.456e7},
+ {"-123.456e7", -123.456e7},
+ {"123.456E7", 123.456E7},
+ {"-123.456E7", -123.456E7},
+ /* negative exponent */
+ {"1e-2", 1e-2},
+ {"-1e-2", -1e-2},
+ {"1E-2", 1E-2},
+ {"-1E-2", -1E-2},
+ {"0.12e-3", 0.12e-3},
+ {"-0.12e-3", -0.12e-3},
+ {"1.23e-4", 1.23e-4},
+ {"-1.23e-4", -1.23e-4},
+ {"1.23E-4", 1.23E-4},
+ {"-1.23E-4", -1.23E-4},
+ {"123.456e-7", 123.456e-7},
+ {"-123.456e-7", -123.456e-7},
+ {"123.456E-7", 123.456E-7},
+ {"-123.456E-7", -123.456E-7},
+ /* try overflowing float */
+ {"2e63", 2e63},
+ {"-2e63", -2e63},
+ {"2E63", 2E63},
+ {"-2E63", -2E63},
+ {"18446744073709551615", (double) UINT64_MAX},
+ {"-9223372036854775808", (double) INT64_MIN},
+ /* try overflowing double */
+ {"2e308", HUGE_VAL},
+ {"-2e308", -HUGE_VAL},
+ {"2E308", HUGE_VAL},
+ {"-2E308", HUGE_VAL},
+};
+
const struct num_unsigned_str num_garbage_positive_strs[] = {
/* valid strings with garbage on the end, should still be valid */
/* decimal */
@@ -183,6 +247,30 @@ const struct num_signed_str num_garbage_negative_strs[] = {
{"-9223372036854775808 garbage", INT64_MIN },
};
+const char *float_invalid_strs[] = {
+ "0.",
+ ".1",
+ "1.1.",
+ "1.1.1",
+ "-0.",
+ "-.1",
+ "-1.1.",
+ "-1.1.1",
+ "e",
+ "1e",
+ "-1e",
+ "0.1e",
+ "-0.1e",
+ "1.e",
+ "-1.e",
+ "1.23e3.4",
+ "-1.23e3.4",
+ "1e1e",
+ "1e1e1",
+ "1e-",
+ "-1e-"
+};
+
const char * num_invalid_strs[] = {
"18446744073709551616", /* out of range unsigned */
"-9223372036854775809", /* out of range negative signed */
@@ -202,7 +290,16 @@ const char * num_invalid_strs[] = {
/* too long (128+ chars) */
("0b1111000011110000111100001111000011110000111100001111000011110000"
"1111000011110000111100001111000011110000111100001111000011110000"),
+ /* valid float values but should fail to parse as ints */
"1E3",
+ "-1E3",
+ "1.23",
+ "-1.23",
+ "1E-3",
+ "-1E-3",
+ "1.23E4",
+ "-1.23E4",
+ /* misc invalid values */
"0A",
"-B",
"+4",
@@ -216,6 +313,47 @@ const char * num_invalid_strs[] = {
"\0",
};
+static int
+float_cmp(double expected, void *actual_p, enum cmdline_numtype type)
+{
+ double eps;
+ double actual_d;
+
+ if (type == RTE_FLOAT_SINGLE) {
+ /* read as float, convert to double */
+ actual_d = (double)*(float *)actual_p;
+ /* FLT_EPSILON is too small for some tests */
+ eps = 1e-5f;
+ } else {
+ /* read as double */
+ actual_d = *(double *)actual_p;
+ eps = DBL_EPSILON;
+ }
+ /* compare using epsilon value */
+ if (fabs(expected - actual_d) < eps)
+ return 0;
+ /* not equal */
+ return expected < actual_d ? -1 : 1;
+}
+
+static int
+can_parse_float(double expected_result, enum cmdline_numtype type)
+{
+ switch (type) {
+ case RTE_FLOAT_SINGLE:
+ if (expected_result > FLT_MAX || expected_result < -FLT_MAX)
+ return 0;
+ break;
+ case RTE_FLOAT_DOUBLE:
+ if (expected_result > DBL_MAX || expected_result < -DBL_MAX)
+ return 0;
+ break;
+ default:
+ return 1;
+ }
+ return 1;
+}
+
static int
can_parse_unsigned(uint64_t expected_result, enum cmdline_numtype type)
{
@@ -371,11 +509,11 @@ test_parse_num_invalid_data(void)
int ret = 0;
unsigned i;
char buf[CMDLINE_TEST_BUFSIZE];
- uint64_t result; /* pick largest buffer */
cmdline_parse_token_num_t token;
- /* cycle through all possible parsed types */
+ /* cycle through all possible integer types */
for (type = RTE_UINT8; type <= RTE_INT64; type++) {
+ uint64_t result; /* pick largest buffer */
token.num_data.type = type;
/* test full strings */
@@ -397,6 +535,31 @@ test_parse_num_invalid_data(void)
}
}
}
+
+ /* cycle through all possible float types */
+ for (type = RTE_FLOAT_SINGLE; type <= RTE_FLOAT_DOUBLE; type++) {
+ double result; /* pick largest buffer */
+ token.num_data.type = type;
+
+ /* test full strings */
+ for (i = 0; i < RTE_DIM(float_invalid_strs); i++) {
+
+ memset(&result, 0, sizeof(double));
+ memset(&buf, 0, sizeof(buf));
+
+ ret = cmdline_parse_num((cmdline_parse_token_hdr_t*)&token,
+ float_invalid_strs[i], (void*)&result, sizeof(result));
+ if (ret != -1) {
+ /* get some info about what we are trying to parse */
+ cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token,
+ buf, sizeof(buf));
+
+ printf("Error: parsing %s as %s succeeded!\n",
+ float_invalid_strs[i], buf);
+ return -1;
+ }
+ }
+ }
return 0;
}
@@ -408,13 +571,13 @@ test_parse_num_valid(void)
enum cmdline_numtype type;
unsigned i;
char buf[CMDLINE_TEST_BUFSIZE];
- uint64_t result;
cmdline_parse_token_num_t token;
/** valid strings **/
/* cycle through all possible parsed types */
for (type = RTE_UINT8; type <= RTE_INT64; type++) {
+ uint64_t result;
token.num_data.type = type;
/* test positive strings */
@@ -489,10 +652,44 @@ test_parse_num_valid(void)
}
}
+ /* float values */
+ for (type = RTE_FLOAT_SINGLE; type <= RTE_FLOAT_DOUBLE; type++) {
+ double result;
+ token.num_data.type = type;
+
+ /* test all valid strings */
+ for (i = 0; i < RTE_DIM(num_valid_float_strs); i++) {
+ result = 0;
+ memset(&buf, 0, sizeof(buf));
+
+
+ cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token,
+ buf, sizeof(buf));
+
+ ret = cmdline_parse_num((cmdline_parse_token_hdr_t*) &token,
+ num_valid_float_strs[i].str,
+ (void*)&result, sizeof(result));
+
+ /* if it should have passed but didn't, or if it should have failed but didn't */
+ if ((ret < 0) == (can_parse_float(num_valid_float_strs[i].result, type) > 0)) {
+ printf("Error: parser behaves unexpectedly when parsing %s as %s!\n",
+ num_valid_float_strs[i].str, buf);
+ return -1;
+ }
+ /* check if result matches */
+ if (ret > 0 && float_cmp(num_valid_float_strs[i].result, &result, type) != 0) {
+ printf("Error: parsing %s as %s failed: result mismatch!\n",
+ num_valid_float_strs[i].str, buf);
+ return -1;
+ }
+ }
+ }
+
/** garbage strings **/
/* cycle through all possible parsed types */
for (type = RTE_UINT8; type <= RTE_INT64; type++) {
+ uint64_t result;
token.num_data.type = type;
/* test positive garbage strings */
diff --git a/buildtools/dpdk-cmdline-gen.py b/buildtools/dpdk-cmdline-gen.py
index 7dadded783..6c76d7116a 100755
--- a/buildtools/dpdk-cmdline-gen.py
+++ b/buildtools/dpdk-cmdline-gen.py
@@ -17,16 +17,18 @@
RTE_SET_USED(cl);
RTE_SET_USED(data);
"""
-NUMERIC_TYPES = [
- "UINT8",
- "UINT16",
- "UINT32",
- "UINT64",
- "INT8",
- "INT16",
- "INT32",
- "INT64",
-]
+NUMERIC_TYPES = {
+ "UINT8": "uint8_t",
+ "UINT16": "uint16_t",
+ "UINT32": "uint32_t",
+ "UINT64": "uint64_t",
+ "INT8": "int8_t",
+ "INT16": "int16_t",
+ "INT32": "int32_t",
+ "INT64": "int64_t",
+ "FLOAT_SINGLE": "float",
+ "FLOAT_DOUBLE": "double",
+}
def process_command(lineno, tokens, comment):
@@ -70,7 +72,7 @@ def process_command(lineno, tokens, comment):
f"\tTOKEN_STRING_INITIALIZER(struct cmd_{name}_result, {t_name}, {t_val});"
)
elif t_type in NUMERIC_TYPES:
- result_struct.append(f"\t{t_type.lower()}_t {t_name};")
+ result_struct.append(f"\t{NUMERIC_TYPES[t_type]} {t_name};")
initializers.append(
f"static cmdline_parse_token_num_t cmd_{name}_{t_name}_tok =\n"
f"\tTOKEN_NUM_INITIALIZER(struct cmd_{name}_result, {t_name}, RTE_{t_type});"
diff --git a/doc/guides/prog_guide/cmdline.rst b/doc/guides/prog_guide/cmdline.rst
index e20281ceb5..447a90e32e 100644
--- a/doc/guides/prog_guide/cmdline.rst
+++ b/doc/guides/prog_guide/cmdline.rst
@@ -22,6 +22,7 @@ The DPDK command-line library supports the following features:
* Strings
* Signed/unsigned 16/32/64-bit integers
+ * Single/double precision floats
* IP Addresses
* Ethernet Addresses
@@ -68,6 +69,8 @@ The format of the list file must be:
* ``<UINT16>port_id``
+ * ``<FLOAT_SINGLE>ratio``
+
* ``<IP>src_ip``
* ``<IPv4>dst_ip4``
diff --git a/doc/guides/rel_notes/release_25_07.rst b/doc/guides/rel_notes/release_25_07.rst
index 093b85d206..54bc545110 100644
--- a/doc/guides/rel_notes/release_25_07.rst
+++ b/doc/guides/rel_notes/release_25_07.rst
@@ -55,6 +55,11 @@ New Features
Also, make sure to start the actual text at the margin.
=======================================================
+* **Added floating point numbers support to cmdline library.**
+
+ The cmdline library now supports parsing single- and double-precision
+ floating point numbers in interactive user commands.
+
Removed Items
-------------
diff --git a/lib/cmdline/cmdline_parse_num.c b/lib/cmdline/cmdline_parse_num.c
index f21796bedb..9e4d559325 100644
--- a/lib/cmdline/cmdline_parse_num.c
+++ b/lib/cmdline/cmdline_parse_num.c
@@ -7,6 +7,8 @@
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
+#include <float.h>
+#include <math.h>
#include <string.h>
#include <eal_export.h>
#include <rte_string_fns.h>
@@ -34,6 +36,10 @@ enum num_parse_state_t {
DEC_NEG,
BIN,
HEX,
+ FLOAT_POS,
+ FLOAT_NEG,
+ FLOAT_EXP_POS,
+ FLOAT_EXP_NEG,
ERROR,
@@ -44,12 +50,27 @@ enum num_parse_state_t {
BIN_OK,
DEC_NEG_OK,
DEC_POS_OK,
+ FLOAT_POS_OK,
+ FLOAT_NEG_OK,
+ FLOAT_EXP_POS_OK,
+ FLOAT_EXP_NEG_OK,
+};
+
+struct float_parse_state {
+ uint64_t dec;
+ uint64_t frac;
+ uint64_t frac_exp;
+ uint64_t exp;
+#define FLOAT_FLAG_NEG_RES (1 << 0)
+#define FLOAT_FLAG_NEG_EXP (1 << 1)
+ int flags;
};
/* Keep it sync with enum in .h */
static const char * num_help[] = {
"UINT8", "UINT16", "UINT32", "UINT64",
"INT8", "INT16", "INT32", "INT64",
+ "SINGLE", "DOUBLE"
};
static inline int
@@ -63,6 +84,50 @@ add_to_res(unsigned int c, uint64_t *res, unsigned int base)
return 0;
}
+static inline int
+check_float_result(enum cmdline_numtype res_type, struct float_parse_state *fps,
+ void *res)
+{
+ double dec, frac, exp, result;
+
+ /* extract parts */
+ dec = (double) fps->dec;
+ frac = (double) fps->frac * pow(10.0, -(double)fps->frac_exp);
+ exp = (double) fps->exp;
+
+ /* exponent might be negative */
+ if (fps->flags & FLOAT_FLAG_NEG_EXP)
+ exp = pow(10.0, -exp);
+ else
+ exp = pow(10.0, exp);
+
+ /* combine decimal, fractional, and exponent parts */
+ result = (dec + frac) * exp;
+
+ /* check for any overflows */
+ if (isinf(frac) || isinf(exp) || isinf(result))
+ return -1;
+
+ /* result is a valid double */
+
+ /* check if result needs to be negative */
+ if (fps->flags & FLOAT_FLAG_NEG_RES)
+ result = -result;
+
+ if (res_type == RTE_FLOAT_SINGLE) {
+ /* float can overflow from conversion */
+ float flt = (float)result;
+ if (isinf(flt))
+ return -1;
+ if (res) *(float *)res = flt;
+ } else if (res_type == RTE_FLOAT_DOUBLE) {
+ if (res) *(double *)res = result;
+ } else {
+ return -1;
+ }
+ return 0;
+}
+
static int
check_res_size(struct cmdline_token_num_data *nd, unsigned ressize)
{
@@ -87,6 +152,14 @@ check_res_size(struct cmdline_token_num_data *nd, unsigned ressize)
if (ressize < sizeof(int64_t))
return -1;
break;
+ case RTE_FLOAT_SINGLE:
+ if (ressize < sizeof(float))
+ return -1;
+ break;
+ case RTE_FLOAT_DOUBLE:
+ if (ressize < sizeof(double))
+ return -1;
+ break;
default:
return -1;
}
@@ -104,6 +177,7 @@ cmdline_parse_num(cmdline_parse_token_hdr_t *tk, const char *srcbuf, void *res,
const char * buf;
char c;
uint64_t res1 = 0;
+ struct float_parse_state fps = {};
if (!tk)
return -1;
@@ -156,6 +230,10 @@ cmdline_parse_num(cmdline_parse_token_hdr_t *tk, const char *srcbuf, void *res,
else
st = OCTAL_OK;
}
+ else if (c == '.') {
+ st = FLOAT_POS;
+ break;
+ }
else {
st = ERROR;
}
@@ -173,11 +251,80 @@ cmdline_parse_num(cmdline_parse_token_hdr_t *tk, const char *srcbuf, void *res,
}
break;
+ case FLOAT_POS:
+ if (c >= '0' && c <= '9') {
+ if (add_to_res(c - '0', &res1, 10) < 0)
+ st = ERROR;
+ else {
+ st = FLOAT_POS_OK;
+ fps.frac_exp++;
+ }
+ }
+ else {
+ st = ERROR;
+ }
+ break;
+
+ case FLOAT_NEG:
+ if (c >= '0' && c <= '9') {
+ if (add_to_res(c - '0', &res1, 10) < 0)
+ st = ERROR;
+ else {
+ st = FLOAT_NEG_OK;
+ fps.frac_exp++;
+ }
+ }
+ else {
+ st = ERROR;
+ }
+ break;
+
+ case FLOAT_EXP_POS:
+ if (c >= '0' && c <= '9') {
+ if (add_to_res(c - '0', &res1, 10) < 0)
+ st = ERROR;
+ else
+ st = FLOAT_EXP_POS_OK;
+ }
+ else if (c == '-') {
+ st = FLOAT_EXP_NEG;
+ fps.flags |= FLOAT_FLAG_NEG_EXP;
+ }
+ else {
+ st = ERROR;
+ }
+ break;
+
+ case FLOAT_EXP_NEG:
+ if (c >= '0' && c <= '9') {
+ if (add_to_res(c - '0', &res1, 10) < 0)
+ st = ERROR;
+ else
+ st = FLOAT_EXP_NEG_OK;
+ }
+ else {
+ st = ERROR;
+ }
+ break;
+
case DEC_NEG_OK:
if (c >= '0' && c <= '9') {
if (add_to_res(c - '0', &res1, 10) < 0)
st = ERROR;
}
+ else if (c == '.') {
+ fps.dec = res1;
+ fps.flags |= FLOAT_FLAG_NEG_RES;
+ st = FLOAT_NEG;
+ /* erase result */
+ res1 = 0;
+ } else if (c == 'e' || c == 'E') {
+ fps.dec = res1;
+ fps.flags |= FLOAT_FLAG_NEG_RES;
+ st = FLOAT_EXP_POS;
+ /* erase result */
+ res1 = 0;
+ }
else {
st = ERROR;
}
@@ -188,11 +335,75 @@ cmdline_parse_num(cmdline_parse_token_hdr_t *tk, const char *srcbuf, void *res,
if (add_to_res(c - '0', &res1, 10) < 0)
st = ERROR;
}
+ else if (c == '.') {
+ fps.dec = res1;
+ st = FLOAT_POS;
+ /* erase result */
+ res1 = 0;
+ }
+ else if (c == 'e' || c == 'E') {
+ fps.dec = res1;
+ st = FLOAT_EXP_POS;
+ /* erase result */
+ res1 = 0;
+ }
else {
st = ERROR;
}
break;
+ case FLOAT_POS_OK:
+ if (c >= '0' && c <= '9') {
+ if (add_to_res(c - '0', &res1, 10) < 0)
+ st = ERROR;
+ else
+ fps.frac_exp++;
+ } else if (c == 'e' || c == 'E') {
+ fps.frac = res1;
+ st = FLOAT_EXP_POS;
+ /* erase result */
+ res1 = 0;
+ } else {
+ st = ERROR;
+ }
+ break;
+
+ case FLOAT_NEG_OK:
+ if (c >= '0' && c <= '9') {
+ if (add_to_res(c - '0', &res1, 10) < 0)
+ st = ERROR;
+ else
+ fps.frac_exp++;
+ } else if (c == 'e' || c == 'E') {
+ fps.frac = res1;
+ st = FLOAT_EXP_POS;
+ /* erase result */
+ res1 = 0;
+ } else {
+ st = ERROR;
+ }
+ break;
+
+ case FLOAT_EXP_POS_OK:
+ /* exponent is always whole */
+ if (c >= '0' && c <= '9') {
+ if (add_to_res(c - '0', &res1, 10) < 0)
+ st = ERROR;
+ } else {
+ st = ERROR;
+ }
+ break;
+
+ case FLOAT_EXP_NEG_OK:
+ /* exponent is always whole */
+ if (c >= '0' && c <= '9') {
+ if (add_to_res(c - '0', &res1, 10) < 0)
+ st = ERROR;
+ } else {
+ st = ERROR;
+ }
+ break;
+
case HEX:
st = HEX_OK;
/* fall-through */
@@ -282,6 +493,12 @@ cmdline_parse_num(cmdline_parse_token_hdr_t *tk, const char *srcbuf, void *res,
} else if (nd.type == RTE_UINT64) {
if (res) *(uint64_t *)res = res1;
return buf-srcbuf;
+ } else if (nd.type == RTE_FLOAT_SINGLE || nd.type == RTE_FLOAT_DOUBLE) {
+ /* parsed double from integer */
+ fps.dec = res1;
+ if (check_float_result(nd.type, &fps, res) < 0)
+ return -1;
+ return buf-srcbuf;
} else {
return -1;
}
@@ -304,6 +521,62 @@ cmdline_parse_num(cmdline_parse_token_hdr_t *tk, const char *srcbuf, void *res,
res1 <= (uint64_t)INT64_MAX + 1) {
if (res) *(int64_t *)res = (int64_t) (-res1);
return buf-srcbuf;
+ } else if (nd.type == RTE_FLOAT_SINGLE || nd.type == RTE_FLOAT_DOUBLE) {
+ /* parsed double from negative integer */
+ fps.dec = res1;
+ fps.flags |= FLOAT_FLAG_NEG_RES;
+ if (check_float_result(nd.type, &fps, res) < 0)
+ return -1;
+ return buf-srcbuf;
+ } else {
+ return -1;
+ }
+ break;
+
+ case FLOAT_POS_OK:
+ if (nd.type == RTE_FLOAT_SINGLE || nd.type == RTE_FLOAT_DOUBLE) {
+ fps.frac = res1;
+
+ if (check_float_result(nd.type, &fps, res) < 0)
+ return -1;
+ return buf-srcbuf;
+ } else {
+ return -1;
+ }
+ break;
+
+ case FLOAT_NEG_OK:
+ if (nd.type == RTE_FLOAT_SINGLE || nd.type == RTE_FLOAT_DOUBLE) {
+ fps.frac = res1;
+
+ if (check_float_result(nd.type, &fps, res) < 0)
+ return -1;
+ return buf-srcbuf;
+ } else {
+ return -1;
+ }
+ break;
+
+ case FLOAT_EXP_POS_OK:
+ /* watch for overflow in the exponent */
+ if (nd.type == RTE_FLOAT_SINGLE || nd.type == RTE_FLOAT_DOUBLE) {
+ fps.exp = res1;
+
+ if (check_float_result(nd.type, &fps, res) < 0)
+ return -1;
+ return buf-srcbuf;
+ } else {
+ return -1;
+ }
+ break;
+
+ case FLOAT_EXP_NEG_OK:
+ if (nd.type == RTE_FLOAT_SINGLE || nd.type == RTE_FLOAT_DOUBLE) {
+ fps.exp = res1;
+
+ if (check_float_result(nd.type, &fps, res) < 0)
+ return -1;
+ return buf-srcbuf;
} else {
return -1;
}
diff --git a/lib/cmdline/cmdline_parse_num.h b/lib/cmdline/cmdline_parse_num.h
index bdd0267612..b2792a2d11 100644
--- a/lib/cmdline/cmdline_parse_num.h
+++ b/lib/cmdline/cmdline_parse_num.h
@@ -22,7 +22,9 @@ enum cmdline_numtype {
RTE_INT8,
RTE_INT16,
RTE_INT32,
- RTE_INT64
+ RTE_INT64,
+ RTE_FLOAT_SINGLE,
+ RTE_FLOAT_DOUBLE,
};
struct cmdline_token_num_data {
--
2.47.1
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH v4 2/2] app/testpmd: add sleep command
2025-05-07 10:01 ` [PATCH v4 " Anatoly Burakov
@ 2025-05-07 10:01 ` Anatoly Burakov
2025-05-07 10:35 ` [PATCH v4 1/2] cmdline: add floating point support Konstantin Ananyev
1 sibling, 0 replies; 23+ messages in thread
From: Anatoly Burakov @ 2025-05-07 10:01 UTC (permalink / raw)
To: dev, Aman Singh
Test-pmd already has a way to run a list of commands from file, but there
is no way to pause execution for a specified amount of time between two
commands. This may be necessary for simple automation, particularly for
waiting on some asynchronous operation such as link status update.
Add a simple sleep command to wait until certain number of seconds has
passed, using the newly added cmdline library floating point support.
Signed-off-by: Anatoly Burakov <anatoly.burakov@intel.com>
---
Notes:
v1 -> v2:
- Add floating point support to cmdline
- Use floating point format for pause command
app/test-pmd/cmdline.c | 35 +++++++++++++++++++++++++++++++++++
1 file changed, 35 insertions(+)
diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c
index d4bb3ec998..b6152c07e6 100644
--- a/app/test-pmd/cmdline.c
+++ b/app/test-pmd/cmdline.c
@@ -151,6 +151,9 @@ static void cmd_help_long_parsed(void *parsed_result,
"quit\n"
" Quit to prompt.\n\n"
+
+ "sleep secs\n"
+ " Sleep for secs seconds (can be fractional).\n\n"
);
}
@@ -7768,6 +7771,37 @@ static cmdline_parse_inst_t cmd_quit = {
},
};
+/* *** SLEEP *** */
+struct cmd_sleep_result {
+ cmdline_fixed_string_t sleep;
+ double secs;
+};
+
+static void cmd_sleep_parsed(void *parsed_result,
+ __rte_unused struct cmdline *cl,
+ __rte_unused void *data)
+{
+ struct cmd_sleep_result *res = parsed_result;
+
+ rte_delay_us_sleep(res->secs * 1E6);
+}
+
+static cmdline_parse_token_string_t cmd_sleep_sleep =
+ TOKEN_STRING_INITIALIZER(struct cmd_sleep_result, sleep, "sleep");
+static cmdline_parse_token_num_t cmd_sleep_seconds =
+ TOKEN_NUM_INITIALIZER(struct cmd_sleep_result, secs, RTE_FLOAT_DOUBLE);
+
+static cmdline_parse_inst_t cmd_sleep = {
+ .f = cmd_sleep_parsed,
+ .data = NULL,
+ .help_str = "sleep <secs>: Sleep for a specified number of seconds",
+ .tokens = {
+ (void *)&cmd_sleep_sleep,
+ (void *)&cmd_sleep_seconds,
+ NULL,
+ },
+};
+
/* *** ADD/REMOVE MAC ADDRESS FROM A PORT *** */
struct cmd_mac_addr_result {
cmdline_fixed_string_t mac_addr_cmd;
@@ -13711,6 +13745,7 @@ static cmdline_parse_ctx_t builtin_ctx[] = {
&cmd_showdevice,
&cmd_showcfg,
&cmd_showfwdall,
+ &cmd_sleep,
&cmd_start,
&cmd_start_tx_first,
&cmd_start_tx_first_n,
--
2.47.1
^ permalink raw reply [flat|nested] 23+ messages in thread
* RE: [PATCH v4 1/2] cmdline: add floating point support
2025-05-07 10:01 ` [PATCH v4 " Anatoly Burakov
2025-05-07 10:01 ` [PATCH v4 2/2] app/testpmd: add sleep command Anatoly Burakov
@ 2025-05-07 10:35 ` Konstantin Ananyev
2025-05-07 11:06 ` Burakov, Anatoly
1 sibling, 1 reply; 23+ messages in thread
From: Konstantin Ananyev @ 2025-05-07 10:35 UTC (permalink / raw)
To: Anatoly Burakov, dev
> Add support for parsing floating point numbers in cmdline library, as well
> as unit tests for the new functionality. The parser supports single and
> double precision floats, and will understand decimal fractions as well as
> scientific notation.
There are standard functions for that: strtod/strtof - can't we simply use them?
>
> Signed-off-by: Anatoly Burakov <anatoly.burakov@intel.com>
> ---
>
> Notes:
> v3 -> v4:
> - Removed unnecessary check for integer overflow when parsing negative
> floats (as we convert to double before changing sign)
> - Make naming of float exponent states more consistent
>
> v2 -> v3:
> - Fixed a bug where a free-standing negative exponent ("1e-") would attempt to be
> parsed, and added unit tests for this case
> - Added support for floats in dpdk-cmdline-gen script
> - Added documentation updates to call out float support
>
> app/test/test_cmdline_num.c | 203 +++++++++++++++++-
> buildtools/dpdk-cmdline-gen.py | 24 ++-
> doc/guides/prog_guide/cmdline.rst | 3 +
> doc/guides/rel_notes/release_25_07.rst | 5 +
> lib/cmdline/cmdline_parse_num.c | 273 +++++++++++++++++++++++++
> lib/cmdline/cmdline_parse_num.h | 4 +-
> 6 files changed, 497 insertions(+), 15 deletions(-)
>
> diff --git a/app/test/test_cmdline_num.c b/app/test/test_cmdline_num.c
> index 9276de59bd..e4038271c9 100644
> --- a/app/test/test_cmdline_num.c
> +++ b/app/test/test_cmdline_num.c
> @@ -5,6 +5,8 @@
> #include <stdio.h>
> #include <string.h>
> #include <inttypes.h>
> +#include <float.h>
> +#include <math.h>
>
> #include <rte_string_fns.h>
>
> @@ -23,6 +25,11 @@ struct num_signed_str {
> int64_t result;
> };
>
> +struct num_float_str {
> + const char * str;
> + double result;
> +};
> +
> const struct num_unsigned_str num_valid_positive_strs[] = {
> /* decimal positive */
> {"0", 0 },
> @@ -141,6 +148,63 @@ const struct num_signed_str num_valid_negative_strs[] = {
> {"-9223372036854775808", INT64_MIN },
> };
>
> +const struct num_float_str num_valid_float_strs[] = {
> + /* zero */
> + {"0", 0},
> + /* parse int as float */
> + {"1", 1},
> + {"-1", -1},
> + /* fractional */
> + {"1.23", 1.23},
> + {"-1.23", -1.23},
> + {"0.123", 0.123},
> + {"-0.123", -0.123},
> + {"123.456", 123.456},
> + {"-123.456", -123.456},
> + /* positive exponent */
> + {"1e2", 1e2},
> + {"-1e2", -1e2},
> + {"1E2", 1E2},
> + {"-1E2", -1E2},
> + {"0.12e3", 0.12e3},
> + {"-0.12e3", -0.12e3},
> + {"1.23e4", 1.23e4},
> + {"-1.23e4", -1.23e4},
> + {"1.23E4", 1.23E4},
> + {"-1.23E4", -1.23E4},
> + {"123.456e7", 123.456e7},
> + {"-123.456e7", -123.456e7},
> + {"123.456E7", 123.456E7},
> + {"-123.456E7", -123.456E7},
> + /* negative exponent */
> + {"1e-2", 1e-2},
> + {"-1e-2", -1e-2},
> + {"1E-2", 1E-2},
> + {"-1E-2", -1E-2},
> + {"0.12e-3", 0.12e-3},
> + {"-0.12e-3", -0.12e-3},
> + {"1.23e-4", 1.23e-4},
> + {"-1.23e-4", -1.23e-4},
> + {"1.23E-4", 1.23E-4},
> + {"-1.23E-4", -1.23E-4},
> + {"123.456e-7", 123.456e-7},
> + {"-123.456e-7", -123.456e-7},
> + {"123.456E-7", 123.456E-7},
> + {"-123.456E-7", -123.456E-7},
> + /* try overflowing float */
> + {"2e63", 2e63},
> + {"-2e63", -2e63},
> + {"2E63", 2E63},
> + {"-2E63", -2E63},
> + {"18446744073709551615", (double) UINT64_MAX},
> + {"-9223372036854775808", (double) INT64_MIN},
> + /* try overflowing double */
> + {"2e308", HUGE_VAL},
> + {"-2e308", -HUGE_VAL},
> + {"2E308", HUGE_VAL},
> + {"-2E308", HUGE_VAL},
> +};
> +
> const struct num_unsigned_str num_garbage_positive_strs[] = {
> /* valid strings with garbage on the end, should still be valid */
> /* decimal */
> @@ -183,6 +247,30 @@ const struct num_signed_str num_garbage_negative_strs[] = {
> {"-9223372036854775808 garbage", INT64_MIN },
> };
>
> +const char *float_invalid_strs[] = {
> + "0.",
> + ".1",
> + "1.1.",
> + "1.1.1",
> + "-0.",
> + "-.1",
> + "-1.1.",
> + "-1.1.1",
> + "e",
> + "1e",
> + "-1e",
> + "0.1e",
> + "-0.1e",
> + "1.e",
> + "-1.e",
> + "1.23e3.4",
> + "-1.23e3.4",
> + "1e1e",
> + "1e1e1",
> + "1e-",
> + "-1e-"
> +};
> +
> const char * num_invalid_strs[] = {
> "18446744073709551616", /* out of range unsigned */
> "-9223372036854775809", /* out of range negative signed */
> @@ -202,7 +290,16 @@ const char * num_invalid_strs[] = {
> /* too long (128+ chars) */
> ("0b1111000011110000111100001111000011110000111100001111000011110000"
> "1111000011110000111100001111000011110000111100001111000011110000"),
> + /* valid float values but should fail to parse as ints */
> "1E3",
> + "-1E3",
> + "1.23",
> + "-1.23",
> + "1E-3",
> + "-1E-3",
> + "1.23E4",
> + "-1.23E4",
> + /* misc invalid values */
> "0A",
> "-B",
> "+4",
> @@ -216,6 +313,47 @@ const char * num_invalid_strs[] = {
> "\0",
> };
>
> +static int
> +float_cmp(double expected, void *actual_p, enum cmdline_numtype type)
> +{
> + double eps;
> + double actual_d;
> +
> + if (type == RTE_FLOAT_SINGLE) {
> + /* read as float, convert to double */
> + actual_d = (double)*(float *)actual_p;
> + /* FLT_EPSILON is too small for some tests */
> + eps = 1e-5f;
> + } else {
> + /* read as double */
> + actual_d = *(double *)actual_p;
> + eps = DBL_EPSILON;
> + }
> + /* compare using epsilon value */
> + if (fabs(expected - actual_d) < eps)
> + return 0;
> + /* not equal */
> + return expected < actual_d ? -1 : 1;
> +}
> +
> +static int
> +can_parse_float(double expected_result, enum cmdline_numtype type)
> +{
> + switch (type) {
> + case RTE_FLOAT_SINGLE:
> + if (expected_result > FLT_MAX || expected_result < -FLT_MAX)
> + return 0;
> + break;
> + case RTE_FLOAT_DOUBLE:
> + if (expected_result > DBL_MAX || expected_result < -DBL_MAX)
> + return 0;
> + break;
> + default:
> + return 1;
> + }
> + return 1;
> +}
> +
> static int
> can_parse_unsigned(uint64_t expected_result, enum cmdline_numtype type)
> {
> @@ -371,11 +509,11 @@ test_parse_num_invalid_data(void)
> int ret = 0;
> unsigned i;
> char buf[CMDLINE_TEST_BUFSIZE];
> - uint64_t result; /* pick largest buffer */
> cmdline_parse_token_num_t token;
>
> - /* cycle through all possible parsed types */
> + /* cycle through all possible integer types */
> for (type = RTE_UINT8; type <= RTE_INT64; type++) {
> + uint64_t result; /* pick largest buffer */
> token.num_data.type = type;
>
> /* test full strings */
> @@ -397,6 +535,31 @@ test_parse_num_invalid_data(void)
> }
> }
> }
> +
> + /* cycle through all possible float types */
> + for (type = RTE_FLOAT_SINGLE; type <= RTE_FLOAT_DOUBLE; type++) {
> + double result; /* pick largest buffer */
> + token.num_data.type = type;
> +
> + /* test full strings */
> + for (i = 0; i < RTE_DIM(float_invalid_strs); i++) {
> +
> + memset(&result, 0, sizeof(double));
> + memset(&buf, 0, sizeof(buf));
> +
> + ret = cmdline_parse_num((cmdline_parse_token_hdr_t*)&token,
> + float_invalid_strs[i], (void*)&result, sizeof(result));
> + if (ret != -1) {
> + /* get some info about what we are trying to parse */
> + cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token,
> + buf, sizeof(buf));
> +
> + printf("Error: parsing %s as %s succeeded!\n",
> + float_invalid_strs[i], buf);
> + return -1;
> + }
> + }
> + }
> return 0;
> }
>
> @@ -408,13 +571,13 @@ test_parse_num_valid(void)
> enum cmdline_numtype type;
> unsigned i;
> char buf[CMDLINE_TEST_BUFSIZE];
> - uint64_t result;
> cmdline_parse_token_num_t token;
>
> /** valid strings **/
>
> /* cycle through all possible parsed types */
> for (type = RTE_UINT8; type <= RTE_INT64; type++) {
> + uint64_t result;
> token.num_data.type = type;
>
> /* test positive strings */
> @@ -489,10 +652,44 @@ test_parse_num_valid(void)
> }
> }
>
> + /* float values */
> + for (type = RTE_FLOAT_SINGLE; type <= RTE_FLOAT_DOUBLE; type++) {
> + double result;
> + token.num_data.type = type;
> +
> + /* test all valid strings */
> + for (i = 0; i < RTE_DIM(num_valid_float_strs); i++) {
> + result = 0;
> + memset(&buf, 0, sizeof(buf));
> +
> +
> + cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token,
> + buf, sizeof(buf));
> +
> + ret = cmdline_parse_num((cmdline_parse_token_hdr_t*) &token,
> + num_valid_float_strs[i].str,
> + (void*)&result, sizeof(result));
> +
> + /* if it should have passed but didn't, or if it should have failed but didn't */
> + if ((ret < 0) == (can_parse_float(num_valid_float_strs[i].result, type) > 0)) {
> + printf("Error: parser behaves unexpectedly when parsing %s as %s!\n",
> + num_valid_float_strs[i].str, buf);
> + return -1;
> + }
> + /* check if result matches */
> + if (ret > 0 && float_cmp(num_valid_float_strs[i].result, &result, type) != 0) {
> + printf("Error: parsing %s as %s failed: result mismatch!\n",
> + num_valid_float_strs[i].str, buf);
> + return -1;
> + }
> + }
> + }
> +
> /** garbage strings **/
>
> /* cycle through all possible parsed types */
> for (type = RTE_UINT8; type <= RTE_INT64; type++) {
> + uint64_t result;
> token.num_data.type = type;
>
> /* test positive garbage strings */
> diff --git a/buildtools/dpdk-cmdline-gen.py b/buildtools/dpdk-cmdline-gen.py
> index 7dadded783..6c76d7116a 100755
> --- a/buildtools/dpdk-cmdline-gen.py
> +++ b/buildtools/dpdk-cmdline-gen.py
> @@ -17,16 +17,18 @@
> RTE_SET_USED(cl);
> RTE_SET_USED(data);
> """
> -NUMERIC_TYPES = [
> - "UINT8",
> - "UINT16",
> - "UINT32",
> - "UINT64",
> - "INT8",
> - "INT16",
> - "INT32",
> - "INT64",
> -]
> +NUMERIC_TYPES = {
> + "UINT8": "uint8_t",
> + "UINT16": "uint16_t",
> + "UINT32": "uint32_t",
> + "UINT64": "uint64_t",
> + "INT8": "int8_t",
> + "INT16": "int16_t",
> + "INT32": "int32_t",
> + "INT64": "int64_t",
> + "FLOAT_SINGLE": "float",
> + "FLOAT_DOUBLE": "double",
> +}
>
>
> def process_command(lineno, tokens, comment):
> @@ -70,7 +72,7 @@ def process_command(lineno, tokens, comment):
> f"\tTOKEN_STRING_INITIALIZER(struct cmd_{name}_result, {t_name}, {t_val});"
> )
> elif t_type in NUMERIC_TYPES:
> - result_struct.append(f"\t{t_type.lower()}_t {t_name};")
> + result_struct.append(f"\t{NUMERIC_TYPES[t_type]} {t_name};")
> initializers.append(
> f"static cmdline_parse_token_num_t cmd_{name}_{t_name}_tok =\n"
> f"\tTOKEN_NUM_INITIALIZER(struct cmd_{name}_result, {t_name}, RTE_{t_type});"
> diff --git a/doc/guides/prog_guide/cmdline.rst b/doc/guides/prog_guide/cmdline.rst
> index e20281ceb5..447a90e32e 100644
> --- a/doc/guides/prog_guide/cmdline.rst
> +++ b/doc/guides/prog_guide/cmdline.rst
> @@ -22,6 +22,7 @@ The DPDK command-line library supports the following features:
>
> * Strings
> * Signed/unsigned 16/32/64-bit integers
> + * Single/double precision floats
> * IP Addresses
> * Ethernet Addresses
>
> @@ -68,6 +69,8 @@ The format of the list file must be:
>
> * ``<UINT16>port_id``
>
> + * ``<FLOAT_SINGLE>ratio``
> +
> * ``<IP>src_ip``
>
> * ``<IPv4>dst_ip4``
> diff --git a/doc/guides/rel_notes/release_25_07.rst b/doc/guides/rel_notes/release_25_07.rst
> index 093b85d206..54bc545110 100644
> --- a/doc/guides/rel_notes/release_25_07.rst
> +++ b/doc/guides/rel_notes/release_25_07.rst
> @@ -55,6 +55,11 @@ New Features
> Also, make sure to start the actual text at the margin.
> =======================================================
>
> +* **Added floating point numbers support to cmdline library.**
> +
> + The cmdline library now supports parsing single- and double-precision
> + floating point numbers in interactive user commands.
> +
>
> Removed Items
> -------------
> diff --git a/lib/cmdline/cmdline_parse_num.c b/lib/cmdline/cmdline_parse_num.c
> index f21796bedb..9e4d559325 100644
> --- a/lib/cmdline/cmdline_parse_num.c
> +++ b/lib/cmdline/cmdline_parse_num.c
> @@ -7,6 +7,8 @@
> #include <stdio.h>
> #include <stdint.h>
> #include <inttypes.h>
> +#include <float.h>
> +#include <math.h>
> #include <string.h>
> #include <eal_export.h>
> #include <rte_string_fns.h>
> @@ -34,6 +36,10 @@ enum num_parse_state_t {
> DEC_NEG,
> BIN,
> HEX,
> + FLOAT_POS,
> + FLOAT_NEG,
> + FLOAT_EXP_POS,
> + FLOAT_EXP_NEG,
>
> ERROR,
>
> @@ -44,12 +50,27 @@ enum num_parse_state_t {
> BIN_OK,
> DEC_NEG_OK,
> DEC_POS_OK,
> + FLOAT_POS_OK,
> + FLOAT_NEG_OK,
> + FLOAT_EXP_POS_OK,
> + FLOAT_EXP_NEG_OK,
> +};
> +
> +struct float_parse_state {
> + uint64_t dec;
> + uint64_t frac;
> + uint64_t frac_exp;
> + uint64_t exp;
> +#define FLOAT_FLAG_NEG_RES (1 << 0)
> +#define FLOAT_FLAG_NEG_EXP (1 << 1)
> + int flags;
> };
>
> /* Keep it sync with enum in .h */
> static const char * num_help[] = {
> "UINT8", "UINT16", "UINT32", "UINT64",
> "INT8", "INT16", "INT32", "INT64",
> + "SINGLE", "DOUBLE"
> };
>
> static inline int
> @@ -63,6 +84,50 @@ add_to_res(unsigned int c, uint64_t *res, unsigned int base)
> return 0;
> }
>
> +static inline int
> +check_float_result(enum cmdline_numtype res_type, struct float_parse_state *fps,
> + void *res)
> +{
> + double dec, frac, exp, result;
> +
> + /* extract parts */
> + dec = (double) fps->dec;
> + frac = (double) fps->frac * pow(10.0, -(double)fps->frac_exp);
> + exp = (double) fps->exp;
> +
> + /* exponent might be negative */
> + if (fps->flags & FLOAT_FLAG_NEG_EXP)
> + exp = pow(10.0, -exp);
> + else
> + exp = pow(10.0, exp);
> +
> + /* combine decimal, fractional, and exponent parts */
> + result = (dec + frac) * exp;
> +
> + /* check for any overflows */
> + if (isinf(frac) || isinf(exp) || isinf(result))
> + return -1;
> +
> + /* result is a valid double */
> +
> + /* check if result needs to be negative */
> + if (fps->flags & FLOAT_FLAG_NEG_RES)
> + result = -result;
> +
> + if (res_type == RTE_FLOAT_SINGLE) {
> + /* float can overflow from conversion */
> + float flt = (float)result;
> + if (isinf(flt))
> + return -1;
> + if (res) *(float *)res = flt;
> + } else if (res_type == RTE_FLOAT_DOUBLE) {
> + if (res) *(double *)res = result;
> + } else {
> + return -1;
> + }
> + return 0;
> +}
> +
> static int
> check_res_size(struct cmdline_token_num_data *nd, unsigned ressize)
> {
> @@ -87,6 +152,14 @@ check_res_size(struct cmdline_token_num_data *nd, unsigned ressize)
> if (ressize < sizeof(int64_t))
> return -1;
> break;
> + case RTE_FLOAT_SINGLE:
> + if (ressize < sizeof(float))
> + return -1;
> + break;
> + case RTE_FLOAT_DOUBLE:
> + if (ressize < sizeof(double))
> + return -1;
> + break;
> default:
> return -1;
> }
> @@ -104,6 +177,7 @@ cmdline_parse_num(cmdline_parse_token_hdr_t *tk, const char *srcbuf, void *res,
> const char * buf;
> char c;
> uint64_t res1 = 0;
> + struct float_parse_state fps = {};
>
> if (!tk)
> return -1;
> @@ -156,6 +230,10 @@ cmdline_parse_num(cmdline_parse_token_hdr_t *tk, const char *srcbuf, void *res,
> else
> st = OCTAL_OK;
> }
> + else if (c == '.') {
> + st = FLOAT_POS;
> + break;
> + }
> else {
> st = ERROR;
> }
> @@ -173,11 +251,80 @@ cmdline_parse_num(cmdline_parse_token_hdr_t *tk, const char *srcbuf, void *res,
> }
> break;
>
> + case FLOAT_POS:
> + if (c >= '0' && c <= '9') {
> + if (add_to_res(c - '0', &res1, 10) < 0)
> + st = ERROR;
> + else {
> + st = FLOAT_POS_OK;
> + fps.frac_exp++;
> + }
> + }
> + else {
> + st = ERROR;
> + }
> + break;
> +
> + case FLOAT_NEG:
> + if (c >= '0' && c <= '9') {
> + if (add_to_res(c - '0', &res1, 10) < 0)
> + st = ERROR;
> + else {
> + st = FLOAT_NEG_OK;
> + fps.frac_exp++;
> + }
> + }
> + else {
> + st = ERROR;
> + }
> + break;
> +
> + case FLOAT_EXP_POS:
> + if (c >= '0' && c <= '9') {
> + if (add_to_res(c - '0', &res1, 10) < 0)
> + st = ERROR;
> + else
> + st = FLOAT_EXP_POS_OK;
> + }
> + else if (c == '-') {
> + st = FLOAT_EXP_NEG;
> + fps.flags |= FLOAT_FLAG_NEG_EXP;
> + }
> + else {
> + st = ERROR;
> + }
> + break;
> +
> + case FLOAT_EXP_NEG:
> + if (c >= '0' && c <= '9') {
> + if (add_to_res(c - '0', &res1, 10) < 0)
> + st = ERROR;
> + else
> + st = FLOAT_EXP_NEG_OK;
> + }
> + else {
> + st = ERROR;
> + }
> + break;
> +
> case DEC_NEG_OK:
> if (c >= '0' && c <= '9') {
> if (add_to_res(c - '0', &res1, 10) < 0)
> st = ERROR;
> }
> + else if (c == '.') {
> + fps.dec = res1;
> + fps.flags |= FLOAT_FLAG_NEG_RES;
> + st = FLOAT_NEG;
> + /* erase result */
> + res1 = 0;
> + } else if (c == 'e' || c == 'E') {
> + fps.dec = res1;
> + fps.flags |= FLOAT_FLAG_NEG_RES;
> + st = FLOAT_EXP_POS;
> + /* erase result */
> + res1 = 0;
> + }
> else {
> st = ERROR;
> }
> @@ -188,11 +335,75 @@ cmdline_parse_num(cmdline_parse_token_hdr_t *tk, const char *srcbuf, void *res,
> if (add_to_res(c - '0', &res1, 10) < 0)
> st = ERROR;
> }
> + else if (c == '.') {
> + fps.dec = res1;
> + st = FLOAT_POS;
> + /* erase result */
> + res1 = 0;
> + }
> + else if (c == 'e' || c == 'E') {
> + fps.dec = res1;
> + st = FLOAT_EXP_POS;
> + /* erase result */
> + res1 = 0;
> + }
> else {
> st = ERROR;
> }
> break;
>
> + case FLOAT_POS_OK:
> + if (c >= '0' && c <= '9') {
> + if (add_to_res(c - '0', &res1, 10) < 0)
> + st = ERROR;
> + else
> + fps.frac_exp++;
> + } else if (c == 'e' || c == 'E') {
> + fps.frac = res1;
> + st = FLOAT_EXP_POS;
> + /* erase result */
> + res1 = 0;
> + } else {
> + st = ERROR;
> + }
> + break;
> +
> + case FLOAT_NEG_OK:
> + if (c >= '0' && c <= '9') {
> + if (add_to_res(c - '0', &res1, 10) < 0)
> + st = ERROR;
> + else
> + fps.frac_exp++;
> + } else if (c == 'e' || c == 'E') {
> + fps.frac = res1;
> + st = FLOAT_EXP_POS;
> + /* erase result */
> + res1 = 0;
> + } else {
> + st = ERROR;
> + }
> + break;
> +
> + case FLOAT_EXP_POS_OK:
> + /* exponent is always whole */
> + if (c >= '0' && c <= '9') {
> + if (add_to_res(c - '0', &res1, 10) < 0)
> + st = ERROR;
> + } else {
> + st = ERROR;
> + }
> + break;
> +
> + case FLOAT_EXP_NEG_OK:
> + /* exponent is always whole */
> + if (c >= '0' && c <= '9') {
> + if (add_to_res(c - '0', &res1, 10) < 0)
> + st = ERROR;
> + } else {
> + st = ERROR;
> + }
> + break;
> +
> case HEX:
> st = HEX_OK;
> /* fall-through */
> @@ -282,6 +493,12 @@ cmdline_parse_num(cmdline_parse_token_hdr_t *tk, const char *srcbuf, void *res,
> } else if (nd.type == RTE_UINT64) {
> if (res) *(uint64_t *)res = res1;
> return buf-srcbuf;
> + } else if (nd.type == RTE_FLOAT_SINGLE || nd.type == RTE_FLOAT_DOUBLE) {
> + /* parsed double from integer */
> + fps.dec = res1;
> + if (check_float_result(nd.type, &fps, res) < 0)
> + return -1;
> + return buf-srcbuf;
> } else {
> return -1;
> }
> @@ -304,6 +521,62 @@ cmdline_parse_num(cmdline_parse_token_hdr_t *tk, const char *srcbuf, void *res,
> res1 <= (uint64_t)INT64_MAX + 1) {
> if (res) *(int64_t *)res = (int64_t) (-res1);
> return buf-srcbuf;
> + } else if (nd.type == RTE_FLOAT_SINGLE || nd.type == RTE_FLOAT_DOUBLE) {
> + /* parsed double from negative integer */
> + fps.dec = res1;
> + fps.flags |= FLOAT_FLAG_NEG_RES;
> + if (check_float_result(nd.type, &fps, res) < 0)
> + return -1;
> + return buf-srcbuf;
> + } else {
> + return -1;
> + }
> + break;
> +
> + case FLOAT_POS_OK:
> + if (nd.type == RTE_FLOAT_SINGLE || nd.type == RTE_FLOAT_DOUBLE) {
> + fps.frac = res1;
> +
> + if (check_float_result(nd.type, &fps, res) < 0)
> + return -1;
> + return buf-srcbuf;
> + } else {
> + return -1;
> + }
> + break;
> +
> + case FLOAT_NEG_OK:
> + if (nd.type == RTE_FLOAT_SINGLE || nd.type == RTE_FLOAT_DOUBLE) {
> + fps.frac = res1;
> +
> + if (check_float_result(nd.type, &fps, res) < 0)
> + return -1;
> + return buf-srcbuf;
> + } else {
> + return -1;
> + }
> + break;
> +
> + case FLOAT_EXP_POS_OK:
> + /* watch for overflow in the exponent */
> + if (nd.type == RTE_FLOAT_SINGLE || nd.type == RTE_FLOAT_DOUBLE) {
> + fps.exp = res1;
> +
> + if (check_float_result(nd.type, &fps, res) < 0)
> + return -1;
> + return buf-srcbuf;
> + } else {
> + return -1;
> + }
> + break;
> +
> + case FLOAT_EXP_NEG_OK:
> + if (nd.type == RTE_FLOAT_SINGLE || nd.type == RTE_FLOAT_DOUBLE) {
> + fps.exp = res1;
> +
> + if (check_float_result(nd.type, &fps, res) < 0)
> + return -1;
> + return buf-srcbuf;
> } else {
> return -1;
> }
> diff --git a/lib/cmdline/cmdline_parse_num.h b/lib/cmdline/cmdline_parse_num.h
> index bdd0267612..b2792a2d11 100644
> --- a/lib/cmdline/cmdline_parse_num.h
> +++ b/lib/cmdline/cmdline_parse_num.h
> @@ -22,7 +22,9 @@ enum cmdline_numtype {
> RTE_INT8,
> RTE_INT16,
> RTE_INT32,
> - RTE_INT64
> + RTE_INT64,
> + RTE_FLOAT_SINGLE,
> + RTE_FLOAT_DOUBLE,
> };
>
> struct cmdline_token_num_data {
> --
> 2.47.1
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v4 1/2] cmdline: add floating point support
2025-05-07 10:35 ` [PATCH v4 1/2] cmdline: add floating point support Konstantin Ananyev
@ 2025-05-07 11:06 ` Burakov, Anatoly
2025-05-07 12:24 ` Konstantin Ananyev
0 siblings, 1 reply; 23+ messages in thread
From: Burakov, Anatoly @ 2025-05-07 11:06 UTC (permalink / raw)
To: Konstantin Ananyev, dev
On 5/7/2025 12:35 PM, Konstantin Ananyev wrote:
>
>
>> Add support for parsing floating point numbers in cmdline library, as well
>> as unit tests for the new functionality. The parser supports single and
>> double precision floats, and will understand decimal fractions as well as
>> scientific notation.
>
> There are standard functions for that: strtod/strtof - can't we simply use them?
I can ask the same question of the entire existence of this part of the
library: there are strtoull-type functions that should be available to
all targets, so if we're going to use them for floating point parsing,
we might as well remove the number parsing part of the library entirely.
Either way is fine by me.
--
Thanks,
Anatoly
^ permalink raw reply [flat|nested] 23+ messages in thread
* RE: [PATCH v4 1/2] cmdline: add floating point support
2025-05-07 11:06 ` Burakov, Anatoly
@ 2025-05-07 12:24 ` Konstantin Ananyev
2025-05-07 14:06 ` Burakov, Anatoly
0 siblings, 1 reply; 23+ messages in thread
From: Konstantin Ananyev @ 2025-05-07 12:24 UTC (permalink / raw)
To: Burakov, Anatoly, dev
> -----Original Message-----
> From: Burakov, Anatoly <anatoly.burakov@intel.com>
> Sent: Wednesday, May 7, 2025 12:07 PM
> To: Konstantin Ananyev <konstantin.ananyev@huawei.com>; dev@dpdk.org
> Subject: Re: [PATCH v4 1/2] cmdline: add floating point support
>
> On 5/7/2025 12:35 PM, Konstantin Ananyev wrote:
> >
> >
> >> Add support for parsing floating point numbers in cmdline library, as well
> >> as unit tests for the new functionality. The parser supports single and
> >> double precision floats, and will understand decimal fractions as well as
> >> scientific notation.
> >
> > There are standard functions for that: strtod/strtof - can't we simply use them?
>
> I can ask the same question of the entire existence of this part of the
> library: there are strtoull-type functions that should be available to
> all targets,
Probably due to historical reasons - a while ago DPDK was able to run on bare-metal (not any more).
> so if we're going to use them for floating point parsing,
> we might as well remove the number parsing part of the library entirely.
Sounds like a good cleanup for me.
+1 for it.
> Either way is fine by me.
>
> --
> Thanks,
> Anatoly
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v4 1/2] cmdline: add floating point support
2025-05-07 12:24 ` Konstantin Ananyev
@ 2025-05-07 14:06 ` Burakov, Anatoly
0 siblings, 0 replies; 23+ messages in thread
From: Burakov, Anatoly @ 2025-05-07 14:06 UTC (permalink / raw)
To: Konstantin Ananyev, dev
On 5/7/2025 2:24 PM, Konstantin Ananyev wrote:
>
>
>> -----Original Message-----
>> From: Burakov, Anatoly <anatoly.burakov@intel.com>
>> Sent: Wednesday, May 7, 2025 12:07 PM
>> To: Konstantin Ananyev <konstantin.ananyev@huawei.com>; dev@dpdk.org
>> Subject: Re: [PATCH v4 1/2] cmdline: add floating point support
>>
>> On 5/7/2025 12:35 PM, Konstantin Ananyev wrote:
>>>
>>>
>>>> Add support for parsing floating point numbers in cmdline library, as well
>>>> as unit tests for the new functionality. The parser supports single and
>>>> double precision floats, and will understand decimal fractions as well as
>>>> scientific notation.
>>>
>>> There are standard functions for that: strtod/strtof - can't we simply use them?
>>
>> I can ask the same question of the entire existence of this part of the
>> library: there are strtoull-type functions that should be available to
>> all targets,
>
> Probably due to historical reasons - a while ago DPDK was able to run on bare-metal (not any more).
>
>> so if we're going to use them for floating point parsing,
>> we might as well remove the number parsing part of the library entirely.
>
> Sounds like a good cleanup for me.
> +1 for it.
>
>
>> Either way is fine by me.
>>
>> --
>> Thanks,
>> Anatoly
>
There's a small number of differences between what DPDK can do and what
strtoull/strtod can do. For example strtoull doesn't support binary
while we do. Strtoull has a concept of "negative hex numbers" while we
consider them to be invalid. Still, seems to work now so I'll submit a
v5 with the rework.
--
Thanks,
Anatoly
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH v5 1/3] cmdline: use C standard library as number parser
2025-05-02 12:27 [PATCH v1 1/1] app/testpmd: add sleep command Anatoly Burakov
` (4 preceding siblings ...)
2025-05-07 10:01 ` [PATCH v4 " Anatoly Burakov
@ 2025-05-07 15:22 ` Anatoly Burakov
2025-05-07 15:22 ` [PATCH v5 2/3] cmdline: add floating point support Anatoly Burakov
2025-05-07 15:22 ` [PATCH v5 3/3] app/testpmd: add sleep command Anatoly Burakov
5 siblings, 2 replies; 23+ messages in thread
From: Anatoly Burakov @ 2025-05-07 15:22 UTC (permalink / raw)
To: dev
Remove custom number parser and use C standard library instead. In order to
keep compatibility with earlier versions of the parser, we have to take
into account a few quirks:
- We do not consider "negative" numbers to be valid for anything other than
base-10 numbers, whereas C standard library does. We work around that by
forcing base-10 when parsing numbers we expect to be negative.
- We do not consider numbers such as "+4" to be valid, whereas C standard
library does. No one probably relies on this, so we just remove it from
our tests, as it is now a valid number.
- C standard library's strtoull does not do range checks on negative
numbers, so we have to parse knowingly-negative numbers as signed. We've
already changed this to be the case on account of point 1 from this list,
so no biggie.
- C standard library does not support binary numbers, so we keep around the
relevant parts of the custom parser in place to support binary numbers.
Signed-off-by: Anatoly Burakov <anatoly.burakov@intel.com>
---
app/test/test_cmdline_num.c | 1 -
lib/cmdline/cmdline_parse_num.c | 353 +++++++++++++++-----------------
2 files changed, 167 insertions(+), 187 deletions(-)
diff --git a/app/test/test_cmdline_num.c b/app/test/test_cmdline_num.c
index 9276de59bd..471e9964ee 100644
--- a/app/test/test_cmdline_num.c
+++ b/app/test/test_cmdline_num.c
@@ -205,7 +205,6 @@ const char * num_invalid_strs[] = {
"1E3",
"0A",
"-B",
- "+4",
"1.23G",
"",
" ",
diff --git a/lib/cmdline/cmdline_parse_num.c b/lib/cmdline/cmdline_parse_num.c
index f21796bedb..f7ccd26d96 100644
--- a/lib/cmdline/cmdline_parse_num.c
+++ b/lib/cmdline/cmdline_parse_num.c
@@ -4,6 +4,7 @@
* All rights reserved.
*/
+#include <errno.h>
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
@@ -29,23 +30,6 @@ struct cmdline_token_ops cmdline_token_num_ops = {
.get_help = cmdline_get_help_num,
};
-enum num_parse_state_t {
- START,
- DEC_NEG,
- BIN,
- HEX,
-
- ERROR,
-
- FIRST_OK, /* not used */
- ZERO_OK,
- HEX_OK,
- OCTAL_OK,
- BIN_OK,
- DEC_NEG_OK,
- DEC_POS_OK,
-};
-
/* Keep it sync with enum in .h */
static const char * num_help[] = {
"UINT8", "UINT16", "UINT32", "UINT64",
@@ -53,13 +37,13 @@ static const char * num_help[] = {
};
static inline int
-add_to_res(unsigned int c, uint64_t *res, unsigned int base)
+add_to_bin(unsigned int c, uint64_t *res)
{
/* overflow */
- if ((UINT64_MAX - c) / base < *res)
+ if ((UINT64_MAX - c) / 2 < *res)
return -1;
- *res = (uint64_t) (*res * base + c);
+ *res = (uint64_t) (*res * 2 + c);
return 0;
}
@@ -93,133 +77,106 @@ check_res_size(struct cmdline_token_num_data *nd, unsigned ressize)
return 0;
}
-/* parse an int */
-RTE_EXPORT_SYMBOL(cmdline_parse_num)
-int
-cmdline_parse_num(cmdline_parse_token_hdr_t *tk, const char *srcbuf, void *res,
- unsigned ressize)
+static int
+check_parsed_num(enum cmdline_numtype type, int neg, uint64_t uintres)
{
- struct cmdline_token_num_data nd;
- enum num_parse_state_t st = START;
+ int lo_ok, hi_ok;
+
+ switch (type) {
+ case RTE_UINT8:
+ if (neg || uintres > UINT8_MAX)
+ return -1;
+ return 0;
+ case RTE_UINT16:
+ if (neg || uintres > UINT16_MAX)
+ return -1;
+ return 0;
+ case RTE_UINT32:
+ if (neg || uintres > UINT32_MAX)
+ return -1;
+ return 0;
+ case RTE_UINT64:
+ if (neg)
+ return -1;
+ return 0;
+ case RTE_INT8:
+ lo_ok = !neg || (int64_t)uintres >= INT8_MIN;
+ hi_ok = neg || uintres <= INT8_MAX;
+ break;
+ case RTE_INT16:
+ lo_ok = !neg || (int64_t)uintres >= INT16_MIN;
+ hi_ok = neg || uintres <= INT16_MAX;
+ break;
+ case RTE_INT32:
+ lo_ok = !neg || (int64_t)uintres >= INT32_MIN;
+ hi_ok = neg || uintres <= INT32_MAX;
+ break;
+ case RTE_INT64:
+ lo_ok = 1; /* always valid */
+ hi_ok = neg || uintres <= INT64_MAX;
+ break;
+ default:
+ return -1;
+ }
+ /* check ranges */
+ if (!lo_ok || !hi_ok)
+ return -1;
+ return 0;
+}
+
+static int
+parse_num(const char *srcbuf, uint64_t *resptr)
+{
+ uint64_t uintres;
+ char *end;
+ int neg = *srcbuf == '-';
+
+ errno = 0;
+ if (neg)
+ /* for negatives, only support base-10 */
+ uintres = (uint64_t)strtoll(srcbuf, &end, 10);
+ else
+ /* 0 means autodetect base */
+ uintres = strtoull(srcbuf, &end, 0);
+
+ if (end == srcbuf || !cmdline_isendoftoken(*end) || errno == ERANGE)
+ return -1;
+ *resptr = uintres;
+ return end - srcbuf;
+}
+
+static int
+parse_bin(const char *srcbuf, uint64_t *res)
+{
+ uint64_t uintres = 0;
+ enum {
+ ERROR,
+ START,
+ BIN,
+ ZERO_OK,
+ BIN_OK,
+ } st = START;
const char * buf;
char c;
- uint64_t res1 = 0;
-
- if (!tk)
- return -1;
-
- if (!srcbuf || !*srcbuf)
- return -1;
buf = srcbuf;
c = *buf;
-
- memcpy(&nd, &((struct cmdline_token_num *)tk)->num_data, sizeof(nd));
-
- /* check that we have enough room in res */
- if (res) {
- if (check_res_size(&nd, ressize) < 0)
- return -1;
- }
-
while (st != ERROR && c && !cmdline_isendoftoken(c)) {
debug_printf("%c %x -> ", c, c);
switch (st) {
case START:
- if (c == '-') {
- st = DEC_NEG;
- }
- else if (c == '0') {
+ if (c == '0') {
st = ZERO_OK;
}
- else if (c >= '1' && c <= '9') {
- if (add_to_res(c - '0', &res1, 10) < 0)
- st = ERROR;
- else
- st = DEC_POS_OK;
- }
- else {
+ else {
st = ERROR;
}
break;
case ZERO_OK:
- if (c == 'x') {
- st = HEX;
- }
- else if (c == 'b') {
+ if (c == 'b') {
st = BIN;
}
- else if (c >= '0' && c <= '7') {
- if (add_to_res(c - '0', &res1, 10) < 0)
- st = ERROR;
- else
- st = OCTAL_OK;
- }
- else {
- st = ERROR;
- }
- break;
-
- case DEC_NEG:
- if (c >= '0' && c <= '9') {
- if (add_to_res(c - '0', &res1, 10) < 0)
- st = ERROR;
- else
- st = DEC_NEG_OK;
- }
- else {
- st = ERROR;
- }
- break;
-
- case DEC_NEG_OK:
- if (c >= '0' && c <= '9') {
- if (add_to_res(c - '0', &res1, 10) < 0)
- st = ERROR;
- }
- else {
- st = ERROR;
- }
- break;
-
- case DEC_POS_OK:
- if (c >= '0' && c <= '9') {
- if (add_to_res(c - '0', &res1, 10) < 0)
- st = ERROR;
- }
- else {
- st = ERROR;
- }
- break;
-
- case HEX:
- st = HEX_OK;
- /* fall-through */
- case HEX_OK:
- if (c >= '0' && c <= '9') {
- if (add_to_res(c - '0', &res1, 16) < 0)
- st = ERROR;
- }
- else if (c >= 'a' && c <= 'f') {
- if (add_to_res(c - 'a' + 10, &res1, 16) < 0)
- st = ERROR;
- }
- else if (c >= 'A' && c <= 'F') {
- if (add_to_res(c - 'A' + 10, &res1, 16) < 0)
- st = ERROR;
- }
- else {
- st = ERROR;
- }
- break;
-
-
- case OCTAL_OK:
- if (c >= '0' && c <= '7') {
- if (add_to_res(c - '0', &res1, 8) < 0)
- st = ERROR;
- }
else {
st = ERROR;
}
@@ -230,7 +187,7 @@ cmdline_parse_num(cmdline_parse_token_hdr_t *tk, const char *srcbuf, void *res,
/* fall-through */
case BIN_OK:
if (c >= '0' && c <= '1') {
- if (add_to_res(c - '0', &res1, 2) < 0)
+ if (add_to_bin(c - '0', &uintres) < 0)
st = ERROR;
}
else {
@@ -239,10 +196,10 @@ cmdline_parse_num(cmdline_parse_token_hdr_t *tk, const char *srcbuf, void *res,
break;
default:
debug_printf("not impl ");
-
+ st = ERROR;
}
- debug_printf("(%"PRIu64")\n", res1);
+ debug_printf("(%"PRIu64")\n", uintres);
buf ++;
c = *buf;
@@ -252,66 +209,90 @@ cmdline_parse_num(cmdline_parse_token_hdr_t *tk, const char *srcbuf, void *res,
return -1;
}
- switch (st) {
- case ZERO_OK:
- case DEC_POS_OK:
- case HEX_OK:
- case OCTAL_OK:
- case BIN_OK:
- if (nd.type == RTE_INT8 && res1 <= INT8_MAX) {
- if (res) *(int8_t *)res = (int8_t) res1;
- return buf-srcbuf;
- } else if (nd.type == RTE_INT16 && res1 <= INT16_MAX) {
- if (res) *(int16_t *)res = (int16_t) res1;
- return buf-srcbuf;
- } else if (nd.type == RTE_INT32 && res1 <= INT32_MAX) {
- if (res) *(int32_t *)res = (int32_t) res1;
- return buf-srcbuf;
- } else if (nd.type == RTE_INT64 && res1 <= INT64_MAX) {
- if (res) *(int64_t *)res = (int64_t) res1;
- return buf-srcbuf;
- } else if (nd.type == RTE_UINT8 && res1 <= UINT8_MAX) {
- if (res) *(uint8_t *)res = (uint8_t) res1;
- return buf-srcbuf;
- } else if (nd.type == RTE_UINT16 && res1 <= UINT16_MAX) {
- if (res) *(uint16_t *)res = (uint16_t) res1;
- return buf-srcbuf;
- } else if (nd.type == RTE_UINT32 && res1 <= UINT32_MAX) {
- if (res) *(uint32_t *)res = (uint32_t) res1;
- return buf-srcbuf;
- } else if (nd.type == RTE_UINT64) {
- if (res) *(uint64_t *)res = res1;
- return buf-srcbuf;
- } else {
+ if (st != BIN_OK)
+ return -1;
+
+ *res = uintres;
+ return buf - srcbuf;
+}
+
+/* parse an int */
+RTE_EXPORT_SYMBOL(cmdline_parse_num)
+int
+cmdline_parse_num(cmdline_parse_token_hdr_t *tk, const char *srcbuf, void *res,
+ unsigned ressize)
+{
+ struct cmdline_token_num_data nd;
+
+ if (!tk)
+ return -1;
+
+ if (!srcbuf || !*srcbuf)
+ return -1;
+
+ memcpy(&nd, &((struct cmdline_token_num *)tk)->num_data, sizeof(nd));
+
+ /* check that we have enough room in res */
+ if (res) {
+ if (check_res_size(&nd, ressize) < 0)
return -1;
+ }
+
+ if (nd.type >= RTE_UINT8 && nd.type <= RTE_INT64) {
+ int ret, neg = *srcbuf == '-';
+ uint64_t uintres;
+
+ /*
+ * for backwards compatibility with previous iterations of
+ * cmdline library, we need to take into account a few things:
+ *
+ * - we only support negatives when they're decimal
+ * - we support binary which isn't supported by C parsers
+ * - strtoull does not do range checks on negative numbers
+ */
+ ret = parse_num(srcbuf, &uintres);
+
+ if (ret < 0) {
+ /* parse failed, try parsing as binary */
+ ret = parse_bin(srcbuf, &uintres);
+ if (ret < 0)
+ return -1;
}
- break;
-
- case DEC_NEG_OK:
- if (nd.type == RTE_INT8 &&
- res1 <= INT8_MAX + 1) {
- if (res) *(int8_t *)res = (int8_t) (-res1);
- return buf-srcbuf;
- } else if (nd.type == RTE_INT16 &&
- res1 <= (uint16_t)INT16_MAX + 1) {
- if (res) *(int16_t *)res = (int16_t) (-res1);
- return buf-srcbuf;
- } else if (nd.type == RTE_INT32 &&
- res1 <= (uint32_t)INT32_MAX + 1) {
- if (res) *(int32_t *)res = (int32_t) (-res1);
- return buf-srcbuf;
- } else if (nd.type == RTE_INT64 &&
- res1 <= (uint64_t)INT64_MAX + 1) {
- if (res) *(int64_t *)res = (int64_t) (-res1);
- return buf-srcbuf;
- } else {
+ /* check if we're within valid range */
+ if (check_parsed_num(nd.type, neg, uintres) < 0)
+ return -1;
+
+ switch (nd.type) {
+ case RTE_UINT8:
+ if (res) *(uint8_t *)res = (uint8_t)uintres;
+ break;
+ case RTE_UINT16:
+ if (res) *(uint16_t *)res = (uint16_t)uintres;
+ break;
+ case RTE_UINT32:
+ if (res) *(uint32_t *)res = (uint32_t)uintres;
+ break;
+ case RTE_UINT64:
+ if (res) *(uint64_t *)res = uintres;
+ break;
+ case RTE_INT8:
+ if (res) *(int8_t *)res = (int8_t)uintres;
+ break;
+ case RTE_INT16:
+ if (res) *(int16_t *)res = (int16_t)uintres;
+ break;
+ case RTE_INT32:
+ if (res) *(int32_t *)res = (int32_t)uintres;
+ break;
+ case RTE_INT64:
+ if (res) *(int64_t *)res = (int64_t)uintres;
+ break;
+ default:
return -1;
}
- break;
- default:
- debug_printf("error\n");
- return -1;
+ return ret;
}
+ return -1;
}
--
2.47.1
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH v5 2/3] cmdline: add floating point support
2025-05-07 15:22 ` [PATCH v5 1/3] cmdline: use C standard library as number parser Anatoly Burakov
@ 2025-05-07 15:22 ` Anatoly Burakov
2025-05-07 15:22 ` [PATCH v5 3/3] app/testpmd: add sleep command Anatoly Burakov
1 sibling, 0 replies; 23+ messages in thread
From: Anatoly Burakov @ 2025-05-07 15:22 UTC (permalink / raw)
To: dev
Add support for parsing floating point numbers in cmdline library, as well
as unit tests for the new functionality. Use C library for parsing.
Signed-off-by: Anatoly Burakov <anatoly.burakov@intel.com>
---
Notes:
v4 -> v5:
- Reworked to use standard C library functions as much as possible,
keeping near-100% compatibility with earlier versions (the only
difference is that strings like "+4" are now considered valid)
v3 -> v4:
- Removed unnecessary check for integer overflow when parsing negative
floats (as we convert to double before changing sign)
- Make naming of float exponent states more consistent
v2 -> v3:
- Fixed a bug where a free-standing negative exponent ("1e-") would attempt to be
parsed, and added unit tests for this case
- Added support for floats in dpdk-cmdline-gen script
- Added documentation updates to call out float support
app/test/test_cmdline_num.c | 205 ++++++++++++++++++++++++-
buildtools/dpdk-cmdline-gen.py | 24 +--
doc/guides/prog_guide/cmdline.rst | 3 +
doc/guides/rel_notes/release_25_07.rst | 5 +
lib/cmdline/cmdline_parse_num.c | 34 +++-
lib/cmdline/cmdline_parse_num.h | 4 +-
6 files changed, 255 insertions(+), 20 deletions(-)
diff --git a/app/test/test_cmdline_num.c b/app/test/test_cmdline_num.c
index 471e9964ee..db3b286601 100644
--- a/app/test/test_cmdline_num.c
+++ b/app/test/test_cmdline_num.c
@@ -5,6 +5,8 @@
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
+#include <float.h>
+#include <math.h>
#include <rte_string_fns.h>
@@ -23,6 +25,11 @@ struct num_signed_str {
int64_t result;
};
+struct num_float_str {
+ const char * str;
+ double result;
+};
+
const struct num_unsigned_str num_valid_positive_strs[] = {
/* decimal positive */
{"0", 0 },
@@ -141,6 +148,63 @@ const struct num_signed_str num_valid_negative_strs[] = {
{"-9223372036854775808", INT64_MIN },
};
+const struct num_float_str num_valid_float_strs[] = {
+ /* zero */
+ {"0", 0},
+ /* parse int as float */
+ {"1", 1},
+ {"-1", -1},
+ /* fractional */
+ {"1.23", 1.23},
+ {"-1.23", -1.23},
+ {"0.123", 0.123},
+ {"-0.123", -0.123},
+ {"123.456", 123.456},
+ {"-123.456", -123.456},
+ /* positive exponent */
+ {"1e2", 1e2},
+ {"-1e2", -1e2},
+ {"1E2", 1E2},
+ {"-1E2", -1E2},
+ {"0.12e3", 0.12e3},
+ {"-0.12e3", -0.12e3},
+ {"1.23e4", 1.23e4},
+ {"-1.23e4", -1.23e4},
+ {"1.23E4", 1.23E4},
+ {"-1.23E4", -1.23E4},
+ {"123.456e7", 123.456e7},
+ {"-123.456e7", -123.456e7},
+ {"123.456E7", 123.456E7},
+ {"-123.456E7", -123.456E7},
+ /* negative exponent */
+ {"1e-2", 1e-2},
+ {"-1e-2", -1e-2},
+ {"1E-2", 1E-2},
+ {"-1E-2", -1E-2},
+ {"0.12e-3", 0.12e-3},
+ {"-0.12e-3", -0.12e-3},
+ {"1.23e-4", 1.23e-4},
+ {"-1.23e-4", -1.23e-4},
+ {"1.23E-4", 1.23E-4},
+ {"-1.23E-4", -1.23E-4},
+ {"123.456e-7", 123.456e-7},
+ {"-123.456e-7", -123.456e-7},
+ {"123.456E-7", 123.456E-7},
+ {"-123.456E-7", -123.456E-7},
+ /* try overflowing float */
+ {"2e63", 2e63},
+ {"-2e63", -2e63},
+ {"2E63", 2E63},
+ {"-2E63", -2E63},
+ {"18446744073709551615", (double) UINT64_MAX},
+ {"-9223372036854775808", (double) INT64_MIN},
+ /* try overflowing double */
+ {"2e308", HUGE_VAL},
+ {"-2e308", -HUGE_VAL},
+ {"2E308", HUGE_VAL},
+ {"-2E308", HUGE_VAL},
+};
+
const struct num_unsigned_str num_garbage_positive_strs[] = {
/* valid strings with garbage on the end, should still be valid */
/* decimal */
@@ -183,6 +247,26 @@ const struct num_signed_str num_garbage_negative_strs[] = {
{"-9223372036854775808 garbage", INT64_MIN },
};
+const char *float_invalid_strs[] = {
+ "1.1.",
+ "1.1.1",
+ "-1.1.",
+ "-1.1.1",
+ "e",
+ "1e",
+ "-1e",
+ "0.1e",
+ "-0.1e",
+ "1.e",
+ "-1.e",
+ "1.23e3.4",
+ "-1.23e3.4",
+ "1e1e",
+ "1e1e1",
+ "1e-",
+ "-1e-"
+};
+
const char * num_invalid_strs[] = {
"18446744073709551616", /* out of range unsigned */
"-9223372036854775809", /* out of range negative signed */
@@ -202,7 +286,16 @@ const char * num_invalid_strs[] = {
/* too long (128+ chars) */
("0b1111000011110000111100001111000011110000111100001111000011110000"
"1111000011110000111100001111000011110000111100001111000011110000"),
+ /* valid float values but should fail to parse as ints */
"1E3",
+ "-1E3",
+ "1.23",
+ "-1.23",
+ "1E-3",
+ "-1E-3",
+ "1.23E4",
+ "-1.23E4",
+ /* misc invalid values */
"0A",
"-B",
"1.23G",
@@ -215,6 +308,47 @@ const char * num_invalid_strs[] = {
"\0",
};
+static int
+float_cmp(double expected, void *actual_p, enum cmdline_numtype type)
+{
+ double eps;
+ double actual_d;
+
+ if (type == RTE_FLOAT_SINGLE) {
+ /* read as float, convert to double */
+ actual_d = (double)*(float *)actual_p;
+ /* FLT_EPSILON is too small for some tests */
+ eps = 1e-5f;
+ } else {
+ /* read as double */
+ actual_d = *(double *)actual_p;
+ eps = DBL_EPSILON;
+ }
+ /* compare using epsilon value */
+ if (fabs(expected - actual_d) < eps)
+ return 0;
+ /* not equal */
+ return expected < actual_d ? -1 : 1;
+}
+
+static int
+can_parse_float(double expected_result, enum cmdline_numtype type)
+{
+ switch (type) {
+ case RTE_FLOAT_SINGLE:
+ if (expected_result > FLT_MAX || expected_result < -FLT_MAX)
+ return 0;
+ break;
+ case RTE_FLOAT_DOUBLE:
+ if (expected_result > DBL_MAX || expected_result < -DBL_MAX)
+ return 0;
+ break;
+ default:
+ return 1;
+ }
+ return 1;
+}
+
static int
can_parse_unsigned(uint64_t expected_result, enum cmdline_numtype type)
{
@@ -370,11 +504,11 @@ test_parse_num_invalid_data(void)
int ret = 0;
unsigned i;
char buf[CMDLINE_TEST_BUFSIZE];
- uint64_t result; /* pick largest buffer */
cmdline_parse_token_num_t token;
- /* cycle through all possible parsed types */
+ /* cycle through all possible integer types */
for (type = RTE_UINT8; type <= RTE_INT64; type++) {
+ uint64_t result; /* pick largest buffer */
token.num_data.type = type;
/* test full strings */
@@ -396,6 +530,31 @@ test_parse_num_invalid_data(void)
}
}
}
+
+ /* cycle through all possible float types */
+ for (type = RTE_FLOAT_SINGLE; type <= RTE_FLOAT_DOUBLE; type++) {
+ double result; /* pick largest buffer */
+ token.num_data.type = type;
+
+ /* test full strings */
+ for (i = 0; i < RTE_DIM(float_invalid_strs); i++) {
+
+ memset(&result, 0, sizeof(double));
+ memset(&buf, 0, sizeof(buf));
+
+ ret = cmdline_parse_num((cmdline_parse_token_hdr_t*)&token,
+ float_invalid_strs[i], (void*)&result, sizeof(result));
+ if (ret != -1) {
+ /* get some info about what we are trying to parse */
+ cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token,
+ buf, sizeof(buf));
+
+ printf("Error: parsing %s as %s succeeded!\n",
+ float_invalid_strs[i], buf);
+ return -1;
+ }
+ }
+ }
return 0;
}
@@ -407,13 +566,13 @@ test_parse_num_valid(void)
enum cmdline_numtype type;
unsigned i;
char buf[CMDLINE_TEST_BUFSIZE];
- uint64_t result;
cmdline_parse_token_num_t token;
/** valid strings **/
- /* cycle through all possible parsed types */
+ /* cycle through all possible integer types */
for (type = RTE_UINT8; type <= RTE_INT64; type++) {
+ uint64_t result;
token.num_data.type = type;
/* test positive strings */
@@ -452,7 +611,7 @@ test_parse_num_valid(void)
cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token,
buf, sizeof(buf));
- ret = cmdline_parse_num((cmdline_parse_token_hdr_t*) &token,
+ ret = cmdline_parse_num((cmdline_parse_token_hdr_t*) &token,
num_valid_negative_strs[i].str,
(void*)&result, sizeof(result));
@@ -488,10 +647,44 @@ test_parse_num_valid(void)
}
}
+ /* cycle through all possible float types */
+ for (type = RTE_FLOAT_SINGLE; type <= RTE_FLOAT_DOUBLE; type++) {
+ double result;
+ token.num_data.type = type;
+
+ /* test all valid strings */
+ for (i = 0; i < RTE_DIM(num_valid_float_strs); i++) {
+ result = 0;
+ memset(&buf, 0, sizeof(buf));
+
+
+ cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token,
+ buf, sizeof(buf));
+
+ ret = cmdline_parse_num((cmdline_parse_token_hdr_t*) &token,
+ num_valid_float_strs[i].str,
+ (void*)&result, sizeof(result));
+
+ /* if it should have passed but didn't, or if it should have failed but didn't */
+ if ((ret < 0) == (can_parse_float(num_valid_float_strs[i].result, type) > 0)) {
+ printf("Error: parser behaves unexpectedly when parsing %s as %s!\n",
+ num_valid_float_strs[i].str, buf);
+ return -1;
+ }
+ /* check if result matches */
+ if (ret > 0 && float_cmp(num_valid_float_strs[i].result, &result, type) != 0) {
+ printf("Error: parsing %s as %s failed: result mismatch!\n",
+ num_valid_float_strs[i].str, buf);
+ return -1;
+ }
+ }
+ }
+
/** garbage strings **/
- /* cycle through all possible parsed types */
+ /* cycle through all possible integer types */
for (type = RTE_UINT8; type <= RTE_INT64; type++) {
+ uint64_t result;
token.num_data.type = type;
/* test positive garbage strings */
diff --git a/buildtools/dpdk-cmdline-gen.py b/buildtools/dpdk-cmdline-gen.py
index 7dadded783..6c76d7116a 100755
--- a/buildtools/dpdk-cmdline-gen.py
+++ b/buildtools/dpdk-cmdline-gen.py
@@ -17,16 +17,18 @@
RTE_SET_USED(cl);
RTE_SET_USED(data);
"""
-NUMERIC_TYPES = [
- "UINT8",
- "UINT16",
- "UINT32",
- "UINT64",
- "INT8",
- "INT16",
- "INT32",
- "INT64",
-]
+NUMERIC_TYPES = {
+ "UINT8": "uint8_t",
+ "UINT16": "uint16_t",
+ "UINT32": "uint32_t",
+ "UINT64": "uint64_t",
+ "INT8": "int8_t",
+ "INT16": "int16_t",
+ "INT32": "int32_t",
+ "INT64": "int64_t",
+ "FLOAT_SINGLE": "float",
+ "FLOAT_DOUBLE": "double",
+}
def process_command(lineno, tokens, comment):
@@ -70,7 +72,7 @@ def process_command(lineno, tokens, comment):
f"\tTOKEN_STRING_INITIALIZER(struct cmd_{name}_result, {t_name}, {t_val});"
)
elif t_type in NUMERIC_TYPES:
- result_struct.append(f"\t{t_type.lower()}_t {t_name};")
+ result_struct.append(f"\t{NUMERIC_TYPES[t_type]} {t_name};")
initializers.append(
f"static cmdline_parse_token_num_t cmd_{name}_{t_name}_tok =\n"
f"\tTOKEN_NUM_INITIALIZER(struct cmd_{name}_result, {t_name}, RTE_{t_type});"
diff --git a/doc/guides/prog_guide/cmdline.rst b/doc/guides/prog_guide/cmdline.rst
index e20281ceb5..447a90e32e 100644
--- a/doc/guides/prog_guide/cmdline.rst
+++ b/doc/guides/prog_guide/cmdline.rst
@@ -22,6 +22,7 @@ The DPDK command-line library supports the following features:
* Strings
* Signed/unsigned 16/32/64-bit integers
+ * Single/double precision floats
* IP Addresses
* Ethernet Addresses
@@ -68,6 +69,8 @@ The format of the list file must be:
* ``<UINT16>port_id``
+ * ``<FLOAT_SINGLE>ratio``
+
* ``<IP>src_ip``
* ``<IPv4>dst_ip4``
diff --git a/doc/guides/rel_notes/release_25_07.rst b/doc/guides/rel_notes/release_25_07.rst
index 093b85d206..54bc545110 100644
--- a/doc/guides/rel_notes/release_25_07.rst
+++ b/doc/guides/rel_notes/release_25_07.rst
@@ -55,6 +55,11 @@ New Features
Also, make sure to start the actual text at the margin.
=======================================================
+* **Added floating point numbers support to cmdline library.**
+
+ The cmdline library now supports parsing single- and double-precision
+ floating point numbers in interactive user commands.
+
Removed Items
-------------
diff --git a/lib/cmdline/cmdline_parse_num.c b/lib/cmdline/cmdline_parse_num.c
index f7ccd26d96..4ffc58aeb5 100644
--- a/lib/cmdline/cmdline_parse_num.c
+++ b/lib/cmdline/cmdline_parse_num.c
@@ -8,6 +8,8 @@
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
+#include <float.h>
+#include <math.h>
#include <string.h>
#include <eal_export.h>
#include <rte_string_fns.h>
@@ -34,6 +36,7 @@ struct cmdline_token_ops cmdline_token_num_ops = {
static const char * num_help[] = {
"UINT8", "UINT16", "UINT32", "UINT64",
"INT8", "INT16", "INT32", "INT64",
+ "FLOAT_SINGLE", "FLOAT_DOUBLE"
};
static inline int
@@ -71,6 +74,14 @@ check_res_size(struct cmdline_token_num_data *nd, unsigned ressize)
if (ressize < sizeof(int64_t))
return -1;
break;
+ case RTE_FLOAT_SINGLE:
+ if (ressize < sizeof(float))
+ return -1;
+ break;
+ case RTE_FLOAT_DOUBLE:
+ if (ressize < sizeof(double))
+ return -1;
+ break;
default:
return -1;
}
@@ -216,7 +227,7 @@ parse_bin(const char *srcbuf, uint64_t *res)
return buf - srcbuf;
}
-/* parse an int */
+/* parse a number */
RTE_EXPORT_SYMBOL(cmdline_parse_num)
int
cmdline_parse_num(cmdline_parse_token_hdr_t *tk, const char *srcbuf, void *res,
@@ -237,7 +248,7 @@ cmdline_parse_num(cmdline_parse_token_hdr_t *tk, const char *srcbuf, void *res,
if (check_res_size(&nd, ressize) < 0)
return -1;
}
-
+ /* integer parsing */
if (nd.type >= RTE_UINT8 && nd.type <= RTE_INT64) {
int ret, neg = *srcbuf == '-';
uint64_t uintres;
@@ -291,6 +302,25 @@ cmdline_parse_num(cmdline_parse_token_hdr_t *tk, const char *srcbuf, void *res,
return -1;
}
return ret;
+ /* float parsing */
+ } else if (nd.type >= RTE_FLOAT_SINGLE && nd.type <= RTE_FLOAT_DOUBLE) {
+ char *end;
+ double dres = strtod(srcbuf, &end);
+
+ if (end == srcbuf || !cmdline_isendoftoken(*end) || isinf(dres))
+ return -1;
+
+ /* we parsed something, now let's ensure it fits */
+ if (nd.type == RTE_FLOAT_SINGLE) {
+ float flt = (float)dres;
+ if (isinf(flt))
+ return -1;
+ if (res) *(float *)res = flt;
+ return end-srcbuf;
+ } else if (nd.type == RTE_FLOAT_DOUBLE) {
+ if (res) *(double *)res = dres;
+ return end-srcbuf;
+ }
}
return -1;
}
diff --git a/lib/cmdline/cmdline_parse_num.h b/lib/cmdline/cmdline_parse_num.h
index bdd0267612..b2792a2d11 100644
--- a/lib/cmdline/cmdline_parse_num.h
+++ b/lib/cmdline/cmdline_parse_num.h
@@ -22,7 +22,9 @@ enum cmdline_numtype {
RTE_INT8,
RTE_INT16,
RTE_INT32,
- RTE_INT64
+ RTE_INT64,
+ RTE_FLOAT_SINGLE,
+ RTE_FLOAT_DOUBLE,
};
struct cmdline_token_num_data {
--
2.47.1
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH v5 3/3] app/testpmd: add sleep command
2025-05-07 15:22 ` [PATCH v5 1/3] cmdline: use C standard library as number parser Anatoly Burakov
2025-05-07 15:22 ` [PATCH v5 2/3] cmdline: add floating point support Anatoly Burakov
@ 2025-05-07 15:22 ` Anatoly Burakov
1 sibling, 0 replies; 23+ messages in thread
From: Anatoly Burakov @ 2025-05-07 15:22 UTC (permalink / raw)
To: dev, Aman Singh
Test-pmd already has a way to run a list of commands from file, but there
is no way to pause execution for a specified amount of time between two
commands. This may be necessary for simple automation, particularly for
waiting on some asynchronous operation such as link status update.
Add a simple sleep command to wait until certain number of seconds has
passed, using the newly added cmdline library floating point support.
Signed-off-by: Anatoly Burakov <anatoly.burakov@intel.com>
---
Notes:
v1 -> v2:
- Add floating point support to cmdline
- Use floating point format for pause command
app/test-pmd/cmdline.c | 35 +++++++++++++++++++++++++++++++++++
1 file changed, 35 insertions(+)
diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c
index d4bb3ec998..b6152c07e6 100644
--- a/app/test-pmd/cmdline.c
+++ b/app/test-pmd/cmdline.c
@@ -151,6 +151,9 @@ static void cmd_help_long_parsed(void *parsed_result,
"quit\n"
" Quit to prompt.\n\n"
+
+ "sleep secs\n"
+ " Sleep for secs seconds (can be fractional).\n\n"
);
}
@@ -7768,6 +7771,37 @@ static cmdline_parse_inst_t cmd_quit = {
},
};
+/* *** SLEEP *** */
+struct cmd_sleep_result {
+ cmdline_fixed_string_t sleep;
+ double secs;
+};
+
+static void cmd_sleep_parsed(void *parsed_result,
+ __rte_unused struct cmdline *cl,
+ __rte_unused void *data)
+{
+ struct cmd_sleep_result *res = parsed_result;
+
+ rte_delay_us_sleep(res->secs * 1E6);
+}
+
+static cmdline_parse_token_string_t cmd_sleep_sleep =
+ TOKEN_STRING_INITIALIZER(struct cmd_sleep_result, sleep, "sleep");
+static cmdline_parse_token_num_t cmd_sleep_seconds =
+ TOKEN_NUM_INITIALIZER(struct cmd_sleep_result, secs, RTE_FLOAT_DOUBLE);
+
+static cmdline_parse_inst_t cmd_sleep = {
+ .f = cmd_sleep_parsed,
+ .data = NULL,
+ .help_str = "sleep <secs>: Sleep for a specified number of seconds",
+ .tokens = {
+ (void *)&cmd_sleep_sleep,
+ (void *)&cmd_sleep_seconds,
+ NULL,
+ },
+};
+
/* *** ADD/REMOVE MAC ADDRESS FROM A PORT *** */
struct cmd_mac_addr_result {
cmdline_fixed_string_t mac_addr_cmd;
@@ -13711,6 +13745,7 @@ static cmdline_parse_ctx_t builtin_ctx[] = {
&cmd_showdevice,
&cmd_showcfg,
&cmd_showfwdall,
+ &cmd_sleep,
&cmd_start,
&cmd_start_tx_first,
&cmd_start_tx_first_n,
--
2.47.1
^ permalink raw reply [flat|nested] 23+ messages in thread
end of thread, other threads:[~2025-05-07 15:22 UTC | newest]
Thread overview: 23+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-05-02 12:27 [PATCH v1 1/1] app/testpmd: add sleep command Anatoly Burakov
2025-05-02 12:37 ` Bruce Richardson
2025-05-02 14:35 ` Burakov, Anatoly
2025-05-02 14:43 ` Bruce Richardson
2025-05-02 15:33 ` Morten Brørup
2025-05-02 15:42 ` Stephen Hemminger
2025-05-06 12:36 ` Burakov, Anatoly
2025-05-06 13:08 ` [PATCH v2 1/2] cmdline: add floating point support Anatoly Burakov
2025-05-06 13:08 ` [PATCH v2 2/2] app/testpmd: add sleep command Anatoly Burakov
2025-05-06 13:38 ` [PATCH v2 1/2] cmdline: add floating point support Bruce Richardson
2025-05-07 9:02 ` Burakov, Anatoly
2025-05-07 9:50 ` [PATCH v3 " Anatoly Burakov
2025-05-07 9:50 ` [PATCH v3 2/2] app/testpmd: add sleep command Anatoly Burakov
2025-05-07 9:53 ` [PATCH v3 1/2] cmdline: add floating point support Burakov, Anatoly
2025-05-07 10:01 ` [PATCH v4 " Anatoly Burakov
2025-05-07 10:01 ` [PATCH v4 2/2] app/testpmd: add sleep command Anatoly Burakov
2025-05-07 10:35 ` [PATCH v4 1/2] cmdline: add floating point support Konstantin Ananyev
2025-05-07 11:06 ` Burakov, Anatoly
2025-05-07 12:24 ` Konstantin Ananyev
2025-05-07 14:06 ` Burakov, Anatoly
2025-05-07 15:22 ` [PATCH v5 1/3] cmdline: use C standard library as number parser Anatoly Burakov
2025-05-07 15:22 ` [PATCH v5 2/3] cmdline: add floating point support Anatoly Burakov
2025-05-07 15:22 ` [PATCH v5 3/3] app/testpmd: add sleep command Anatoly Burakov
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).