]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | /* | |
4 | * Ceph - scalable distributed file system | |
5 | * | |
6 | * Copyright (C) 2011 New Dream Network | |
7 | * | |
8 | * This is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU Lesser General Public | |
10 | * License version 2.1, as published by the Free Software | |
11 | * Foundation. See file COPYING. | |
12 | * | |
13 | */ | |
14 | ||
15 | #ifndef CEPH_COMMON_STRTOL_H | |
16 | #define CEPH_COMMON_STRTOL_H | |
17 | ||
f91f0fd5 TL |
18 | #if __has_include(<charconv>) |
19 | #include <charconv> | |
20 | #endif // __has_include(<charconv>) | |
21 | #include <cinttypes> | |
22 | #include <cstdlib> | |
23 | #include <optional> | |
7c673cae | 24 | #include <string> |
f91f0fd5 TL |
25 | #include <string_view> |
26 | #include <system_error> | |
27 | #include <type_traits> | |
28 | ||
29 | ||
30 | namespace ceph { | |
31 | #if __has_include(<charconv>) | |
32 | // Wrappers around std::from_chars. | |
33 | // | |
34 | // Why do we want this instead of strtol and friends? Because the | |
35 | // string doesn't have to be NUL-terminated! (Also, for a lot of | |
36 | // purposes, just putting a string_view in and getting an optional out | |
37 | // is friendly.) | |
38 | // | |
39 | // Returns the found number on success. Returns an empty optional on | |
40 | // failure OR on trailing characters. | |
41 | template<typename T> | |
42 | auto parse(std::string_view s, int base = 10) | |
43 | -> std::enable_if_t<std::is_integral_v<T>, std::optional<T>> | |
44 | { | |
45 | T t; | |
46 | auto r = std::from_chars(s.data(), s.data() + s.size(), t, base); | |
47 | if ((r.ec != std::errc{}) || (r.ptr != s.data() + s.size())) { | |
48 | return std::nullopt; | |
49 | } | |
50 | return t; | |
51 | } | |
52 | ||
53 | // As above, but succeed on trailing characters and trim the supplied | |
54 | // string_view to remove the parsed number. Set the supplied | |
55 | // string_view to empty if it ends with the number. | |
56 | template<typename T> | |
57 | auto consume(std::string_view& s, int base = 10) | |
58 | -> std::enable_if_t<std::is_integral_v<T>, std::optional<T>> | |
59 | { | |
60 | T t; | |
61 | auto r = std::from_chars(s.data(), s.data() + s.size(), t, base); | |
62 | if (r.ec != std::errc{}) | |
63 | return std::nullopt; | |
64 | ||
65 | if (r.ptr == s.data() + s.size()) { | |
66 | s = std::string_view{}; | |
67 | } else { | |
68 | s.remove_prefix(r.ptr - s.data()); | |
69 | } | |
70 | return t; | |
71 | } | |
72 | // Sadly GCC is missing the floating point versions. | |
73 | #else // __has_include(<charconv>) | |
74 | template<typename T> | |
75 | auto parse(std::string_view sv, int base = 10) | |
76 | -> std::enable_if_t<std::is_integral_v<T>, std::optional<T>> | |
77 | { | |
78 | std::string s(sv); | |
79 | char* end = nullptr; | |
80 | std::conditional_t<std::is_signed_v<T>, std::intmax_t, std::uintmax_t> v; | |
81 | errno = 0; | |
82 | ||
83 | if (s.size() > 0 && std::isspace(s[0])) | |
84 | return std::nullopt; | |
85 | ||
86 | if constexpr (std::is_signed_v<T>) { | |
87 | v = std::strtoimax(s.data(), &end, base); | |
88 | } else { | |
89 | if (s.size() > 0 && s[0] == '-') | |
90 | return std::nullopt; | |
91 | v = std::strtoumax(s.data(), &end, base); | |
92 | } | |
93 | if (errno != 0 || | |
94 | end != s.data() + s.size() || | |
95 | v > std::numeric_limits<T>::max() || | |
96 | v < std::numeric_limits<T>::min()) | |
97 | return std::nullopt; | |
98 | return static_cast<T>(v); | |
99 | } | |
100 | ||
101 | template<typename T> | |
102 | auto consume(std::string_view& sv, int base = 10) | |
103 | -> std::enable_if_t<std::is_integral_v<T>, std::optional<T>> | |
104 | { | |
105 | std::string s(sv); | |
106 | char* end = nullptr; | |
107 | std::conditional_t<std::is_signed_v<T>, std::intmax_t, std::uintmax_t> v; | |
108 | errno = 0; | |
109 | ||
110 | if (s.size() > 0 && std::isspace(s[0])) | |
111 | return std::nullopt; | |
112 | ||
113 | if constexpr (std::is_signed_v<T>) { | |
114 | v = std::strtoimax(s.data(), &end, base); | |
115 | } else { | |
116 | if (s.size() > 0 && s[0] == '-') | |
117 | return std::nullopt; | |
118 | v = std::strtoumax(s.data(), &end, base); | |
119 | } | |
120 | if (errno != 0 || | |
121 | end == s.data() || | |
122 | v > std::numeric_limits<T>::max() || | |
123 | v < std::numeric_limits<T>::min()) | |
124 | return std::nullopt; | |
125 | if (end == s.data() + s.size()) { | |
126 | sv = std::string_view{}; | |
127 | } else { | |
128 | sv.remove_prefix(end - s.data()); | |
129 | } | |
130 | return static_cast<T>(v); | |
7c673cae | 131 | } |
f91f0fd5 TL |
132 | #endif // __has_include(<charconv>) |
133 | } // namespace ceph | |
7c673cae | 134 | |
f67539c2 TL |
135 | bool strict_strtob(const char* str, std::string *err); |
136 | ||
137 | long long strict_strtoll(std::string_view str, int base, std::string *err); | |
7c673cae FG |
138 | long long strict_strtoll(const char *str, int base, std::string *err); |
139 | ||
f67539c2 | 140 | int strict_strtol(std::string_view str, int base, std::string *err); |
7c673cae FG |
141 | int strict_strtol(const char *str, int base, std::string *err); |
142 | ||
143 | double strict_strtod(const char *str, std::string *err); | |
144 | ||
145 | float strict_strtof(const char *str, std::string *err); | |
146 | ||
1adf2230 AA |
147 | uint64_t strict_iecstrtoll(const char *str, std::string *err); |
148 | ||
149 | template<typename T> | |
150 | T strict_iec_cast(const char *str, std::string *err); | |
151 | ||
7c673cae FG |
152 | uint64_t strict_sistrtoll(const char *str, std::string *err); |
153 | ||
154 | template<typename T> | |
155 | T strict_si_cast(const char *str, std::string *err); | |
156 | ||
157 | /* On enter buf points to the end of the buffer, e.g. where the least | |
158 | * significant digit of the input number will be printed. Returns pointer to | |
159 | * where the most significant digit were printed, including zero padding. | |
160 | * Does NOT add zero at the end of buffer, this is responsibility of the caller. | |
161 | */ | |
162 | template<typename T, const unsigned base = 10, const unsigned width = 1> | |
163 | static inline | |
164 | char* ritoa(T u, char *buf) | |
165 | { | |
f67539c2 | 166 | static_assert(std::is_unsigned_v<T>, "signed types are not supported"); |
7c673cae FG |
167 | static_assert(base <= 16, "extend character map below to support higher bases"); |
168 | unsigned digits = 0; | |
169 | while (u) { | |
170 | *--buf = "0123456789abcdef"[u % base]; | |
171 | u /= base; | |
172 | digits++; | |
173 | } | |
174 | while (digits++ < width) | |
175 | *--buf = '0'; | |
176 | return buf; | |
177 | } | |
178 | ||
179 | #endif |