]> www.infradead.org Git - users/sagi/nvme-cli.git/commitdiff
util: Reimplement suffix_si_parse
authorDaniel Wagner <dwagner@suse.de>
Wed, 1 Feb 2023 15:16:00 +0000 (00:16 +0900)
committerDaniel Wagner <dwagner@suse.de>
Tue, 7 Feb 2023 15:30:43 +0000 (16:30 +0100)
suffix_si_parse() has an very awkward interface with using errno and
suffixed as return value. Let's just use the return code as status
value and use an argument for returning the value and avoid setting the
errno.

Furthermoe, this function should not know anything about LBAs, it just
supposed to parse a string with a suffix. In order to support the
previous use case we also return the endprt which allows to implement
the nsze-si parse use case and the caller side (see documentation on
create-ns).

Next problem is that suffix_is_parse uses double for calculation which
tend to get inaccurate, e.g. 6.14T results in 61399999997. Instead let's
do do all in pure integer arithmetic.

Also handle the decimal point according the locale settings.

Reviewed-by: Tokunori Ikegami <ikegami.t@gmail.com>
Signed-off-by: Daniel Wagner <dwagner@suse.de>
nvme.c
unit/test-suffix-si-parse.c
util/suffix.c
util/suffix.h

diff --git a/nvme.c b/nvme.c
index 350e8239d0b5f53ba08e1e53bca135a5ebad8434..892f99572fde468db06c3d88bc1841865bbf46a1 100644 (file)
--- a/nvme.c
+++ b/nvme.c
@@ -2809,10 +2809,10 @@ static int detach_ns(int argc, char **argv, struct command *cmd, struct plugin *
 static int parse_lba_num_si(struct nvme_dev *dev, const char *opt,
                            const char *val, __u8 flbas, __u64 *num)
 {
-       bool suffixed = false;
        struct nvme_id_ctrl ctrl;
        __u32 nsid = 1;
        struct nvme_id_ns ns;
+       char *endptr;
        int err = -EINVAL;
        int i;
        int lbas;
@@ -2873,17 +2873,17 @@ static int parse_lba_num_si(struct nvme_dev *dev, const char *opt,
        i = flbas & NVME_NS_FLBAS_LOWER_MASK;
        lbas = (1 << ns.lbaf[i].ds) + ns.lbaf[i].ms;
 
-       *num = suffix_si_parse(val, &suffixed);
-
-       if (errno)
+       if (suffix_si_parse(val, &endptr, (uint64_t*)num)) {
                fprintf(stderr,
                        "Expected long suffixed integer argument for '%s-si' but got '%s'!\n",
                        opt, val);
+               return -errno;
+       }
 
-       if (suffixed)
+       if (endptr[0] != '\0')
                *num /= lbas;
 
-       return errno;
+       return 0;
 }
 
 static int create_ns(int argc, char **argv, struct command *cmd, struct plugin *plugin)
index 879518bb601892f8e33515f339c79de79c4278e1..bc9245522687279c99cf7f2adf0660c0dd9d0e8e 100644 (file)
@@ -3,6 +3,8 @@
 #include <string.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <errno.h>
+#include <locale.h>
 
 #include "../util/suffix.h"
 #include "../util/types.h"
 
 static int test_rc;
 
-static void check_num(const char *val, int lbas, __u64 exp, __u64 num)
+static void check_num(const char *val, __u64 exp, __u64 num)
 {
        if (exp == num)
                return;
 
-       printf("ERROR: printing {%s} (lbas %d), got '%llu', expected '%llu'\n",
-              val, lbas, (unsigned long long)num, (unsigned long long)exp);
+       printf("ERROR: printing {%s} got '%llu', expected '%llu'\n",
+              val, (unsigned long long)num, (unsigned long long)exp);
 
        test_rc = 1;
 }
 
 struct tonum_test {
        const char *val;
-       int lbas;
-       const __u64 exp;
+       const uint64_t exp;
+       int ret;
 };
 
 static struct tonum_test tonum_tests[] = {
-       { "11995709440", 512, 11995709440 },
-       { "1199570940", 512, 1199570940 },
-       { "6.14T", 512, 11992187500 },
-       { "6.14T", 520, 11807692307 },
-       { "6.14T", 4096, 1499023437 },
-       { "6.14", 512, 0 },
-       { "6.14#", 512, 0 },
+       { "11995709440", 11995709440, 0 },
+       { "1199570940", 1199570940, 0},
+       { "234.567M", 234567000, 0 },
+       { "1.2k", 1200, 0 },
+       { "6.14T", 6140000000000, 0 },
+       { "123.4567k", 123456, 0 },
+       { "12345.6789101112M", 12345678910, 0},
+       { "6.14", 6, 0 },
+       { "6.14#", 0, -EINVAL },
+       { "2,33", 0, -EINVAL },
+       { "3..3", 0, -EINVAL },
+       { "123.12MM", 0, -EINVAL },
 };
 
 void tonum_test(struct tonum_test *test)
 {
-       __u64 num;
-       bool suffixed;
-
-       num = suffix_si_parse(test->val, &suffixed);
-
-       if (suffixed)
-               num /= test->lbas;
+       char *endptr;
+       uint64_t num;
+       int ret;
+
+       ret = suffix_si_parse(test->val, &endptr, &num);
+       if (ret != test->ret) {
+               printf("ERROR: converting {%s} failed\n", test->val);
+               test_rc = 1;
+               return;
+       }
+       if (ret)
+               return;
 
-       check_num(test->val, test->lbas, test->exp, num);
+       check_num(test->val, test->exp, num);
 }
 
 int main(void)
@@ -56,6 +68,7 @@ int main(void)
        unsigned int i;
 
        test_rc = 0;
+       setlocale(LC_NUMERIC, "C");
 
        for (i = 0; i < ARRAY_SIZE(tonum_tests); i++)
                tonum_test(&tonum_tests[i]);
index 41069586a121b27041f09a6d3641aabc1d0ffcb1..94bd279c69e86f556803135c2fe83c78af983e4d 100644 (file)
  */
 
 #include "suffix.h"
+#include "common.h"
 
 #include <stdlib.h>
 #include <ctype.h>
 #include <errno.h>
 #include <math.h>
+#include <float.h>
+#include <limits.h>
+#include <locale.h>
 
 static struct si_suffix {
        long double magnitude;
+       unsigned int exponent;
        const char *suffix;
 } si_suffixes[] = {
-       {1e30, "Q"},
-       {1e27, "R"},
-       {1e24, "Y"},
-       {1e21, "Z"},
-       {1e18, "E"},
-       {1e15, "P"},
-       {1e12, "T"},
-       {1e9, "G"},
-       {1e6, "M"},
-       {1e3, "k"},
-       {1e0, ""},
-       {0}
+       {1e30, 30, "Q"},
+       {1e27, 27, "R"},
+       {1e24, 24, "Y"},
+       {1e21, 21, "Z"},
+       {1e18, 18, "E"},
+       {1e15, 15, "P"},
+       {1e12, 12, "T"},
+       {1e9, 9, "G"},
+       {1e6, 6, "M"},
+       {1e3, 3, "k"},
 };
 
 const char *suffix_si_get(double *value)
@@ -65,36 +68,87 @@ const char *suffix_si_get(double *value)
        return suffix;
 }
 
-uint64_t suffix_si_parse(const char *value, bool *suffixed)
+int suffix_si_parse(const char *str, char **endptr, uint64_t *val)
 {
-       char *suffix;
-       double ret;
-       struct si_suffix *s;
-
-       errno = 0;
-       ret = strtod(value, &suffix);
-       if (errno)
+       unsigned long long num, frac;
+       char *sep, *tmp;
+       int frac_len, len, i;
+
+       num = strtoull(str, endptr, 0);
+       if (str == *endptr ||
+           ((num == ULLONG_MAX) && errno == ERANGE))
+               return -EINVAL;
+
+       /* simple number, no decimal point not suffix */
+       if ((*endptr)[0] == '\0') {
+               *val = num;
                return 0;
+       }
+
+       /* get rid of the decimal point */
+       sep = localeconv()->decimal_point;
+       if (sep)
+               len = strlen(sep);
+       else
+               len = 0;
 
-       for (s = si_suffixes; s->magnitude != 0; s++) {
-               if (suffix[0] == s->suffix[0]) {
-                       if (suffixed && suffix[0] != '\0')
-                               *suffixed = true;
-                       return ret *= s->magnitude;
+       for (i = 0; i < len; i++) {
+               if (((*endptr)[i] == '\0') || (*endptr)[i] != sep[i])
+                       return -EINVAL;
+       }
+       *endptr += len;
+       tmp = *endptr;
+
+       /* extract the digits after decimal point */
+       frac = strtoull(tmp, endptr, 0);
+       if (tmp == *endptr ||
+           ((frac == ULLONG_MAX) && errno == ERANGE))
+               return -EINVAL;
+
+       /* test that we have max one character as suffix */
+       if ((*endptr)[0] != '\0' && (*endptr)[1] != '\0')
+               return -EINVAL;
+
+       frac_len = *endptr - tmp;
+
+       for (i = 0; i < ARRAY_SIZE(si_suffixes); i++) {
+               struct si_suffix *s = &si_suffixes[i];
+
+               if ((*endptr)[0] != s->suffix[0])
+                       continue;
+
+               /* we should check for overflow */
+               for (int j = 0; j < s->exponent; j++)
+                       num *= 10;
+
+               if (s->exponent > frac_len) {
+                       for (int j = 0; j < s->exponent - frac_len;  j++)
+                               frac *= 10;
+               } else if (s->exponent < frac_len) {
+                       for (int j = 0; j < frac_len - s->exponent;  j++)
+                               frac /= 10;
+               } else {
+                       frac = 0;
                }
+
+               *val = num + frac;
+               return 0;
        }
 
-       if (suffix[0] != '\0')
-               errno = EINVAL;
+       if ((*endptr)[0] != '\0')
+               return -EINVAL;
 
-       return (uint64_t)ret;
+       *val = num;
+       return 0;
 }
 
 const char *suffix_si_get_ld(long double *value)
 {
-       struct si_suffix *s;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(si_suffixes); i++) {
+               struct si_suffix *s = &si_suffixes[i];
 
-       for (s = si_suffixes; s->magnitude != 0; s++) {
                if (*value >= s->magnitude) {
                        *value /= s->magnitude;
                        return s->suffix;
index b367ce416aebdf7cdfffcc841b280798cdc86a11..8fab823b1acb5b12e6970a0bf1af1262a124f26d 100644 (file)
@@ -36,7 +36,7 @@
 #include <stdbool.h>
 
 const char *suffix_si_get(double *value);
-uint64_t suffix_si_parse(const char *value, bool *suffixed);
+int suffix_si_parse(const char *str, char **endptr, uint64_t *val);
 const char *suffix_si_get_ld(long double *value);
 const char *suffix_binary_get(long long *value);
 const char *suffix_dbinary_get(double *value);