This is an automated email from the ASF dual-hosted git repository.

twice pushed a commit to branch unstable
in repository https://gitbox.apache.org/repos/asf/incubator-kvrocks.git


The following commit(s) were added to refs/heads/unstable by this push:
     new e4ae784  Add `ParseInt` in `StatusOr` style (#787)
e4ae784 is described below

commit e4ae784a271b4d815eb3a015e27c6f6e296b3036
Author: Twice <[email protected]>
AuthorDate: Sat Aug 27 22:29:15 2022 +0800

    Add `ParseInt` in `StatusOr` style (#787)
    
    - add `TryParseInt<T>`, `ParseInt<T>` and unit tests
    - use `union` in `StatusOr` since it is more readable and no strict 
aliasing issue
---
 src/parse_util.h            | 141 ++++++++++++++++++++++++++++++++++++++++++++
 src/status.h                |  83 ++++++++++++--------------
 tests/cppunit/main.cc       |   3 +-
 tests/cppunit/parse_util.cc |  57 ++++++++++++++++++
 4 files changed, 239 insertions(+), 45 deletions(-)

diff --git a/src/parse_util.h b/src/parse_util.h
new file mode 100644
index 0000000..0718540
--- /dev/null
+++ b/src/parse_util.h
@@ -0,0 +1,141 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#pragma once
+
+#include <cstdlib>
+#include <limits>
+#include <string>
+#include <tuple>
+#include <status.h>
+
+namespace details {
+
+template <typename>
+struct ParseIntFunc;
+
+template <>
+struct ParseIntFunc<short> { // NOLINT
+  constexpr static const auto value = std::strtol;
+};
+
+template <>
+struct ParseIntFunc<int> {
+  constexpr static const auto value = std::strtol;
+};
+
+template <>
+struct ParseIntFunc<long> { // NOLINT
+  constexpr static const auto value = std::strtol;
+};
+
+template <>
+struct ParseIntFunc<long long> { // NOLINT
+  constexpr static const auto value = std::strtoll;
+};
+
+template <>
+struct ParseIntFunc<unsigned short> { // NOLINT
+  constexpr static const auto value = std::strtoul;
+};
+
+template <>
+struct ParseIntFunc<unsigned> {
+  constexpr static const auto value = std::strtoul;
+};
+
+template <>
+struct ParseIntFunc<unsigned long> { // NOLINT
+  constexpr static const auto value = std::strtoul;
+};
+
+template <>
+struct ParseIntFunc<unsigned long long> { // NOLINT
+  constexpr static const auto value = std::strtoull;
+};
+
+}  // namespace details
+
+template <typename T>
+using ParseResultAndPos = std::tuple<T, const char *>;
+
+// TryParseInt parses a string to a integer,
+// if non-integer characters is encountered, it stop parsing and
+// return the result integer and the current string position.
+// e.g. TryParseInt("100MB") -> {100, "MB"}
+// if no integer can be parsed or out of type range, an error will be returned
+// base can be in {0, 2, ..., 36}, refer to strto* in standard c for more 
details
+template <typename T = long long> // NOLINT
+StatusOr<ParseResultAndPos<T>> TryParseInt(const char *v, int base = 0) {
+  char *end;
+
+  errno = 0;
+  auto res = details::ParseIntFunc<T>::value(v, &end, base);
+
+  if (v == end) {
+    return {Status::NotOK, "not started as an integer"};
+  }
+
+  if (errno) {
+    return {Status::NotOK, std::strerror(errno)};
+  }
+
+  if (!std::is_same<T, decltype(res)>::value &&
+    (res < std::numeric_limits<T>::min() || res > 
std::numeric_limits<T>::max())) {
+    return {Status::NotOK, "out of range of integer type"};
+  }
+
+  return ParseResultAndPos<T>{res, end};
+}
+
+// ParseInt parses a string to a integer,
+// not like TryParseInt, the whole string need to be parsed as an integer,
+// e.g. ParseInt("100MB") -> error status
+template <typename T = long long> // NOLINT
+StatusOr<T> ParseInt(const std::string& v, int base = 0) {
+  const char *begin = v.c_str();
+  auto res = TryParseInt<T>(begin, base);
+
+  if (!res) return res;
+
+  if (std::get<1>(*res) != begin + v.size()) {
+    return {Status::NotOK, "encounter non-integer characters"};
+  }
+
+  return std::get<0>(*res);
+}
+
+template <typename T>
+using NumericRange = std::tuple<T, T>;
+
+// this overload accepts a range {min, max},
+// integer out of the range will trigger an error status
+template <typename T = long long> // NOLINT
+StatusOr<T> ParseInt(const std::string& v, NumericRange<T> range, int base = 
0) {
+  auto res = ParseInt<T>(v, base);
+
+  if (!res) return res;
+
+  if (*res < std::get<0>(range) || *res > std::get<1>(range)) {
+    return {Status::NotOK, "out of numeric range"};
+  }
+
+  return *res;
+}
diff --git a/src/status.h b/src/status.h
index 5ec3f5f..a742b4d 100644
--- a/src/status.h
+++ b/src/status.h
@@ -80,12 +80,12 @@ class Status {
   }
 
   std::string Msg() const& {
-    if (*this) return ok_msg();
+    if (*this) return ok_msg;
     return msg_;
   }
 
   std::string Msg() && {
-    if (*this) return ok_msg();
+    if (*this) return ok_msg;
     return std::move(msg_);
   }
 
@@ -95,11 +95,9 @@ class Status {
   Code code_;
   std::string msg_;
 
-  static constexpr const char* ok_msg() {
-    return "ok";
-  }
+  static constexpr const char* ok_msg = "ok";
 
-  template <typename T>
+  template <typename>
   friend struct StatusOr;
 };
 
@@ -109,10 +107,10 @@ using first_element = typename std::tuple_element<0, 
std::tuple<Ts...>>::type;
 template <typename T>
 using remove_cvref_t = typename std::remove_cv<typename 
std::remove_reference<T>::type>::type;
 
-template <typename T>
+template <typename>
 struct StatusOr;
 
-template <typename T>
+template <typename>
 struct IsStatusOr : std::integral_constant<bool, false> {};
 
 template <typename T>
@@ -134,12 +132,12 @@ struct StatusOr {
 
   explicit StatusOr(Status s) : code_(s.code_) {
     CHECK(!s);
-    new(value_or_error_) error_type(new std::string(std::move(s.msg_)));
+    new(&error_) error_type(new std::string(std::move(s.msg_)));
   }
 
   StatusOr(Code code, std::string msg = {}) : code_(code) { // NOLINT
     CHECK(code != Code::cOK);
-    new(value_or_error_) error_type(new std::string(std::move(msg)));
+    new(&error_) error_type(new std::string(std::move(msg)));
   }
 
   template <typename ...Ts,
@@ -151,26 +149,34 @@ struct StatusOr {
         !std::is_same<StatusOr, remove_cvref_t<first_element<Ts...>>>::value
       ), int>::type = 0> // NOLINT
   explicit StatusOr(Ts && ... args) : code_(Code::cOK) {
-    new(value_or_error_) value_type(std::forward<Ts>(args)...);
+    new(&value_) value_type(std::forward<Ts>(args)...);
   }
 
   StatusOr(T&& value) : code_(Code::cOK) { // NOLINT
-    new(value_or_error_) value_type(std::move(value));
+    new(&value_) value_type(std::move(value));
   }
 
   StatusOr(const T& value) : code_(Code::cOK) { // NOLINT
-    new(value_or_error_) value_type(value);
+    new(&value_) value_type(value);
   }
 
   StatusOr(const StatusOr&) = delete;
-  StatusOr(StatusOr&& other) : code_(other.code_) {
+
+  template <typename U, typename std::enable_if<std::is_convertible<U, 
T>::value, int>::type = 0>
+  StatusOr(StatusOr<U>&& other) : code_(other.code_) {
     if (code_ == Code::cOK) {
-      new(value_or_error_) value_type(std::move(other.getValue()));
+      new(&value_) value_type(std::move(other.value_));
     } else {
-      new(value_or_error_) error_type(std::move(other.getError()));
+      new(&error_) error_type(std::move(other.error_));
     }
   }
 
+  template <typename U, typename std::enable_if<!std::is_convertible<U, 
T>::value, int>::type = 0>
+  StatusOr(StatusOr<U>&& other) : code_(other.code_) {
+    CHECK(code_ != Code::cOK);
+    new(&error_) error_type(std::move(other.error_));
+  }
+
   Status& operator=(const Status&) = delete;
 
   template <Code code>
@@ -181,12 +187,12 @@ struct StatusOr {
 
   Status ToStatus() const& {
     if (*this) return Status::OK();
-    return Status(code_, *getError());
+    return Status(code_, *error_);
   }
 
   Status ToStatus() && {
     if (*this) return Status::OK();
-    return Status(code_, std::move(*getError()));
+    return Status(code_, std::move(*error_));
   }
 
   Code GetCode() const {
@@ -195,17 +201,17 @@ struct StatusOr {
 
   value_type& GetValue() & {
     CHECK(*this);
-    return getValue();
+    return value_;
   }
 
   value_type&& GetValue() && {
     CHECK(*this);
-    return std::move(getValue());
+    return std::move(value_);
   }
 
   const value_type& GetValue() const& {
     CHECK(*this);
-    return getValue();
+    return value_;
   }
 
   value_type& operator*() & {
@@ -229,41 +235,30 @@ struct StatusOr {
   }
 
   std::string Msg() const& {
-    if (*this) return Status::ok_msg();
-    return *getError();
+    if (*this) return Status::ok_msg;
+    return *error_;
   }
 
   std::string Msg() && {
-    if (*this) return Status::ok_msg();
-    return std::move(*getError());
+    if (*this) return Status::ok_msg;
+    return std::move(*error_);
   }
 
   ~StatusOr() {
     if (*this) {
-      getValue().~value_type();
+      value_.~value_type();
     } else {
-      getError().~error_type();
+      error_.~error_type();
     }
   }
 
  private:
   Status::Code code_;
-  alignas(value_type) alignas(error_type) unsigned char value_or_error_
-    [sizeof(value_type) < sizeof(error_type) ? sizeof(error_type) : 
sizeof(value_type)];
-
-  value_type& getValue() {
-    return *reinterpret_cast<value_type*>(value_or_error_);
-  }
-
-  const value_type& getValue() const {
-    return *reinterpret_cast<const value_type*>(value_or_error_);
-  }
-
-  error_type& getError() {
-    return *reinterpret_cast<error_type*>(value_or_error_);
-  }
+  union {
+    value_type value_;
+    error_type error_;
+  };
 
-  const error_type& getError() const {
-    return *reinterpret_cast<const error_type*>(value_or_error_);
-  }
+  template <typename>
+  friend struct StatusOr;
 };
diff --git a/tests/cppunit/main.cc b/tests/cppunit/main.cc
index 06cc4fc..8ef4873 100644
--- a/tests/cppunit/main.cc
+++ b/tests/cppunit/main.cc
@@ -27,7 +27,8 @@ Server *GetServer() {
 }
 
 int main(int argc, char **argv) {
-//  gflags::SetUsageMessage("kvrocks unittest");
   testing::InitGoogleTest(&argc, argv);
+  google::InitGoogleLogging(argv[0]);
+  google::InstallFailureSignalHandler();
   return RUN_ALL_TESTS();
 }
diff --git a/tests/cppunit/parse_util.cc b/tests/cppunit/parse_util.cc
new file mode 100644
index 0000000..6c00c8f
--- /dev/null
+++ b/tests/cppunit/parse_util.cc
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <gtest/gtest.h>
+
+#include <parse_util.h>
+
+TEST(ParseUtil, TryParseInt) {
+  long long v;
+  const char *str = "12345hellooo", *end;
+  
+  std::tie(v, end) = *TryParseInt(str);
+  ASSERT_EQ(v, 12345);
+  ASSERT_EQ(end - str, 5);
+
+  ASSERT_EQ(TryParseInt("hello").Msg(), "not started as an integer");
+  ASSERT_FALSE(TryParseInt("9999999999999999999999999999999999"));
+  ASSERT_FALSE(TryParseInt("1", 100));
+}
+
+TEST(ParseUtil, ParseInt) {
+  ASSERT_EQ(*ParseInt("-2333"), -2333);
+  ASSERT_EQ(*ParseInt("0x1a"), 26);
+  ASSERT_EQ(*ParseInt("011"), 9);
+  ASSERT_EQ(*ParseInt("111", 2), 7);
+  ASSERT_EQ(*ParseInt("11", 010), 9);
+  ASSERT_EQ(*ParseInt("11", 10), 11);
+  ASSERT_EQ(*ParseInt("11", 0x10), 17);
+
+  ASSERT_EQ(ParseInt("hello").Msg(), "not started as an integer");
+  ASSERT_EQ(ParseInt("123hello").Msg(), "encounter non-integer characters");
+  ASSERT_FALSE(ParseInt("9999999999999999999999999999999999"));
+  ASSERT_EQ(ParseInt<short>("99999").Msg(), "out of range of integer type");
+  ASSERT_EQ(*ParseInt<short>("30000"), 30000);
+  ASSERT_EQ(*ParseInt<int>("99999"), 99999);
+  ASSERT_EQ(ParseInt<int>("3000000000").Msg(), "out of range of integer type");
+
+  ASSERT_EQ(*ParseInt("123", {0, 123}), 123);
+  ASSERT_EQ(ParseInt("124", {0, 123}).Msg(), "out of numeric range");
+}

Reply via email to