]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
common/strtol: add string_view interface and use string_view internally
authorJan Fajerski <jfajerski@suse.com>
Tue, 30 Jan 2018 08:50:25 +0000 (09:50 +0100)
committerJan Fajerski <jfajerski@suse.com>
Tue, 3 Apr 2018 07:23:24 +0000 (09:23 +0200)
Signed-off-by: Jan Fajerski <jfajerski@suse.com>
src/common/strtol.cc
src/test/cli/monmaptool/feature-set-unset-list.t
src/test/strtol.cc

index 43a7de011faa51894d9b2024019992cbc324bef8..b2a90963dff13056f0497c1fad90fcbde3783a76 100644 (file)
 #include <limits>
 #include <cmath>
 #include <sstream>
+#include <string_view>
 
 using std::ostringstream;
 
-long long strict_strtoll(const char *str, int base, std::string *err)
+long long strict_strtoll(const std::string_view str, int base, std::string *err)
 {
+  ostringstream errStr;
+  if (auto invalid = str.find_first_not_of("0123456789-+");
+      invalid != std::string_view::npos ||
+      invalid == 0) {
+    errStr << "The option value '" << str << "' contains invalid digits";
+    *err =  errStr.str();
+    return 0;
+  }
   char *endptr;
-  std::string errStr;
   errno = 0; /* To distinguish success/failure after call (see man page) */
-  long long ret = strtoll(str, &endptr, base);
+  long long ret = strtoll(str.data(), &endptr, base);
 
-  if (endptr == str) {
-    errStr = "Expected option value to be integer, got '";
-    errStr.append(str);
-    errStr.append("'");
-    *err =  errStr;
+  if (endptr == str.data()) {
+    errStr << "Expected option value to be integer, got '" << str << "'";
+    *err =  errStr.str();
     return 0;
   }
   if ((errno == ERANGE && (ret == LLONG_MAX || ret == LLONG_MIN))
       || (errno != 0 && ret == 0)) {
-    errStr = "The option value '";
-    errStr.append(str);
-    errStr.append("'");
-    errStr.append(" seems to be invalid");
-    *err = errStr;
-    return 0;
-  }
-  if (*endptr != '\0') {
-    errStr = "The option value '";
-    errStr.append(str);
-    errStr.append("'");
-    errStr.append(" contains invalid digits");
-    *err =  errStr;
+    errStr << "The option value '" << str << "' seems to be invalid";
+    *err = errStr.str();
     return 0;
   }
   *err = "";
   return ret;
 }
 
-int strict_strtol(const char *str, int base, std::string *err)
+long long strict_strtoll(const char *str, int base, std::string *err)
 {
-  std::string errStr;
+  return strict_strtoll(std::string_view(str), base, err);
+}
+
+int strict_strtol(const std::string_view str, int base, std::string *err)
+{
+  ostringstream errStr;
   long long ret = strict_strtoll(str, base, err);
   if (!err->empty())
     return 0;
   if ((ret < INT_MIN) || (ret > INT_MAX)) {
-    errStr = "The option value '";
-    errStr.append(str);
-    errStr.append("'");
-    errStr.append(" seems to be invalid");
-    *err = errStr;
+    errStr << "The option value '" << str << "' seems to be invalid";
+    *err = errStr.str();
     return 0;
   }
   return static_cast<int>(ret);
 }
 
-double strict_strtod(const char *str, std::string *err)
+int strict_strtol(const char *str, int base, std::string *err)
+{
+  return strict_strtol(std::string_view(str), base, err);
+}
+
+double strict_strtod(const std::string_view str, std::string *err)
 {
   char *endptr;
+  ostringstream oss;
   errno = 0; /* To distinguish success/failure after call (see man page) */
-  double ret = strtod(str, &endptr);
+  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;
@@ -101,26 +101,29 @@ double strict_strtod(const char *str, std::string *err)
   return ret;
 }
 
-float strict_strtof(const char *str, std::string *err)
+double strict_strtod(const char *str, std::string *err)
+{
+  return strict_strtod(std::string_view(str), err);
+}
+
+float strict_strtof(const std::string_view str, std::string *err)
 {
   char *endptr;
+  ostringstream oss;
   errno = 0; /* To distinguish success/failure after call (see man page) */
-  float ret = strtof(str, &endptr);
+  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;
@@ -129,55 +132,74 @@ float strict_strtof(const char *str, std::string *err)
   return ret;
 }
 
+float strict_strtof(const char *str, std::string *err)
+{
+  return strict_strtof(std::string_view(str), err);
+}
+
 template<typename T>
-T strict_iec_cast(const char *str, std::string *err)
+T strict_iec_cast(const std::string_view str, std::string *err)
 {
-  std::string s(str);
-  if (s.empty()) {
+  if (str.empty()) {
     *err = "strict_iecstrtoll: value not specified";
     return 0;
   }
-  char &u = s.back();
-  // consume 'i' if present, fail if "Bi" is passed
-  if ( u == 'i') {
-    s.pop_back();
-    u = s.back();
-    if (u == 'B') {
-      *err = "strict_iecstrtoll: illegal prefix \"Bi\"";
+  // 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 is 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;
+    }
   }
-  int m = 0;
-  if (u == 'B')
-    m = 0;
-  else if (u == 'K')
-    m = 10;
-  else if (u == 'M')
-    m = 20;
-  else if (u == 'G')
-    m = 30;
-  else if (u == 'T')
-    m = 40;
-  else if (u == 'P')
-    m = 50;
-  else if (u == 'E')
-    m = 60;
-  else
-    m = -1;
-
-  if (m >= 0)
-    s.pop_back();
-  else
-    m = 0;
 
-  long long ll = strict_strtoll(s.c_str(), 10, err);
+  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");
+        "type");
     return 0;
   }
   using promoted_t = typename std::common_type<decltype(ll), T>::type;
@@ -194,44 +216,68 @@ T strict_iec_cast(const char *str, std::string *err)
   return (ll << m);
 }
 
