]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
common/strtol: add strict_iecstrtoll; strict_sistrtoll uses base 10
authorJan Fajerski <jfajerski@suse.com>
Tue, 20 Feb 2018 16:38:05 +0000 (17:38 +0100)
committerJan Fajerski <jfajerski@suse.com>
Tue, 3 Apr 2018 07:23:24 +0000 (09:23 +0200)
This adds parsing of iec units (IEC units Ki, Mi, ...) besides the parsing
of the usual unit multipliers (SI units K, M, ...). Both unit prefixes are
parsed with base 2. While SI units should be parsed with base 10, this
would likely confuse users.

Signed-off-by: Jan Fajerski <jfajerski@suse.com>
src/common/strtol.cc
src/common/strtol.h
src/kv/RocksDBStore.cc
src/mon/OSDMonitor.cc
src/rgw/rgw_admin.cc
src/test/objectstore_bench.cc
src/test/strtol.cc
src/tools/rados/rados.cc
src/tools/rbd/ArgumentTypes.cc
src/tools/rbd/action/Bench.cc

index 5721b63fb41d7b8c57b1c9c52c44ccc43b6f6334..43a7de011faa51894d9b2024019992cbc324bef8 100644 (file)
@@ -16,6 +16,7 @@
 
 #include <climits>
 #include <limits>
+#include <cmath>
 #include <sstream>
 
 using std::ostringstream;
@@ -129,14 +130,23 @@ float strict_strtof(const char *str, std::string *err)
 }
 
 template<typename T>
-T strict_si_cast(const char *str, std::string *err)
+T strict_iec_cast(const char *str, std::string *err)
 {
   std::string s(str);
   if (s.empty()) {
-    *err = "strict_sistrtoll: value not specified";
+    *err = "strict_iecstrtoll: value not specified";
     return 0;
   }
-  const char &u = s.back();
+  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\"";
+      return 0;
+    }
+  }
   int m = 0;
   if (u == 'B')
     m = 0;
@@ -162,28 +172,84 @@ T strict_si_cast(const char *str, std::string *err)
 
   long long ll = strict_strtoll(s.c_str(), 10, err);
   if (ll < 0 && !std::numeric_limits<T>::is_signed) {
-    *err = "strict_sistrtoll: value should not be negative";
+    *err = "strict_iecstrtoll: value should not be negative";
     return 0;
   }
   if (static_cast<unsigned>(m) >= sizeof(T) * CHAR_BIT) {
-    *err = ("strict_sistrtoll: the SI prefix is too large for the designated "
+    *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_sistrtoll: value seems to be too small";
+    *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_sistrtoll: value seems to be too large";
+    *err = "strict_iecstrtoll: value seems to be too large";
     return 0;
   }
   return (ll << m);
 }
 
