]>
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 | #include "strtol.h" | |
16 | ||
9f95a23c | 17 | #include <algorithm> |
7c673cae | 18 | #include <climits> |
7c673cae | 19 | #include <limits> |
1adf2230 | 20 | #include <cmath> |
7c673cae | 21 | #include <sstream> |
f67539c2 | 22 | #include <strings.h> |
11fdf7f2 | 23 | #include <string_view> |
7c673cae FG |
24 | |
25 | using std::ostringstream; | |
26 | ||
f67539c2 TL |
27 | bool strict_strtob(const char* str, std::string *err) |
28 | { | |
29 | if (strcasecmp(str, "false") == 0) { | |
30 | return false; | |
31 | } else if (strcasecmp(str, "true") == 0) { | |
32 | return true; | |
33 | } else { | |
34 | int b = strict_strtol(str, 10, err); | |
35 | return (bool)!!b; | |
36 | } | |
37 | } | |
38 | ||
11fdf7f2 | 39 | long long strict_strtoll(std::string_view str, int base, std::string *err) |
7c673cae FG |
40 | { |
41 | char *endptr; | |
7c673cae | 42 | errno = 0; /* To distinguish success/failure after call (see man page) */ |
1adf2230 AA |
43 | long long ret = strtoll(str.data(), &endptr, base); |
44 | if (endptr == str.data() || endptr != str.data() + str.size()) { | |
45 | *err = (std::string{"Expected option value to be integer, got '"} + | |
46 | std::string{str} + "'"); | |
7c673cae FG |
47 | return 0; |
48 | } | |
1adf2230 AA |
49 | if (errno) { |
50 | *err = (std::string{"The option value '"} + std::string{str} + | |
51 | "' seems to be invalid"); | |
7c673cae FG |
52 | return 0; |
53 | } | |
54 | *err = ""; | |
55 | return ret; | |
56 | } | |
57 | ||
11fdf7f2 | 58 | int strict_strtol(std::string_view str, int base, std::string *err) |
7c673cae | 59 | { |
7c673cae FG |
60 | long long ret = strict_strtoll(str, base, err); |
61 | if (!err->empty()) | |
62 | return 0; | |
11fdf7f2 | 63 | if ((ret < INT_MIN) || (ret > INT_MAX)) { |
1adf2230 AA |
64 | ostringstream errStr; |
65 | errStr << "The option value '" << str << "' seems to be invalid"; | |
66 | *err = errStr.str(); | |
7c673cae FG |
67 | return 0; |
68 | } | |
69 | return static_cast<int>(ret); | |
70 | } | |
71 | ||
1adf2230 AA |
72 | int strict_strtol(const char *str, int base, std::string *err) |
73 | { | |
11fdf7f2 | 74 | return strict_strtol(std::string_view(str), base, err); |
1adf2230 AA |
75 | } |
76 | ||
11fdf7f2 | 77 | double strict_strtod(std::string_view str, std::string *err) |
7c673cae FG |
78 | { |
79 | char *endptr; | |
80 | errno = 0; /* To distinguish success/failure after call (see man page) */ | |
1adf2230 | 81 | double ret = strtod(str.data(), &endptr); |
7c673cae FG |
82 | if (errno == ERANGE) { |
83 | ostringstream oss; | |
84 | oss << "strict_strtod: floating point overflow or underflow parsing '" | |
85 | << str << "'"; | |
86 | *err = oss.str(); | |
87 | return 0.0; | |
88 | } | |
89 | if (endptr == str) { | |
90 | ostringstream oss; | |
91 | oss << "strict_strtod: expected double, got: '" << str << "'"; | |
92 | *err = oss.str(); | |
93 | return 0; | |
94 | } | |
95 | if (*endptr != '\0') { | |
96 | ostringstream oss; | |
97 | oss << "strict_strtod: garbage at end of string. got: '" << str << "'"; | |
98 | *err = oss.str(); | |
99 | return 0; | |
100 | } | |
101 | *err = ""; | |
102 | return ret; | |
103 | } | |
104 | ||
11fdf7f2 | 105 | float strict_strtof(std::string_view str, std::string *err) |
7c673cae FG |
106 | { |
107 | char *endptr; | |
108 | errno = 0; /* To distinguish success/failure after call (see man page) */ | |
1adf2230 | 109 | float ret = strtof(str.data(), &endptr); |
7c673cae FG |
110 | if (errno == ERANGE) { |
111 | ostringstream oss; | |
112 | oss << "strict_strtof: floating point overflow or underflow parsing '" | |
113 | << str << "'"; | |
114 | *err = oss.str(); | |
115 | return 0.0; | |
116 | } | |
117 | if (endptr == str) { | |
118 | ostringstream oss; | |
119 | oss << "strict_strtof: expected float, got: '" << str << "'"; | |
120 | *err = oss.str(); | |
121 | return 0; | |
122 | } | |
123 | if (*endptr != '\0') { | |
124 | ostringstream oss; | |
125 | oss << "strict_strtof: garbage at end of string. got: '" << str << "'"; | |
126 | *err = oss.str(); | |
127 | return 0; | |
128 | } | |
129 | *err = ""; | |
130 | return ret; | |
131 | } | |
132 | ||
133 | template<typename T> | |
11fdf7f2 | 134 | T strict_iec_cast(std::string_view str, std::string *err) |
7c673cae | 135 | { |
1adf2230 AA |
136 | if (str.empty()) { |
137 | *err = "strict_iecstrtoll: value not specified"; | |
7c673cae FG |
138 | return 0; |
139 | } | |
1adf2230 | 140 | // get a view of the unit and of the value |
11fdf7f2 TL |
141 | std::string_view unit; |
142 | std::string_view n = str; | |
1adf2230 | 143 | size_t u = str.find_first_not_of("0123456789-+"); |
7c673cae | 144 | int m = 0; |
f67539c2 | 145 | // deal with unit prefix if there is one |
11fdf7f2 | 146 | if (u != std::string_view::npos) { |
1adf2230 AA |
147 | n = str.substr(0, u); |
148 | unit = str.substr(u, str.length() - u); | |
149 | // we accept both old si prefixes as well as the proper iec prefixes | |
150 | // i.e. K, M, ... and Ki, Mi, ... | |
151 | if (unit.back() == 'i') { | |
152 | if (unit.front() == 'B') { | |
153 | *err = "strict_iecstrtoll: illegal prefix \"Bi\""; | |
154 | return 0; | |
155 | } | |
156 | } | |
157 | if (unit.length() > 2) { | |
158 | *err = "strict_iecstrtoll: illegal prefix (length > 2)"; | |
159 | return 0; | |
160 | } | |
161 | switch(unit.front()) { | |
162 | case 'K': | |
163 | m = 10; | |
164 | break; | |
165 | case 'M': | |
166 | m = 20; | |
167 | break; | |
168 | case 'G': | |
169 | m = 30; | |
170 | break; | |
171 | case 'T': | |
172 | m = 40; | |
173 | break; | |
174 | case 'P': | |
175 | m = 50; | |
176 | break; | |
177 | case 'E': | |
178 | m = 60; | |
179 | break; | |
180 | case 'B': | |
181 | break; | |
182 | default: | |
183 | *err = "strict_iecstrtoll: unit prefix not recognized"; | |
184 | return 0; | |
185 | } | |
186 | } | |
187 | ||
188 | long long ll = strict_strtoll(n, 10, err); | |
7c673cae | 189 | if (ll < 0 && !std::numeric_limits<T>::is_signed) { |
1adf2230 | 190 | *err = "strict_iecstrtoll: value should not be negative"; |
7c673cae FG |
191 | return 0; |
192 | } | |
193 | if (static_cast<unsigned>(m) >= sizeof(T) * CHAR_BIT) { | |
1adf2230 AA |
194 | *err = ("strict_iecstrtoll: the IEC prefix is too large for the designated " |
195 | "type"); | |
7c673cae FG |
196 | return 0; |
197 | } | |
198 | using promoted_t = typename std::common_type<decltype(ll), T>::type; | |
199 | if (static_cast<promoted_t>(ll) < | |
200 | static_cast<promoted_t>(std::numeric_limits<T>::min()) >> m) { | |
1adf2230 | 201 | *err = "strict_iecstrtoll: value seems to be too small"; |
7c673cae FG |
202 | return 0; |
203 | } | |
204 | if (static_cast<promoted_t>(ll) > | |
205 | static_cast<promoted_t>(std::numeric_limits<T>::max()) >> m) { | |
1adf2230 | 206 | *err = "strict_iecstrtoll: value seems to be too large"; |
7c673cae FG |
207 | return 0; |
208 | } | |
209 | return (ll << m); | |
210 | } | |
211 | ||
11fdf7f2 TL |
212 | template int strict_iec_cast<int>(std::string_view str, std::string *err); |
213 | template long strict_iec_cast<long>(std::string_view str, std::string *err); | |
214 | template long long strict_iec_cast<long long>(std::string_view str, std::string *err); | |
215 | template uint64_t strict_iec_cast<uint64_t>(std::string_view str, std::string *err); | |
216 | template uint32_t strict_iec_cast<uint32_t>(std::string_view str, std::string *err); | |
1adf2230 | 217 | |
11fdf7f2 | 218 | uint64_t strict_iecstrtoll(std::string_view str, std::string *err) |
1adf2230 AA |
219 | { |
220 | return strict_iec_cast<uint64_t>(str, err); | |
221 | } | |
222 | ||
1adf2230 | 223 | template<typename T> |
11fdf7f2 | 224 | T strict_si_cast(std::string_view str, std::string *err) |
1adf2230 AA |
225 | { |
226 | if (str.empty()) { | |
227 | *err = "strict_sistrtoll: value not specified"; | |
228 | return 0; | |
229 | } | |
11fdf7f2 | 230 | std::string_view n = str; |
1adf2230 AA |
231 | int m = 0; |
232 | // deal with unit prefix is there is one | |
11fdf7f2 | 233 | if (str.find_first_not_of("0123456789+-") != std::string_view::npos) { |
1adf2230 AA |
234 | const char &u = str.back(); |
235 | if (u == 'K') | |
236 | m = 3; | |
237 | else if (u == 'M') | |
238 | m = 6; | |
239 | else if (u == 'G') | |
240 | m = 9; | |
241 | else if (u == 'T') | |
242 | m = 12; | |
243 | else if (u == 'P') | |
244 | m = 15; | |
245 | else if (u == 'E') | |
246 | m = 18; | |
247 | else if (u != 'B') { | |
248 | *err = "strict_si_cast: unit prefix not recognized"; | |
249 | return 0; | |
250 | } | |
251 | ||
252 | if (m >= 3) | |
253 | n = str.substr(0, str.length() -1); | |
254 | } | |
255 | ||
256 | long long ll = strict_strtoll(n, 10, err); | |
257 | if (ll < 0 && !std::numeric_limits<T>::is_signed) { | |
258 | *err = "strict_sistrtoll: value should not be negative"; | |
259 | return 0; | |
260 | } | |
261 | using promoted_t = typename std::common_type<decltype(ll), T>::type; | |
9f95a23c TL |
262 | auto v = static_cast<promoted_t>(ll); |
263 | auto coefficient = static_cast<promoted_t>(powl(10, m)); | |
264 | if (v != std::clamp(v, | |
265 | (static_cast<promoted_t>(std::numeric_limits<T>::min()) / | |
266 | coefficient), | |
267 | (static_cast<promoted_t>(std::numeric_limits<T>::max()) / | |
268 | coefficient))) { | |
269 | *err = "strict_sistrtoll: value out of range"; | |
1adf2230 AA |
270 | return 0; |
271 | } | |
9f95a23c | 272 | return v * coefficient; |
1adf2230 AA |
273 | } |
274 | ||
11fdf7f2 TL |
275 | template int strict_si_cast<int>(std::string_view str, std::string *err); |
276 | template long strict_si_cast<long>(std::string_view str, std::string *err); | |
277 | template long long strict_si_cast<long long>(std::string_view str, std::string *err); | |
278 | template uint64_t strict_si_cast<uint64_t>(std::string_view str, std::string *err); | |
279 | template uint32_t strict_si_cast<uint32_t>(std::string_view str, std::string *err); |