+template int strict_iec_cast<int>(const std::string_view str, std::string *err);
+template long strict_iec_cast<long>(const std::string_view str, std::string *err);
+template long long strict_iec_cast<long long>(const std::string_view str, std::string *err);
+template uint64_t strict_iec_cast<uint64_t>(const std::string_view str, std::string *err);
+template uint32_t strict_iec_cast<uint32_t>(const std::string_view str, std::string *err);
+
+uint64_t strict_iecstrtoll(const std::string_view str, std::string *err)
+{
+  return strict_iec_cast<uint64_t>(str, err);
+}
+
+uint64_t strict_iecstrtoll(const char *str, std::string *err)
+{
+  return strict_iec_cast<uint64_t>(std::string_view(str), err);
+}
+
+template<typename T>
+T strict_iec_cast(const char *str, std::string *err)
+{
+  return strict_iec_cast<T>(std::string_view(str), err);
+}
+
 template int strict_iec_cast<int>(const char *str, std::string *err);
 template long strict_iec_cast<long>(const char *str, std::string *err);
 template long long strict_iec_cast<long long>(const char *str, std::string *err);
 template uint64_t strict_iec_cast<uint64_t>(const char *str, std::string *err);
 template uint32_t strict_iec_cast<uint32_t>(const char *str, std::string *err);
 
-uint64_t strict_iecstrtoll(const char *str, std::string *err)
-{
-  return strict_iec_cast<uint64_t>(str, err);
-}
-
 template<typename T>
-T strict_si_cast(const char *str, std::string *err)
+T strict_si_cast(const std::string_view str, std::string *err)
 {
-  std::string s(str);
-  if (s.empty()) {
+  if (str.empty()) {
     *err = "strict_sistrtoll: value not specified";
     return 0;
   }
-  char &u = s.back();
+  std::string_view n = str;
   int m = 0;
-  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;
+  // 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)
-    s.pop_back();
+    if (m >= 3)
+      n = str.substr(0, str.length() -1);
+  }
 
-  long long ll = strict_strtoll(s.c_str(), 10, err);
+  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;
@@ -250,13 +296,30 @@ T strict_si_cast(const char *str, std::string *err)
   return (ll * pow (10,  m));
 }
 
-template int strict_si_cast<int>(const char *str, std::string *err);
-template long strict_si_cast<long>(const char *str, std::string *err);
-template long long strict_si_cast<long long>(const char *str, std::string *err);
-template uint64_t strict_si_cast<uint64_t>(const char *str, std::string *err);
-template uint32_t strict_si_cast<uint32_t>(const char *str, std::string *err);
+template int strict_si_cast<int>(const std::string_view str, std::string *err);
+template long strict_si_cast<long>(const std::string_view str, std::string *err);
+template long long strict_si_cast<long long>(const std::string_view str, std::string *err);
+template uint64_t strict_si_cast<uint64_t>(const std::string_view str, std::string *err);
+template uint32_t strict_si_cast<uint32_t>(const std::string_view str, std::string *err);
+
+uint64_t strict_sistrtoll(const std::string_view str, std::string *err)
+{
+  return strict_si_cast<uint64_t>(str, err);
+}
 
 uint64_t strict_sistrtoll(const char *str, std::string *err)
 {
   return strict_si_cast<uint64_t>(str, err);
 }
+
+template<typename T>
+T strict_si_cast(const char *str, std::string *err)
+{
+  return strict_si_cast<T>(std::string_view(str), err);
+}
+
+template int strict_si_cast<int>(const char *str, std::string *err);
+template long strict_si_cast<long>(const char *str, std::string *err);
+template long long strict_si_cast<long long>(const char *str, std::string *err);
+template uint64_t strict_si_cast<uint64_t>(const char *str, std::string *err);
+template uint32_t strict_si_cast<uint32_t>(const char *str, std::string *err);
index 33e069be22748a9c60d8b54dd5750db38d939378..306a495cb6db52d75bd4e9cda56e5a16eef18e34 100644 (file)
@@ -28,7 +28,7 @@
   available:persistent:[kraken(1),luminous(2),mimic(4)]
 
   $ monmaptool --feature-set foo /tmp/test.monmap.1234
-  unknown features name 'foo' or unable to parse value: Expected option value to be integer, got 'foo'
+  unknown features name 'foo' or unable to parse value: The option value 'foo' contains invalid digits
    usage: [--print] [--create [--clobber][--fsid uuid]]
           [--generate] [--set-initial-members]
           [--add name 1.2.3.4:567] [--rm name]
index a7ef3e51d1db7d2715a75bc0f102c7ec6b97bab3..69d1a70871e0a3d458e8e7d8ab1b1de52ec6416a 100644 (file)
@@ -127,9 +127,11 @@ TEST(StrToL, Error1) {
   test_strict_strtoll_err("604462909807314587353088"); // overflow
   test_strict_strtoll_err("aw shucks"); // invalid
   test_strict_strtoll_err("343245 aw shucks"); // invalid chars at end
+  test_strict_strtoll_err("-"); // invalid
 
   test_strict_strtol_err("35 aw shucks"); // invalid chars at end
   test_strict_strtol_err("--0");
+  test_strict_strtol_err("-");
 
   test_strict_strtod_err("345345.0-");
   test_strict_strtod_err("34.0 garbo");