+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)
+{
+  std::string s(str);
+  if (s.empty()) {
+    *err = "strict_sistrtoll: value not specified";
+    return 0;
+  }
+  char &u = s.back();
+  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;
+
+  if (m >= 3)
+    s.pop_back();
+
+  long long ll = strict_strtoll(s.c_str(), 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;
+  if (static_cast<promoted_t>(ll) <
+      static_cast<promoted_t>(std::numeric_limits<T>::min()) / pow (10, m)) {
+    *err = "strict_sistrtoll: value seems to be too small";
+    return 0;
+  }
+  if (static_cast<promoted_t>(ll) >
+      static_cast<promoted_t>(std::numeric_limits<T>::max()) / pow (10, m)) {
+    *err = "strict_sistrtoll: value seems to be too large";
+    return 0;
+  }
+  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);
index 810273ebd23258e33ee40f3908919d1f938237b0..a7c0cc220b6f9c6f02c5c1dde5d612d3ff59d9b4 100644 (file)
@@ -28,6 +28,11 @@ double strict_strtod(const char *str, std::string *err);
 
 float strict_strtof(const char *str, std::string *err);
 
+uint64_t strict_iecstrtoll(const char *str, std::string *err);
+
+template<typename T>
+T strict_iec_cast(const char *str, std::string *err);
+
 uint64_t strict_sistrtoll(const char *str, std::string *err);
 
 template<typename T>
index 26ee903ef997b2e21a2405061ee3e7178d05d827..779441ce97533aabff73ebbc0567d507ff213d6b 100644 (file)
@@ -212,14 +212,14 @@ int RocksDBStore::tryInterpret(const string &key, const string &val, rocksdb::Op
 {
   if (key == "compaction_threads") {
     std::string err;
-    int f = strict_sistrtoll(val.c_str(), &err);
+    int f = strict_iecstrtoll(val.c_str(), &err);
     if (!err.empty())
       return -EINVAL;
     //Low priority threadpool is used for compaction
     opt.env->SetBackgroundThreads(f, rocksdb::Env::Priority::LOW);
   } else if (key == "flusher_threads") {
     std::string err;
-    int f = strict_sistrtoll(val.c_str(), &err);
+    int f = strict_iecstrtoll(val.c_str(), &err);
     if (!err.empty())
       return -EINVAL;
     //High priority threadpool is used for flusher
index 0e14ad6443b748b98ee27c9657d26c4bb4fec23a..924db810cb5c0e734b2e2b08e61994f02b50fd07 100644 (file)
@@ -5307,7 +5307,7 @@ int OSDMonitor::normalize_profile(const string& profilename,
   auto it = profile.find("stripe_unit");
   if (it != profile.end()) {
     string err_str;
-    uint32_t stripe_unit = strict_si_cast<uint32_t>(it->second.c_str(), &err_str);
+    uint32_t stripe_unit = strict_iecstrtoll(it->second.c_str(), &err_str);
     if (!err_str.empty()) {
       *ss << "could not parse stripe_unit '" << it->second
          << "': " << err_str << std::endl;
@@ -5581,7 +5581,7 @@ int OSDMonitor::prepare_pool_stripe_width(const unsigned pool_type,
       auto it = profile.find("stripe_unit");
       if (it != profile.end()) {
        string err_str;
-       stripe_unit = strict_si_cast<uint32_t>(it->second.c_str(), &err_str);
+       stripe_unit = strict_iecstrtoll(it->second.c_str(), &err_str);
        assert(err_str.empty());
       }
       *stripe_width = data_chunks *
@@ -10950,11 +10950,18 @@ bool OSDMonitor::prepare_command_impl(MonOpRequestRef op,
     // val could contain unit designations, so we treat as a string
     string val;
     cmd_getval(cct, cmdmap, "val", val);
-    stringstream tss;
-    int64_t value = unit_to_bytesize(val, &tss);
-    if (value < 0) {
-      ss << "error parsing value '" << value << "': " << tss.str();
-      err = value;
+    string tss;
+    int64_t value;
+    if (field == "max_objects") {
+      value = strict_sistrtoll(val.c_str(), &tss);
+    } else if (field == "max_bytes") {
+      value = strict_iecstrtoll(val.c_str(), &tss);
+    } else {
+      assert(0 == "unrecognized option");
+    }
+    if (!tss.empty()) {
+      ss << "error parsing value '" << val << "': " << tss;
+      err = -EINVAL;
       goto reply;
     }
 
index 949fc376f17a4f85223cd7bb17fbecf4d3294d95..10a619d998dc055c877584086325f15df621b2a6 100644 (file)
@@ -2635,7 +2635,7 @@ int main(int argc, const char **argv)
         return EINVAL;
       }
     } else if (ceph_argparse_witharg(args, i, &val, "--max-size", (char*)NULL)) {
-      max_size = strict_si_cast<int64_t>(val.c_str(), &err);
+      max_size = strict_iecstrtoll(val.c_str(), &err);
       if (!err.empty()) {
         cerr << "ERROR: failed to parse max size: " << err << std::endl;
         return EINVAL;
index 406dce3a75e1559e3c4ac1da5e593793a6359c08..281fd522cc711fbb4394eeca33dd8d64418a181f 100644 (file)
@@ -47,7 +47,7 @@ struct byte_units {
 
 bool byte_units::parse(const std::string &val, std::string *err)
 {
-  v = strict_sistrtoll(val.c_str(), err);
+  v = strict_iecstrtoll(val.c_str(), err);
   return err->empty();
 }
 
index 3946736b915725f4dcb8ee4276e32cb1d7c9a0b4..a7ef3e51d1db7d2715a75bc0f102c7ec6b97bab3 100644 (file)
@@ -13,6 +13,7 @@
  */
 
 #include "common/strtol.h"
+#include <math.h>
 #include <string>
 #include <map>
 
@@ -137,6 +138,137 @@ TEST(StrToL, Error1) {
 }
 
 
+static void test_strict_iecstrtoll(const char *str)
+{
+  std::string err;
+  strict_iecstrtoll(str, &err);
+  ASSERT_EQ(err, "");
+}
+
+static void test_strict_iecstrtoll_units(const std::string& foo,
+                                      std::string u, const int m)
+{
+  std::string s(foo);
+  s.append(u);
+  const char *str = s.c_str();
+  std::string err;
+  uint64_t r = strict_iecstrtoll(str, &err);
+  ASSERT_EQ(err, "");
+
+  str = foo.c_str();
+  std::string err2;
+  long long tmp = strict_strtoll(str, 10, &err2);
+  ASSERT_EQ(err2, "");
+  tmp = (tmp << m);
+  ASSERT_EQ(tmp, (long long)r);
+}
+
+TEST(IECStrToLL, WithUnits) {
+  std::map<std::string,int> units;
+  units["B"] = 0;
+  units["K"] = 10;
+  units["M"] = 20;
+  units["G"] = 30;
+  units["T"] = 40;
+  units["P"] = 50;
+  units["E"] = 60;
+  units["Ki"] = 10;
+  units["Mi"] = 20;
+  units["Gi"] = 30;
+  units["Ti"] = 40;
+  units["Pi"] = 50;
+  units["Ei"] = 60;
+
+  for (std::map<std::string,int>::iterator p = units.begin();
+       p != units.end(); ++p) {
+    // the upper bound of uint64_t is 2^64 = 4E
+    test_strict_iecstrtoll_units("4", p->first, p->second);
+    test_strict_iecstrtoll_units("1", p->first, p->second);
+    test_strict_iecstrtoll_units("0", p->first, p->second);
+  }
+}
+
+TEST(IECStrToLL, WithoutUnits) {
+  test_strict_iecstrtoll("1024");
+  test_strict_iecstrtoll("1152921504606846976");
+  test_strict_iecstrtoll("0");
+}
+
+static void test_strict_iecstrtoll_err(const char *str)
+{
+  std::string err;
+  strict_iecstrtoll(str, &err);
+  ASSERT_NE(err, "");
+}
+
+TEST(IECStrToLL, Error) {
+  test_strict_iecstrtoll_err("1024F");
+  test_strict_iecstrtoll_err("QDDSA");
+  test_strict_iecstrtoll_err("1b");
+  test_strict_iecstrtoll_err("100k");
+  test_strict_iecstrtoll_err("1000m");
+  test_strict_iecstrtoll_err("1g");
+  test_strict_iecstrtoll_err("20t");
+  test_strict_iecstrtoll_err("100p");
+  test_strict_iecstrtoll_err("1000e");
+  test_strict_iecstrtoll_err("B");
+  test_strict_iecstrtoll_err("M");
+  test_strict_iecstrtoll_err("BM");
+  test_strict_iecstrtoll_err("B0wef");
+  test_strict_iecstrtoll_err("0m");
+  test_strict_iecstrtoll_err("-1"); // it returns uint64_t
+  test_strict_iecstrtoll_err("-1K");
+  test_strict_iecstrtoll_err("1Bi");
+  test_strict_iecstrtoll_err("Bi");
+  test_strict_iecstrtoll_err("bi");
+  test_strict_iecstrtoll_err("gi");
+  test_strict_iecstrtoll_err("100ki");
+  test_strict_iecstrtoll_err("1000mi");
+  test_strict_iecstrtoll_err("1gi");
+  test_strict_iecstrtoll_err("20ti");
+  test_strict_iecstrtoll_err("100pi");
+  test_strict_iecstrtoll_err("1000ei");
+  // the upper bound of uint64_t is 2^64 = 4E, so 1024E overflows
+  test_strict_iecstrtoll_err("1024E"); // overflows after adding the suffix
+}
+
+// since strict_iecstrtoll is an alias of strict_iec_cast<uint64_t>(), quite a few
+// of cases are covered by existing test cases of strict_iecstrtoll already.
+TEST(StrictIECCast, Error) {
+  {
+    std::string err;
+    // the SI prefix is way too large for `int`.
+    (void)strict_iec_cast<int>("2E", &err);
+    ASSERT_NE(err, "");
+  }
+  {
+    std::string err;
+    (void)strict_iec_cast<int>("-2E", &err);
+    ASSERT_NE(err, "");
+  }
+  {
+    std::string err;
+    (void)strict_iec_cast<int>("1T", &err);
+    ASSERT_NE(err, "");
+  }
+  {
+    std::string err;
+    (void)strict_iec_cast<int64_t>("2E", &err);
+    ASSERT_EQ(err, "");
+  }
+  {
+    std::string err;
+    (void)strict_iec_cast<int64_t>("-2E", &err);
+    ASSERT_EQ(err, "");
+  }
+  {
+    std::string err;
+    (void)strict_iec_cast<int64_t>("1T", &err);
+    ASSERT_EQ(err, "");
+  }
+}
+
+
 static void test_strict_sistrtoll(const char *str)
 {
   std::string err;
@@ -145,10 +277,10 @@ static void test_strict_sistrtoll(const char *str)
 }
 
 static void test_strict_sistrtoll_units(const std::string& foo,
-                                      char u, const int m)
+                                      std::string u, const long long m)
 {
   std::string s(foo);
-  s.push_back(u);
+  s.append(u);
   const char *str = s.c_str();
   std::string err;
   uint64_t r = strict_sistrtoll(str, &err);
@@ -158,21 +290,20 @@ static void test_strict_sistrtoll_units(const std::string& foo,
   std::string err2;
   long long tmp = strict_strtoll(str, 10, &err2);
   ASSERT_EQ(err2, "");
-  tmp = (tmp << m);
+  tmp = (tmp  m);
   ASSERT_EQ(tmp, (long long)r);
 }
 
 TEST(SIStrToLL, WithUnits) {
-  std::map<char,int> units;
-  units['B'] = 0;
-  units['K'] = 10;
-  units['M'] = 20;
-  units['G'] = 30;
-  units['T'] = 40;
-  units['P'] = 50;
-  units['E'] = 60;
-
-  for (std::map<char,int>::iterator p = units.begin();
+  std::map<std::string,long long> units;
+  units["K"] = pow(10, 3);
+  units["M"] = pow(10, 6);
+  units["G"] = pow(10, 9);
+  units["T"] = pow(10, 12);
+  units["P"] = pow(10, 15);
+  units["E"] = pow(10, 18);
+
+  for (std::map<std::string,long long>::iterator p = units.begin();
        p != units.end(); ++p) {
     // the upper bound of uint64_t is 2^64 = 4E
     test_strict_sistrtoll_units("4", p->first, p->second);
@@ -211,6 +342,17 @@ TEST(SIStrToLL, Error) {
   test_strict_sistrtoll_err("0m");
   test_strict_sistrtoll_err("-1"); // it returns uint64_t
   test_strict_sistrtoll_err("-1K");
+  test_strict_sistrtoll_err("1Bi");
+  test_strict_sistrtoll_err("Bi");
+  test_strict_sistrtoll_err("bi");
+  test_strict_sistrtoll_err("gi");
+  test_strict_sistrtoll_err("100ki");
+  test_strict_sistrtoll_err("1000mi");
+  test_strict_sistrtoll_err("1gi");
+  test_strict_sistrtoll_err("20ti");
+  test_strict_sistrtoll_err("100pi");
+  test_strict_sistrtoll_err("1000ei");
+  test_strict_sistrtoll_err("1B");
   // the upper bound of uint64_t is 2^64 = 4E, so 1024E overflows
   test_strict_sistrtoll_err("1024E"); // overflows after adding the suffix
 }
index 79ac8fe0fdc799d2e58630b47bb629a0aabe9248..a0200eb96172dbb01b0c8f619c36be8115257ec3 100644 (file)
@@ -248,7 +248,7 @@ unsigned default_op_size = 1 << 22;
 template <typename I, typename T>
 static int rados_sistrtoll(I &i, T *val) {
   std::string err;
-  *val = strict_sistrtoll(i->second.c_str(), &err);
+  *val = strict_iecstrtoll(i->second.c_str(), &err);
   if (err != "") {
     cerr << "Invalid value for " << i->first << ": " << err << std::endl;
     return -EINVAL;
index 61b84d199a9525edb15bfb57119c180ec0482777..c30a1e4bf286ea53a620856eff0aa8ece16f51fa 100644 (file)
@@ -421,7 +421,7 @@ void validate(boost::any& v, const std::vector<std::string>& values,
   const std::string &s = po::validators::get_single_string(values);
 
   std::string parse_error;
-  uint64_t size = strict_sistrtoll(s.c_str(), &parse_error);
+  uint64_t size = strict_iecstrtoll(s.c_str(), &parse_error);
   if (!parse_error.empty()) {
     throw po::validation_error(po::validation_error::invalid_option_value);
   }
@@ -455,7 +455,7 @@ void validate(boost::any& v, const std::vector<std::string>& values,
   const std::string &s = po::validators::get_single_string(values);
   
   std::string parse_error;
-  uint64_t objectsize = strict_sistrtoll(s.c_str(), &parse_error);
+  uint64_t objectsize = strict_iecstrtoll(s.c_str(), &parse_error);
   if (!parse_error.empty()) {
     throw po::validation_error(po::validation_error::invalid_option_value);
   }
@@ -528,7 +528,7 @@ void validate(boost::any& v, const std::vector<std::string>& values,
   const std::string &s = po::validators::get_single_string(values);
 
   std::string parse_error;
-  uint64_t size = strict_sistrtoll(s.c_str(), &parse_error);
+  uint64_t size = strict_iecstrtoll(s.c_str(), &parse_error);
   if (parse_error.empty() && (size >= (1 << 12))) {
     v = boost::any(size);
     return;
@@ -542,7 +542,7 @@ void validate(boost::any& v, const std::vector<std::string>& values,
   const std::string &s = po::validators::get_single_string(values);
 
   std::string parse_error;
-  uint64_t format = strict_sistrtoll(s.c_str(), &parse_error);
+  uint64_t format = strict_iecstrtoll(s.c_str(), &parse_error);
   if (!parse_error.empty() || (format != 1 && format != 2)) {
     throw po::validation_error(po::validation_error::invalid_option_value);
   }
index 2da32bb7e6c830c22b16c9164fbc989dc4c7ba82..0fe1950426824f5a5f1e80ad59853061cc087d14 100644 (file)
@@ -43,7 +43,7 @@ void validate(boost::any& v, const std::vector<std::string>& values,
   const std::string &s = po::validators::get_single_string(values);
 
   std::string parse_error;
-  uint64_t size = strict_sistrtoll(s.c_str(), &parse_error);
+  uint64_t size = strict_iecstrtoll(s.c_str(), &parse_error);
   if (!parse_error.empty()) {
     throw po::validation_error(po::validation_error::invalid_option_value);
   }