summaryrefslogtreecommitdiffstats
path: root/src/common/strtol.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/strtol.cc')
-rw-r--r--src/common/strtol.cc279
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);