]> git.proxmox.com Git - ceph.git/blobdiff - ceph/src/common/strtol.h
Import ceph 15.2.8
[ceph.git] / ceph / src / common / strtol.h
index a7c0cc220b6f9c6f02c5c1dde5d612d3ff59d9b4..42f6fac9b03e1bb43cd75db817dcf960ed4274ce 100644 (file)
 #ifndef CEPH_COMMON_STRTOL_H
 #define CEPH_COMMON_STRTOL_H
 
+#if __has_include(<charconv>)
+#include <charconv>
+#endif // __has_include(<charconv>)
+#include <cinttypes>
+#include <cstdlib>
+#include <optional>
 #include <string>
-extern "C" {
-#include <stdint.h>
+#include <string_view>
+#include <system_error>
+#include <type_traits>
+
+
+namespace ceph {
+#if __has_include(<charconv>)
+// Wrappers around std::from_chars.
+//
+// Why do we want this instead of strtol and friends? Because the
+// string doesn't have to be NUL-terminated! (Also, for a lot of
+// purposes, just putting a string_view in and getting an optional out
+// is friendly.)
+//
+// Returns the found number on success. Returns an empty optional on
+// failure OR on trailing characters.
+template<typename T>
+auto parse(std::string_view s, int base = 10)
+  -> std::enable_if_t<std::is_integral_v<T>, std::optional<T>>
+{
+  T t;
+  auto r = std::from_chars(s.data(), s.data() + s.size(), t, base);
+  if ((r.ec != std::errc{}) || (r.ptr != s.data() + s.size())) {
+    return std::nullopt;
+  }
+  return t;
+}
+
+// As above, but succeed on trailing characters and trim the supplied
+// string_view to remove the parsed number. Set the supplied
+// string_view to empty if it ends with the number.
+template<typename T>
+auto consume(std::string_view& s, int base = 10)
+  -> std::enable_if_t<std::is_integral_v<T>, std::optional<T>>
+{
+  T t;
+  auto r = std::from_chars(s.data(), s.data() + s.size(), t, base);
+  if (r.ec != std::errc{})
+    return std::nullopt;
+
+  if (r.ptr == s.data() + s.size()) {
+    s = std::string_view{};
+  } else {
+    s.remove_prefix(r.ptr - s.data());
+  }
+  return t;
+}
+// Sadly GCC is missing the floating point versions.
+#else // __has_include(<charconv>)
+template<typename T>
+auto parse(std::string_view sv, int base = 10)
+  -> std::enable_if_t<std::is_integral_v<T>, std::optional<T>>
+{
+  std::string s(sv);
+  char* end = nullptr;
+  std::conditional_t<std::is_signed_v<T>, std::intmax_t, std::uintmax_t> v;
+  errno = 0;
+
+  if (s.size() > 0 && std::isspace(s[0]))
+    return std::nullopt;
+
+  if constexpr (std::is_signed_v<T>) {
+    v = std::strtoimax(s.data(), &end, base);
+  } else {
+    if (s.size() > 0 && s[0] == '-')
+      return std::nullopt;
+    v = std::strtoumax(s.data(), &end, base);
+  }
+  if (errno != 0 ||
+      end != s.data() + s.size() ||
+      v > std::numeric_limits<T>::max() ||
+      v < std::numeric_limits<T>::min())
+    return std::nullopt;
+  return static_cast<T>(v);
+}
+
+template<typename T>
+auto consume(std::string_view& sv, int base = 10)
+  -> std::enable_if_t<std::is_integral_v<T>, std::optional<T>>
+{
+  std::string s(sv);
+  char* end = nullptr;
+  std::conditional_t<std::is_signed_v<T>, std::intmax_t, std::uintmax_t> v;
+  errno = 0;
+
+  if (s.size() > 0 && std::isspace(s[0]))
+    return std::nullopt;
+
+  if constexpr (std::is_signed_v<T>) {
+    v = std::strtoimax(s.data(), &end, base);
+  } else {
+    if (s.size() > 0 && s[0] == '-')
+      return std::nullopt;
+    v = std::strtoumax(s.data(), &end, base);
+  }
+  if (errno != 0 ||
+      end == s.data() ||
+      v > std::numeric_limits<T>::max() ||
+      v < std::numeric_limits<T>::min())
+    return std::nullopt;
+  if (end == s.data() + s.size()) {
+    sv = std::string_view{};
+  } else {
+    sv.remove_prefix(end - s.data());
+  }
+  return static_cast<T>(v);
 }
+#endif // __has_include(<charconv>)
+} // namespace ceph
 
 long long strict_strtoll(const char *str, int base, std::string *err);