diff options
Diffstat (limited to 'src/common/strtol.cc')
-rw-r--r-- | src/common/strtol.cc | 279 |
1 files changed, 279 insertions, 0 deletions
diff --git a/src/common/strtol.cc b/src/common/strtol.cc new file mode 100644 index 000000000..c9e982b63 --- /dev/null +++ b/src/common/strtol.cc @@ -0,0 +1,279 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +/* + * Ceph - scalable distributed file system + * + * Copyright (C) 2011 New Dream Network + * + * This is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software + * Foundation. See file COPYING. + * + */ + +#include "strtol.h" + +#include <algorithm> +#include <climits> +#include <limits> +#include <cmath> +#include <sstream> +#include <strings.h> +#include <string_view> + +using std::ostringstream; + +bool strict_strtob(const char* str, std::string *err) +{ + if (strcasecmp(str, "false") == 0) { + return false; + } else if (strcasecmp(str, "true") == 0) { + return true; + } else { + int b = strict_strtol(str, 10, err); + return (bool)!!b; + } +} + +long long strict_strtoll(std::string_view str, int base, std::string *err) +{ + char *endptr; + errno = 0; /* To distinguish success/failure after call (see man page) */ + long long ret = strtoll(str.data(), &endptr, base); + if (endptr == str.data() || endptr != str.data() + str.size()) { + *err = (std::string{"Expected option value to be integer, got '"} + + std::string{str} + "'"); + return 0; + } + if (errno) { + *err = (std::string{"The option value '"} + std::string{str} + + "' seems to be invalid"); + return 0; + } + *err = ""; + return ret; +} + +int strict_strtol(std::string_view str, int base, std::string *err) +{ + long long ret = strict_strtoll(str, base, err); + if (!err->empty()) + return 0; + if ((ret < INT_MIN) || (ret > INT_MAX)) { + ostringstream errStr; + errStr << "The option value '" << str << "' seems to be invalid"; + *err = errStr.str(); + return 0; + } + return static_cast<int>(ret); +} + +int strict_strtol(const char *str, int base, std::string *err) +{ + return strict_strtol(std::string_view(str), base, err); +} + +double strict_strtod(std::string_view str, std::string *err) +{ + char *endptr; + errno = 0; /* To distinguish success/failure after call (see man page) */ + double ret = strtod(str.data(), &endptr); + if (errno == ERANGE) { + ostringstream oss; + oss << "strict_strtod: floating point overflow or underflow parsing '" + << str << "'"; + *err = oss.str(); + return 0.0; + } + if (endptr == str) { + ostringstream oss; + oss << "strict_strtod: expected double, got: '" << str << "'"; + *err = oss.str(); + return 0; + } + if (*endptr != '\0') { + ostringstream oss; + oss << "strict_strtod: garbage at end of string. got: '" << str << "'"; + *err = oss.str(); + return 0; + } + *err = ""; + return ret; +} + +float strict_strtof(std::string_view str, std::string *err) +{ + char *endptr; + errno = 0; /* To distinguish success/failure after call (see man page) */ + float ret = strtof(str.data(), &endptr); + if (errno == ERANGE) { + ostringstream oss; + oss << "strict_strtof: floating point overflow or underflow parsing '" + << str << "'"; + *err = oss.str(); + return 0.0; + } + if (endptr == str) { + ostringstream oss; + oss << "strict_strtof: expected float, got: '" << str << "'"; + *err = oss.str(); + return 0; + } + if (*endptr != '\0') { + ostringstream oss; + oss << "strict_strtof: garbage at end of string. got: '" << str << "'"; + *err = oss.str(); + return 0; + } + *err = ""; + return ret; +} + +template<typename T> +T strict_iec_cast(std::string_view str, std::string *err) +{ + if (str.empty()) { + *err = "strict_iecstrtoll: value not specified"; + return 0; + } + // get a view of the unit and of the value + std::string_view unit; + std::string_view n = str; + size_t u = str.find_first_not_of("0123456789-+"); + int m = 0; + // deal with unit prefix if there is one + if (u != std::string_view::npos) { + n = str.substr(0, u); + unit = str.substr(u, str.length() - u); + // we accept both old si prefixes as well as the proper iec prefixes + // i.e. K, M, ... and Ki, Mi, ... + if (unit.back() == 'i') { + if (unit.front() == 'B') { + *err = "strict_iecstrtoll: illegal prefix \"Bi\""; + return 0; + } + } + if (unit.length() > 2) { + *err = "strict_iecstrtoll: illegal prefix (length > 2)"; + return 0; + } + switch(unit.front()) { + case 'K': + m = 10; + break; + case 'M': + m = 20; + break; + case 'G': + m = 30; + break; + case 'T': + m = 40; + break; + case 'P': + m = 50; + break; + case 'E': + m = 60; + break; + case 'B': + break; + default: + *err = "strict_iecstrtoll: unit prefix not recognized"; + return 0; + } + } + + long long ll = strict_strtoll(n, 10, err); + if (ll < 0 && !std::numeric_limits<T>::is_signed) { + *err = "strict_iecstrtoll: value should not be negative"; + return 0; + } + if (static_cast<unsigned>(m) >= sizeof(T) * CHAR_BIT) { + *err = ("strict_iecstrtoll: the IEC prefix is too large for the designated " + "type"); + return 0; + } + using promoted_t = typename std::common_type<decltype(ll), T>::type; + if (static_cast<promoted_t>(ll) < + static_cast<promoted_t>(std::numeric_limits<T>::min()) >> m) { + *err = "strict_iecstrtoll: value seems to be too small"; + return 0; + } + if (static_cast<promoted_t>(ll) > + static_cast<promoted_t>(std::numeric_limits<T>::max()) >> m) { + *err = "strict_iecstrtoll: value seems to be too large"; + return 0; + } + return (ll << m); +} + +template int strict_iec_cast<int>(std::string_view str, std::string *err); +template long strict_iec_cast<long>(std::string_view str, std::string *err); +template long long strict_iec_cast<long long>(std::string_view str, std::string *err); +template uint64_t strict_iec_cast<uint64_t>(std::string_view str, std::string *err); +template uint32_t strict_iec_cast<uint32_t>(std::string_view str, std::string *err); + +uint64_t strict_iecstrtoll(std::string_view str, std::string *err) +{ + return strict_iec_cast<uint64_t>(str, err); +} + +template<typename T> +T strict_si_cast(std::string_view str, std::string *err) +{ + if (str.empty()) { + *err = "strict_sistrtoll: value not specified"; + return 0; + } + std::string_view n = str; + int m = 0; + // deal with unit prefix is there is one + if (str.find_first_not_of("0123456789+-") != std::string_view::npos) { + const char &u = str.back(); + if (u == 'K') + m = 3; + else if (u == 'M') + m = 6; + else if (u == 'G') + m = 9; + else if (u == 'T') + m = 12; + else if (u == 'P') + m = 15; + else if (u == 'E') + m = 18; + else if (u != 'B') { + *err = "strict_si_cast: unit prefix not recognized"; + return 0; + } + + if (m >= 3) + n = str.substr(0, str.length() -1); + } + + long long ll = strict_strtoll(n, 10, err); + if (ll < 0 && !std::numeric_limits<T>::is_signed) { + *err = "strict_sistrtoll: value should not be negative"; + return 0; + } + using promoted_t = typename std::common_type<decltype(ll), T>::type; + auto v = static_cast<promoted_t>(ll); + auto coefficient = static_cast<promoted_t>(powl(10, m)); + if (v != std::clamp(v, + (static_cast<promoted_t>(std::numeric_limits<T>::min()) / + coefficient), + (static_cast<promoted_t>(std::numeric_limits<T>::max()) / + coefficient))) { + *err = "strict_sistrtoll: value out of range"; + return 0; + } + return v * coefficient; +} + +template int strict_si_cast<int>(std::string_view str, std::string *err); +template long strict_si_cast<long>(std::string_view str, std::string *err); +template long long strict_si_cast<long long>(std::string_view str, std::string *err); +template uint64_t strict_si_cast<uint64_t>(std::string_view str, std::string *err); +template uint32_t strict_si_cast<uint32_t>(std::string_view str, std::string *err); |