On Mon, Oct 28, 2019 at 03:52:10PM +0800, Tao Xu wrote: > To convert strings with time suffixes to numbers, support time unit are > "ns" for nanosecond, "us" for microsecond, "ms" for millisecond or "s" > for second. Add test for qemu_strtotime_ns, test the input of basic, > time suffixes, float, invaild, trailing and overflow. > > Signed-off-by: Tao Xu <tao3...@intel.com> > --- > > Changes in v14: > - Reuse the codes of do_strtosz to build qemu_strtotime_ns > (Eduardo) > - Squash patch v13 01/12 and 02/12 together (Daniel and Eduardo) > - Drop time unit picosecond (Eric)
Suggestion for the next version: if you are refactoring existing do_strtosz() code, please refactor it in one patch, and add new functionality in another patch. > --- > include/qemu/cutils.h | 1 + > tests/test-cutils.c | 204 ++++++++++++++++++++++++++++++++++++++++++ > util/cutils.c | 89 +++++++++++------- > 3 files changed, 262 insertions(+), 32 deletions(-) > > diff --git a/include/qemu/cutils.h b/include/qemu/cutils.h > index b54c847e0f..ff2b3f4614 100644 > --- a/include/qemu/cutils.h > +++ b/include/qemu/cutils.h > @@ -182,5 +182,6 @@ int uleb128_decode_small(const uint8_t *in, uint32_t *n); > * *str1 is <, == or > than *str2. > */ > int qemu_pstrcmp0(const char **str1, const char **str2); > +int qemu_strtotime_ns(const char *nptr, const char **end, uint64_t *result); > > #endif > diff --git a/tests/test-cutils.c b/tests/test-cutils.c > index 1aa8351520..d6a0824efd 100644 > --- a/tests/test-cutils.c > +++ b/tests/test-cutils.c [...] > +static void test_qemu_strtotime_ns_trailing(void) > +{ > + const char *str; > + const char *endptr; > + int err; > + uint64_t res = 0xbaadf00d; > + > + str = "123xxx"; > + > + err = qemu_strtotime_ns(str, NULL, &res); > + g_assert_cmpint(err, ==, -EINVAL); > + > + str = "1msxxx"; > + err = qemu_strtotime_ns(str, &endptr, &res); > + g_assert_cmpint(err, ==, 0); > + g_assert_cmpint(res, ==, 1000000); > + g_assert(endptr == str + 3); > + > + err = qemu_strtotime_ns(str, NULL, &res); > + g_assert_cmpint(err, ==, -EINVAL); > +} This is better than the test case in v13, where trailing strings were not handled consistently. Good. [...] > diff --git a/util/cutils.c b/util/cutils.c > index fd591cadf0..d83825f8b4 100644 > --- a/util/cutils.c > +++ b/util/cutils.c > @@ -181,41 +181,38 @@ int fcntl_setfl(int fd, int flag) > } > #endif > > -static int64_t suffix_mul(char suffix, int64_t unit) > -{ > - switch (qemu_toupper(suffix)) { > - case 'B': > - return 1; > - case 'K': > - return unit; > - case 'M': > - return unit * unit; > - case 'G': > - return unit * unit * unit; > - case 'T': > - return unit * unit * unit * unit; > - case 'P': > - return unit * unit * unit * unit * unit; > - case 'E': > - return unit * unit * unit * unit * unit * unit; > +static int64_t suffix_mul(const char *suffixes[], int num_suffix, > + const char *endptr, int *offset, int64_t unit) > +{ > + int i, suffix_len; > + int64_t mul = 1; > + > + for (i = 0; i < num_suffix; i++) { > + suffix_len = strlen(suffixes[i]); > + if (strlen(endptr) >= suffix_len && Is the strlen(endptr) check here really necessary? > + g_ascii_strncasecmp(suffixes[i], endptr, suffix_len) == 0) { > + *offset = suffix_len; > + return mul; > + } > + mul *= unit; > } > + > return -1; > } > > /* > - * Convert string to bytes, allowing either B/b for bytes, K/k for KB, > - * M/m for MB, G/g for GB or T/t for TB. End pointer will be returned > - * in *end, if not NULL. Return -ERANGE on overflow, and -EINVAL on > - * other error. > + * Convert string according to different suffixes. End pointer will be > returned > + * in *end, if not NULL. Return -ERANGE on overflow, and -EINVAL on other > error. > */ > -static int do_strtosz(const char *nptr, const char **end, > - const char default_suffix, int64_t unit, > - uint64_t *result) > +static int do_strtomul(const char *nptr, const char **end, > + const char *suffixes[], int num_suffix, > + const char *default_suffix, int64_t unit, > + uint64_t *result) > { > int retval; > const char *endptr; > - unsigned char c; > int mul_required = 0; > + int offset = 0; > double val, mul, integral, fraction; > > retval = qemu_strtod_finite(nptr, &endptr, &val); > @@ -226,12 +223,12 @@ static int do_strtosz(const char *nptr, const char > **end, > if (fraction != 0) { > mul_required = 1; > } > - c = *endptr; > - mul = suffix_mul(c, unit); > + > + mul = suffix_mul(suffixes, num_suffix, endptr, &offset, unit); > if (mul >= 0) { > - endptr++; > + endptr += offset; > } else { > - mul = suffix_mul(default_suffix, unit); > + mul = suffix_mul(suffixes, num_suffix, default_suffix, &offset, > unit); > assert(mul >= 0); > } > if (mul == 1 && mul_required) { > @@ -259,19 +256,47 @@ out: > return retval; > } > > +/* > + * Convert string to bytes, allowing either B/b for bytes, K/k for KB, > + * M/m for MB, G/g for GB or T/t for TB. End pointer will be returned > + * in *end, if not NULL. Return -ERANGE on overflow, and -EINVAL on > + * other error. > + */ > +static int do_strtosz(const char *nptr, const char **end, > + const char *default_suffix, int64_t unit, > + uint64_t *result) > +{ > + static const char *suffixes[] = { "B", "K", "M", "G", "T", "P", "E" }; > + > + return do_strtomul(nptr, end, suffixes, 7, default_suffix, unit, result); [1] You can use ARRAY_SIZE(suffixes) instead of hardcoding the array size. > +} > + > int qemu_strtosz(const char *nptr, const char **end, uint64_t *result) > { > - return do_strtosz(nptr, end, 'B', 1024, result); > + return do_strtosz(nptr, end, "B", 1024, result); > } > > int qemu_strtosz_MiB(const char *nptr, const char **end, uint64_t *result) > { > - return do_strtosz(nptr, end, 'M', 1024, result); > + return do_strtosz(nptr, end, "M", 1024, result); > } > > int qemu_strtosz_metric(const char *nptr, const char **end, uint64_t *result) > { > - return do_strtosz(nptr, end, 'B', 1000, result); > + return do_strtosz(nptr, end, "B", 1000, result); > +} > + > +/* > + * Convert string to time, support time unit are ns for nanosecond, us for > + * microsecond, ms for millisecond and s for second. End pointer will be > + * returned in *end, if not NULL. Return -ERANGE on overflow, and -EINVAL on > + * other error. > + */ > +int qemu_strtotime_ns(const char *nptr, const char **end, uint64_t *result) > +{ > + static const char *suffixes[] = { "ns", "us", "ms", "s" }; > + > + return do_strtomul(nptr, end, suffixes, 4, "ns", 1000, result); Same as above[1]. > } > > /** > -- > 2.20.1 > -- Eduardo