From cd766cd4802bfbb8d7bf247a47874ef6069164be Mon Sep 17 00:00:00 2001 From: Andrew Bettison Date: Tue, 20 Sep 2016 12:15:26 +0930 Subject: [PATCH] Move numeric conversions from "str.h" to "numeric_str.h" --- cli.c | 2 +- conf_schema.c | 2 +- headerfiles.mk | 1 + http_server.c | 2 +- mdp_filter.c | 2 +- meshms_cli.c | 1 + meshms_restful.c | 1 + network_cli.c | 1 + numeric_str.c | 308 +++++++++++++++++++++++++++++++++++++++++++++++ numeric_str.h | 119 ++++++++++++++++++ rhizome_bundle.c | 2 +- sourcefiles.mk | 1 + str.c | 280 +----------------------------------------- str.h | 89 +------------- tfw_createfile.c | 1 + 15 files changed, 440 insertions(+), 372 deletions(-) create mode 100644 numeric_str.c create mode 100644 numeric_str.h diff --git a/cli.c b/cli.c index 478b0168..5e170fa3 100644 --- a/cli.c +++ b/cli.c @@ -27,7 +27,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "os.h" #include "log.h" #include "debug.h" -#include "str.h" +#include "numeric_str.h" #include "strbuf_helpers.h" #include "dataformats.h" diff --git a/conf_schema.c b/conf_schema.c index ab493806..64bb0f6c 100644 --- a/conf_schema.c +++ b/conf_schema.c @@ -31,7 +31,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "debug.h" #include "mem.h" #include "str.h" -#include "strbuf.h" +#include "numeric_str.h" #include "strbuf_helpers.h" #include "conf.h" #include "dataformats.h" diff --git a/headerfiles.mk b/headerfiles.mk index 85ae83ea..9be61be1 100644 --- a/headerfiles.mk +++ b/headerfiles.mk @@ -23,6 +23,7 @@ HDRS= fifo.h \ socket.h \ cli.h \ str.h \ + numeric_str.h \ rotbuf.h \ mem.h \ os.h \ diff --git a/http_server.c b/http_server.c index 45af5004..a0fcdcb2 100644 --- a/http_server.c +++ b/http_server.c @@ -26,7 +26,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "conf.h" #include "log.h" #include "debug.h" -#include "str.h" +#include "numeric_str.h" #include "strbuf.h" #include "strbuf_helpers.h" #include "net.h" diff --git a/mdp_filter.c b/mdp_filter.c index 94f91475..84c2e6ea 100644 --- a/mdp_filter.c +++ b/mdp_filter.c @@ -26,7 +26,7 @@ #include "constants.h" #include "conf.h" #include "mem.h" -#include "str.h" +#include "numeric_str.h" #include "server.h" //#define DEBUG_MDP_FILTER_PARSING 1 diff --git a/meshms_cli.c b/meshms_cli.c index 707a09c0..6ec5db7e 100644 --- a/meshms_cli.c +++ b/meshms_cli.c @@ -8,6 +8,7 @@ #include "instance.h" #include "conf.h" #include "commandline.h" +#include "numeric_str.h" // output the list of existing conversations for a given local identity diff --git a/meshms_restful.c b/meshms_restful.c index 222b674c..1ebf88a8 100644 --- a/meshms_restful.c +++ b/meshms_restful.c @@ -20,6 +20,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "serval.h" #include "conf.h" #include "httpd.h" +#include "numeric_str.h" #include "strbuf_helpers.h" DECLARE_HANDLER("/restful/meshms/", restful_meshms_); diff --git a/network_cli.c b/network_cli.c index 8835e755..cab3e64f 100644 --- a/network_cli.c +++ b/network_cli.c @@ -29,6 +29,7 @@ #include "sighandlers.h" #include "instance.h" #include "serval.h" +#include "numeric_str.h" #include "overlay_buffer.h" diff --git a/numeric_str.c b/numeric_str.c new file mode 100644 index 00000000..ba24b9ed --- /dev/null +++ b/numeric_str.c @@ -0,0 +1,308 @@ +/* + Serval numerical string primitives + Copyright (C) 2012-2016 Serval Project Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#define __SERVAL_DNA__NUMERIC_STR_INLINE +#include "numeric_str.h" +#include "str.h" +#include "strbuf.h" +#include +#include + +int str_to_uint16(const char *str, unsigned base, uint16_t *result, const char **afterp) +{ + return strn_to_uint16(str, 0, base, result, afterp); +} + +int strn_to_uint16(const char *str, size_t strlen, unsigned base, uint16_t *result, const char **afterp) +{ + assert(base > 0); + assert(base <= 16); + uint16_t value = 0; + uint16_t newvalue = 0; + const char *const end = str + strlen; + const char *s; + for (s = str; strlen ? s < end : *s; ++s) { + int digit = hexvalue(*s); + if (digit < 0 || (unsigned)digit >= base) + break; + newvalue = value * base + digit; + if (newvalue / base != value) // overflow + break; + value = newvalue; + } + if (afterp) + *afterp = s; + if (s == str || value != newvalue || (!afterp && (strlen ? s != end : *s))) + return 0; + if (result) + *result = value; + return 1; +} + +int str_to_int32(const char *str, unsigned base, int32_t *result, const char **afterp) +{ + if (isspace(*str)) + return 0; + const char *end = str; + errno = 0; + long value = strtol(str, (char**)&end, base); + if (afterp) + *afterp = end; + if (errno == ERANGE || end == str || value > INT32_MAX || value < INT32_MIN || isdigit(*end) || (!afterp && *end)) + return 0; + if (result) + *result = value; + return 1; +} + +int str_to_uint32(const char *str, unsigned base, uint32_t *result, const char **afterp) +{ + return strn_to_uint32(str, 0, base, result, afterp); +} + +int strn_to_uint32(const char *str, size_t strlen, unsigned base, uint32_t *result, const char **afterp) +{ + assert(base > 0); + assert(base <= 16); + uint32_t value = 0; + uint32_t newvalue = 0; + const char *const end = str + strlen; + const char *s; + for (s = str; strlen ? s < end : *s; ++s) { + int digit = hexvalue(*s); + if (digit < 0 || (unsigned)digit >= base) + break; + newvalue = value * base + digit; + if (newvalue < value) // overflow + break; + value = newvalue; + } + if (afterp) + *afterp = s; + if (s == str || value > UINT32_MAX || value != newvalue || (!afterp && (strlen ? s != end : *s))) + return 0; + if (result) + *result = value; + return 1; +} + +int str_is_uint64_decimal(const char *str) +{ + return str_to_uint64(str, 10, NULL, NULL); +} + +int str_to_int64(const char *str, unsigned base, int64_t *result, const char **afterp) +{ + if (isspace(*str)) + return 0; + const char *end = str; + errno = 0; + long long value = strtoll(str, (char**)&end, base); + if (afterp) + *afterp = end; + if (errno == ERANGE || end == str || isdigit(*end) || (!afterp && *end)) + return 0; + if (result) + *result = value; + return 1; +} + +int str_to_uint64(const char *str, unsigned base, uint64_t *result, const char **afterp) +{ + return strn_to_uint64(str, 0, base, result, afterp); +} + +int strn_to_uint64(const char *str, size_t strlen, unsigned base, uint64_t *result, const char **afterp) +{ + assert(base > 0); + assert(base <= 16); + uint64_t value = 0; + uint64_t newvalue = 0; + const char *const end = str + strlen; + const char *s; + for (s = str; strlen ? s < end : *s; ++s) { + int digit = hexvalue(*s); + if (digit < 0 || (unsigned)digit >= base) + break; + newvalue = value * base + digit; + if (newvalue < value) // overflow + break; + value = newvalue; + } + if (afterp) + *afterp = s; + if (s == str || value > UINT64_MAX || value != newvalue || (!afterp && (strlen ? s != end : *s))) + return 0; + if (result) + *result = value; + return 1; +} + +static struct scale_factor { + char symbol; + uint64_t factor; +} + scale_factors[] = { + { 'P', 1024LL * 1024LL * 1024LL * 1024LL * 1024LL }, + { 'p', 1000LL * 1000LL * 1000LL * 1000LL * 1000LL }, + { 'T', 1024LL * 1024LL * 1024LL * 1024LL }, + { 't', 1000LL * 1000LL * 1000LL * 1000LL }, + { 'G', 1024LL * 1024LL * 1024LL }, + { 'g', 1000LL * 1000LL * 1000LL }, + { 'M', 1024LL * 1024LL }, + { 'm', 1000LL * 1000LL }, + { 'K', 1024LL }, + { 'k', 1000LL } + }; + +uint64_t scale_factor(const char *str, const char **afterp) +{ + uint64_t factor = 1; + int i; + for (i = 0; i != NELS(scale_factors); ++i) + if (scale_factors[i].symbol == str[0]) { + ++str; + factor = scale_factors[i].factor; + break; + } + if (afterp) + *afterp = str; + else if (*str) + factor = 0; + return factor; +} + +int str_to_int64_scaled(const char *str, unsigned base, int64_t *result, const char **afterp) +{ + int64_t value; + const char *end = str; + if (!str_to_int64(str, base, &value, &end)) { + if (afterp) + *afterp = end; + return 0; + } + value *= scale_factor(end, &end); + if (afterp) + *afterp = end; + else if (*end) + return 0; + if (result) + *result = value; + return 1; +} + +int str_to_uint32_scaled(const char *str, unsigned base, uint32_t *result, const char **afterp) +{ + uint32_t value; + const char *end = str; + if (!str_to_uint32(str, base, &value, &end)) { + if (afterp) + *afterp = end; + return 0; + } + value *= scale_factor(end, &end); + if (afterp) + *afterp = end; + else if (*end) + return 0; + if (result) + *result = value; + return 1; +} + +int uint32_scaled_to_str(char *str, size_t len, uint32_t value) +{ + char symbol = '\0'; + int i; + for (i = 0; i != NELS(scale_factors); ++i) + if (value % scale_factors[i].factor == 0) { + value /= scale_factors[i].factor; + symbol = scale_factors[i].symbol; + break; + } + strbuf b = strbuf_local(str, len); + strbuf_sprintf(b, "%lu", (unsigned long) value); + if (symbol) + strbuf_putc(b, symbol); + return strbuf_overrun(b) ? 0 : 1; +} + +int str_to_uint64_scaled(const char *str, unsigned base, uint64_t *result, const char **afterp) +{ + uint64_t value; + const char *end = str; + if (!str_to_uint64(str, base, &value, &end)) { + if (afterp) + *afterp = end; + return 0; + } + value *= scale_factor(end, &end); + if (afterp) + *afterp = end; + else if (*end) + return 0; + if (result) + *result = value; + return 1; +} + +int uint64_scaled_to_str(char *str, size_t len, uint64_t value) +{ + char symbol = '\0'; + int i; + for (i = 0; i != NELS(scale_factors); ++i) + if (value % scale_factors[i].factor == 0) { + value /= scale_factors[i].factor; + symbol = scale_factors[i].symbol; + break; + } + strbuf b = strbuf_local(str, len); + strbuf_sprintf(b, "%llu", (unsigned long long) value); + if (symbol) + strbuf_putc(b, symbol); + return strbuf_overrun(b) ? 0 : 1; +} + +int str_to_uint64_interval_ms(const char *str, int64_t *result, const char **afterp) +{ + const unsigned precision = 1000; + if (isspace(*str)) + return 0; + const char *end = str; + unsigned long long value = strtoull(str, (char**)&end, 10) * precision; + if (end == str) { + if (afterp) + *afterp = end; + return 0; + } + if (end[0] == '.' && isdigit(end[1])) { + ++end; + unsigned factor; + for (factor = precision / 10; isdigit(*end) && factor; factor /= 10) + value += (*end++ - '0') * factor; + } + if (afterp) + *afterp = end; + else if (*end) + return 0; + if (result) + *result = value; + return 1; +} + diff --git a/numeric_str.h b/numeric_str.h new file mode 100644 index 00000000..21d15d9c --- /dev/null +++ b/numeric_str.h @@ -0,0 +1,119 @@ +/* + Serval numerical string primitives + Copyright (C) 2012-2016 Serval Project Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __SERVAL_DNA__NUMERIC_STR_H__ +#define __SERVAL_DNA__NUMERIC_STR_H__ + +#include // for size_t +#include + +#ifndef __SERVAL_DNA__NUMERIC_STR_INLINE +# if __GNUC__ && !__GNUC_STDC_INLINE__ +# define __SERVAL_DNA__NUMERIC_STR_INLINE extern inline +# else +# define __SERVAL_DNA__NUMERIC_STR_INLINE inline +# endif +#endif + +/* Returns 1 if the given nul-terminated string parses successfully as an unsigned 64-bit integer. + * Returns 0 if not. This is simply a shortcut for str_to_uint32(str, 10, NULL, NULL), which is + * convenient for when a pointer to a predicate function is needed. + * + * @author Andrew Bettison + */ +int str_is_uint64_decimal(const char *str); + +/* Parse a NUL-terminated string as an integer in ASCII radix notation in the given 'base' (eg, + * base=10 means decimal). + * + * Returns 1 if a valid integer is parsed, storing the value in *result (unless result is NULL) and + * storing a pointer to the immediately succeeding character in *afterp. If afterp is NULL then + * returns 0 unless the immediately succeeding character is a NUL '\0'. If no integer is parsed or + * if the integer overflows (too many digits), then returns 0, leaving *result unchanged and setting + * setting *afterp to point to the character where parsing failed. + * + * @author Andrew Bettison + */ +int str_to_uint16(const char *str, unsigned base, uint16_t *result, const char **afterp); +int str_to_int32(const char *str, unsigned base, int32_t *result, const char **afterp); +int str_to_uint32(const char *str, unsigned base, uint32_t *result, const char **afterp); +int str_to_int64(const char *str, unsigned base, int64_t *result, const char **afterp); +int str_to_uint64(const char *str, unsigned base, uint64_t *result, const char **afterp); + +/* Parse a length-bound string as an integer in ASCII radix notation in the given 'base' (eg, + * base=10 means decimal). + * + * Returns 1 if a valid integer is parsed, storing the value in *result (unless result is NULL) and + * storing a pointer to the immediately succeeding character in *afterp. If afterp is NULL then + * returns 0 unless all 'strlen' characters of the string were consumed. If no integer is parsed or + * if the integer overflows (too many digits), then returns 0, leaving *result unchanged and setting + * setting *afterp to point to the character where parsing failed. + * + * @author Andrew Bettison + */ +int strn_to_uint16(const char *str, size_t strlen, unsigned base, uint16_t *result, const char **afterp); +int strn_to_uint32(const char *str, size_t strlen, unsigned base, uint32_t *result, const char **afterp); +int strn_to_uint64(const char *str, size_t strlen, unsigned base, uint64_t *result, const char **afterp); + +/* Parse a string as an integer in ASCII radix notation in the given 'base' (eg, base=10 means + * decimal) and scale the result by a factor given by an optional suffix "scaling" character in the + * set {kKmMgG}: 'k' = 1e3, 'K' = 1<<10, 'm' = 1e6, 'M' = 1<<20, 'g' = 1e9, 'G' = * 1<<30. + * + * Return 1 if a valid scaled integer was parsed, storing the value in *result (unless result is + * NULL) and storing a pointer to the immediately succeeding character in *afterp (unless afterp is + * NULL, in which case returns 1 only if the immediately succeeding character is a nul '\0'). + * Returns 0 otherwise, leaving *result and *afterp unchanged. + * + * NOTE: an argument base > 16 will cause any trailing 'g' or 'G' character to be parsed as part of + * the integer, not as a scale suffix. Ditto for base > 20 and 'k' 'K', and base > 22 and 'm' 'M'. + * + * @author Andrew Bettison + */ +int str_to_int32_scaled(const char *str, unsigned base, int32_t *result, const char **afterp); +int str_to_uint32_scaled(const char *str, unsigned base, uint32_t *result, const char **afterp); +int str_to_int64_scaled(const char *str, unsigned base, int64_t *result, const char **afterp); +int str_to_uint64_scaled(const char *str, unsigned base, uint64_t *result, const char **afterp); +uint64_t scale_factor(const char *str, const char **afterp); + +/* Format a string as a decimal integer in ASCII radix notation with a scale suffix character in the + * set {kKmMgG}: 'k' = 1e3, 'K' = 1<<10, 'm' = 1e6, 'M' = 1<<20, 'g' = 1e9, 'G' = * 1<<30 if the + * value is an exact multiple. + * + * Return 1 if the supplied string buffer was large enough to hold the formatted result plus a + * terminating nul character, 0 otherwise. + * + * @author Andrew Bettison + */ +int uint32_scaled_to_str(char *str, size_t len, uint32_t value); +int uint64_scaled_to_str(char *str, size_t len, uint64_t value); + +/* Parse a string as a time interval (seconds) in millisecond resolution. Return the number of + * milliseconds. Valid strings are all unsigned ASCII decimal numbers with up to three digits after + * the decimal point. + * + * Return 1 if a valid interval was parsed, storing the number of milliseconds in *result (unless + * result is NULL) and storing a pointer to the immediately succeeding character in *afterp (unless + * afterp is NULL, in which case returns 1 only if the immediately succeeding character is a nul + * '\0'). Returns 0 otherwise, leaving *result and *afterp unchanged. + * + * @author Andrew Bettison + */ +int str_to_uint64_interval_ms(const char *str, int64_t *result, const char **afterp); + +#endif // __SERVAL_DNA__NUMERIC_STR_H__ diff --git a/rhizome_bundle.c b/rhizome_bundle.c index 241fe00b..7d97cb9e 100644 --- a/rhizome_bundle.c +++ b/rhizome_bundle.c @@ -24,7 +24,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "serval.h" #include "conf.h" #include "rhizome.h" -#include "str.h" +#include "numeric_str.h" #include "mem.h" #include "keyring.h" #include "dataformats.h" diff --git a/sourcefiles.mk b/sourcefiles.mk index 5f5a3b46..3b8c82fb 100644 --- a/sourcefiles.mk +++ b/sourcefiles.mk @@ -23,6 +23,7 @@ SERVAL_CLIENT_SOURCES = \ strbuf.c \ strbuf_helpers.c \ str.c \ + numeric_str.c \ strlcpy.c \ uuid.c \ whence.c \ diff --git a/str.c b/str.c index 9cef2c6c..75dd21ef 100644 --- a/str.c +++ b/str.c @@ -1,6 +1,6 @@ /* Serval string primitives - Copyright (C) 2012 Serval Project Inc. + Copyright (C) 2012-2016 Serval Project Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -760,284 +760,6 @@ char *str_str(char *haystack, const char *needle, size_t haystack_len) return NULL; } -int str_to_uint16(const char *str, unsigned base, uint16_t *result, const char **afterp) -{ - return strn_to_uint16(str, 0, base, result, afterp); -} - -int strn_to_uint16(const char *str, size_t strlen, unsigned base, uint16_t *result, const char **afterp) -{ - assert(base > 0); - assert(base <= 16); - uint16_t value = 0; - uint16_t newvalue = 0; - const char *const end = str + strlen; - const char *s; - for (s = str; strlen ? s < end : *s; ++s) { - int digit = hexvalue(*s); - if (digit < 0 || (unsigned)digit >= base) - break; - newvalue = value * base + digit; - if (newvalue / base != value) // overflow - break; - value = newvalue; - } - if (afterp) - *afterp = s; - if (s == str || value != newvalue || (!afterp && (strlen ? s != end : *s))) - return 0; - if (result) - *result = value; - return 1; -} - -int str_to_int32(const char *str, unsigned base, int32_t *result, const char **afterp) -{ - if (isspace(*str)) - return 0; - const char *end = str; - errno = 0; - long value = strtol(str, (char**)&end, base); - if (afterp) - *afterp = end; - if (errno == ERANGE || end == str || value > INT32_MAX || value < INT32_MIN || isdigit(*end) || (!afterp && *end)) - return 0; - if (result) - *result = value; - return 1; -} - -int str_to_uint32(const char *str, unsigned base, uint32_t *result, const char **afterp) -{ - return strn_to_uint32(str, 0, base, result, afterp); -} - -int strn_to_uint32(const char *str, size_t strlen, unsigned base, uint32_t *result, const char **afterp) -{ - assert(base > 0); - assert(base <= 16); - uint32_t value = 0; - uint32_t newvalue = 0; - const char *const end = str + strlen; - const char *s; - for (s = str; strlen ? s < end : *s; ++s) { - int digit = hexvalue(*s); - if (digit < 0 || (unsigned)digit >= base) - break; - newvalue = value * base + digit; - if (newvalue < value) // overflow - break; - value = newvalue; - } - if (afterp) - *afterp = s; - if (s == str || value > UINT32_MAX || value != newvalue || (!afterp && (strlen ? s != end : *s))) - return 0; - if (result) - *result = value; - return 1; -} - -int str_is_uint64_decimal(const char *str) -{ - return str_to_uint64(str, 10, NULL, NULL); -} - -int str_to_int64(const char *str, unsigned base, int64_t *result, const char **afterp) -{ - if (isspace(*str)) - return 0; - const char *end = str; - errno = 0; - long long value = strtoll(str, (char**)&end, base); - if (afterp) - *afterp = end; - if (errno == ERANGE || end == str || isdigit(*end) || (!afterp && *end)) - return 0; - if (result) - *result = value; - return 1; -} - -int str_to_uint64(const char *str, unsigned base, uint64_t *result, const char **afterp) -{ - return strn_to_uint64(str, 0, base, result, afterp); -} - -int strn_to_uint64(const char *str, size_t strlen, unsigned base, uint64_t *result, const char **afterp) -{ - assert(base > 0); - assert(base <= 16); - uint64_t value = 0; - uint64_t newvalue = 0; - const char *const end = str + strlen; - const char *s; - for (s = str; strlen ? s < end : *s; ++s) { - int digit = hexvalue(*s); - if (digit < 0 || (unsigned)digit >= base) - break; - newvalue = value * base + digit; - if (newvalue < value) // overflow - break; - value = newvalue; - } - if (afterp) - *afterp = s; - if (s == str || value > UINT64_MAX || value != newvalue || (!afterp && (strlen ? s != end : *s))) - return 0; - if (result) - *result = value; - return 1; -} - -static struct scale_factor { - char symbol; - uint64_t factor; -} - scale_factors[] = { - { 'G', 1024LL * 1024LL * 1024LL }, - { 'g', 1000LL * 1000LL * 1000LL }, - { 'M', 1024LL * 1024LL }, - { 'm', 1000LL * 1000LL }, - { 'K', 1024LL }, - { 'k', 1000LL } - }; - -uint64_t scale_factor(const char *str, const char **afterp) -{ - uint64_t factor = 1; - int i; - for (i = 0; i != NELS(scale_factors); ++i) - if (scale_factors[i].symbol == str[0]) { - ++str; - factor = scale_factors[i].factor; - break; - } - if (afterp) - *afterp = str; - else if (*str) - factor = 0; - return factor; -} - -int str_to_int64_scaled(const char *str, unsigned base, int64_t *result, const char **afterp) -{ - int64_t value; - const char *end = str; - if (!str_to_int64(str, base, &value, &end)) { - if (afterp) - *afterp = end; - return 0; - } - value *= scale_factor(end, &end); - if (afterp) - *afterp = end; - else if (*end) - return 0; - if (result) - *result = value; - return 1; -} - -int str_to_uint32_scaled(const char *str, unsigned base, uint32_t *result, const char **afterp) -{ - uint32_t value; - const char *end = str; - if (!str_to_uint32(str, base, &value, &end)) { - if (afterp) - *afterp = end; - return 0; - } - value *= scale_factor(end, &end); - if (afterp) - *afterp = end; - else if (*end) - return 0; - if (result) - *result = value; - return 1; -} - -int uint32_scaled_to_str(char *str, size_t len, uint32_t value) -{ - char symbol = '\0'; - int i; - for (i = 0; i != NELS(scale_factors); ++i) - if (value % scale_factors[i].factor == 0) { - value /= scale_factors[i].factor; - symbol = scale_factors[i].symbol; - break; - } - strbuf b = strbuf_local(str, len); - strbuf_sprintf(b, "%lu", (unsigned long) value); - if (symbol) - strbuf_putc(b, symbol); - return strbuf_overrun(b) ? 0 : 1; -} - -int str_to_uint64_scaled(const char *str, unsigned base, uint64_t *result, const char **afterp) -{ - uint64_t value; - const char *end = str; - if (!str_to_uint64(str, base, &value, &end)) { - if (afterp) - *afterp = end; - return 0; - } - value *= scale_factor(end, &end); - if (afterp) - *afterp = end; - else if (*end) - return 0; - if (result) - *result = value; - return 1; -} - -int uint64_scaled_to_str(char *str, size_t len, uint64_t value) -{ - char symbol = '\0'; - int i; - for (i = 0; i != NELS(scale_factors); ++i) - if (value % scale_factors[i].factor == 0) { - value /= scale_factors[i].factor; - symbol = scale_factors[i].symbol; - break; - } - strbuf b = strbuf_local(str, len); - strbuf_sprintf(b, "%llu", (unsigned long long) value); - if (symbol) - strbuf_putc(b, symbol); - return strbuf_overrun(b) ? 0 : 1; -} - -int str_to_uint64_interval_ms(const char *str, int64_t *result, const char **afterp) -{ - const unsigned precision = 1000; - if (isspace(*str)) - return 0; - const char *end = str; - unsigned long long value = strtoull(str, (char**)&end, 10) * precision; - if (end == str) { - if (afterp) - *afterp = end; - return 0; - } - if (end[0] == '.' && isdigit(end[1])) { - ++end; - unsigned factor; - for (factor = precision / 10; isdigit(*end) && factor; factor /= 10) - value += (*end++ - '0') * factor; - } - if (afterp) - *afterp = end; - else if (*end) - return 0; - if (result) - *result = value; - return 1; -} - /* Compute the length of the string produced by sprintf(fmt, ...). @author Andrew Bettison */ diff --git a/str.h b/str.h index 4c984bc1..11cb2bdc 100644 --- a/str.h +++ b/str.h @@ -1,6 +1,6 @@ /* Serval string primitives - Copyright (C) 2012 Serval Project Inc. + Copyright (C) 2012-2016 Serval Project Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -412,93 +412,6 @@ int strn_str_casecmp(const char *str1, size_t len1, const char *str2); */ char *str_str(char *haystack, const char *needle, size_t haystack_len); -/* -------------------- Numeric strings -------------------- */ - -/* Returns 1 if the given nul-terminated string parses successfully as an unsigned 64-bit integer. - * Returns 0 if not. This is simply a shortcut for str_to_uint32(str, 10, NULL, NULL), which is - * convenient for when a pointer to a predicate function is needed. - * - * @author Andrew Bettison - */ -int str_is_uint64_decimal(const char *str); - -/* Parse a NUL-terminated string as an integer in ASCII radix notation in the given 'base' (eg, - * base=10 means decimal). - * - * Returns 1 if a valid integer is parsed, storing the value in *result (unless result is NULL) and - * storing a pointer to the immediately succeeding character in *afterp. If afterp is NULL then - * returns 0 unless the immediately succeeding character is a NUL '\0'. If no integer is parsed or - * if the integer overflows (too many digits), then returns 0, leaving *result unchanged and setting - * setting *afterp to point to the character where parsing failed. - * - * @author Andrew Bettison - */ -int str_to_uint16(const char *str, unsigned base, uint16_t *result, const char **afterp); -int str_to_int32(const char *str, unsigned base, int32_t *result, const char **afterp); -int str_to_uint32(const char *str, unsigned base, uint32_t *result, const char **afterp); -int str_to_int64(const char *str, unsigned base, int64_t *result, const char **afterp); -int str_to_uint64(const char *str, unsigned base, uint64_t *result, const char **afterp); - -/* Parse a length-bound string as an integer in ASCII radix notation in the given 'base' (eg, - * base=10 means decimal). - * - * Returns 1 if a valid integer is parsed, storing the value in *result (unless result is NULL) and - * storing a pointer to the immediately succeeding character in *afterp. If afterp is NULL then - * returns 0 unless all 'strlen' characters of the string were consumed. If no integer is parsed or - * if the integer overflows (too many digits), then returns 0, leaving *result unchanged and setting - * setting *afterp to point to the character where parsing failed. - * - * @author Andrew Bettison - */ -int strn_to_uint16(const char *str, size_t strlen, unsigned base, uint16_t *result, const char **afterp); -int strn_to_uint32(const char *str, size_t strlen, unsigned base, uint32_t *result, const char **afterp); -int strn_to_uint64(const char *str, size_t strlen, unsigned base, uint64_t *result, const char **afterp); - -/* Parse a string as an integer in ASCII radix notation in the given 'base' (eg, base=10 means - * decimal) and scale the result by a factor given by an optional suffix "scaling" character in the - * set {kKmMgG}: 'k' = 1e3, 'K' = 1<<10, 'm' = 1e6, 'M' = 1<<20, 'g' = 1e9, 'G' = * 1<<30. - * - * Return 1 if a valid scaled integer was parsed, storing the value in *result (unless result is - * NULL) and storing a pointer to the immediately succeeding character in *afterp (unless afterp is - * NULL, in which case returns 1 only if the immediately succeeding character is a nul '\0'). - * Returns 0 otherwise, leaving *result and *afterp unchanged. - * - * NOTE: an argument base > 16 will cause any trailing 'g' or 'G' character to be parsed as part of - * the integer, not as a scale suffix. Ditto for base > 20 and 'k' 'K', and base > 22 and 'm' 'M'. - * - * @author Andrew Bettison - */ -int str_to_int32_scaled(const char *str, unsigned base, int32_t *result, const char **afterp); -int str_to_uint32_scaled(const char *str, unsigned base, uint32_t *result, const char **afterp); -int str_to_int64_scaled(const char *str, unsigned base, int64_t *result, const char **afterp); -int str_to_uint64_scaled(const char *str, unsigned base, uint64_t *result, const char **afterp); -uint64_t scale_factor(const char *str, const char **afterp); - -/* Format a string as a decimal integer in ASCII radix notation with a scale suffix character in the - * set {kKmMgG}: 'k' = 1e3, 'K' = 1<<10, 'm' = 1e6, 'M' = 1<<20, 'g' = 1e9, 'G' = * 1<<30 if the - * value is an exact multiple. - * - * Return 1 if the supplied string buffer was large enough to hold the formatted result plus a - * terminating nul character, 0 otherwise. - * - * @author Andrew Bettison - */ -int uint32_scaled_to_str(char *str, size_t len, uint32_t value); -int uint64_scaled_to_str(char *str, size_t len, uint64_t value); - -/* Parse a string as a time interval (seconds) in millisecond resolution. Return the number of - * milliseconds. Valid strings are all unsigned ASCII decimal numbers with up to three digits after - * the decimal point. - * - * Return 1 if a valid interval was parsed, storing the number of milliseconds in *result (unless - * result is NULL) and storing a pointer to the immediately succeeding character in *afterp (unless - * afterp is NULL, in which case returns 1 only if the immediately succeeding character is a nul - * '\0'). Returns 0 otherwise, leaving *result and *afterp unchanged. - * - * @author Andrew Bettison - */ -int str_to_uint64_interval_ms(const char *str, int64_t *result, const char **afterp); - /* -------------------- URI encoding and decoding -------------------- */ /* Encode up to 'srclen' bytes of byte data (or up to first nul if 'srclen' == -1) at 'src' into at diff --git a/tfw_createfile.c b/tfw_createfile.c index 266eb8af..2c3ee0f2 100644 --- a/tfw_createfile.c +++ b/tfw_createfile.c @@ -54,6 +54,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include #include #include "str.h" +#include "numeric_str.h" static const char *argv0 = "test_createfile";