]> git.proxmox.com Git - ceph.git/blob - ceph/src/common/strtol.cc
bump version to 18.2.4-pve3
[ceph.git] / ceph / src / common / strtol.cc
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
17 #include <algorithm>
18 #include <climits>
19 #include <limits>
20 #include <cmath>
21 #include <sstream>
22 #include <strings.h>
23 #include <string_view>
24
25 using std::ostringstream;
26
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
39 long long strict_strtoll(std::string_view str, int base, std::string *err)
40 {
41 char *endptr;
42 errno = 0; /* To distinguish success/failure after call (see man page) */
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} + "'");
47 return 0;
48 }
49 if (errno) {
50 *err = (std::string{"The option value '"} + std::string{str} +
51 "' seems to be invalid");
52 return 0;
53 }
54 *err = "";
55 return ret;
56 }
57
58 int strict_strtol(std::string_view str, int base, std::string *err)
59 {
60 long long ret = strict_strtoll(str, base, err);
61 if (!err->empty())
62 return 0;
63 if ((ret < INT_MIN) || (ret > INT_MAX)) {
64 ostringstream errStr;
65 errStr << "The option value '" << str << "' seems to be invalid";
66 *err = errStr.str();
67 return 0;
68 }
69 return static_cast<int>(ret);
70 }
71
72 int strict_strtol(const char *str, int base, std::string *err)
73 {
74 return strict_strtol(std::string_view(str), base, err);
75 }
76
77 double strict_strtod(std::string_view str, std::string *err)
78 {
79 char *endptr;
80 errno = 0; /* To distinguish success/failure after call (see man page) */
81 double ret = strtod(str.data(), &endptr);
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
105 float strict_strtof(std::string_view str, std::string *err)
106 {
107 char *endptr;
108 errno = 0; /* To distinguish success/failure after call (see man page) */
109 float ret = strtof(str.data(), &endptr);
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>
134 T strict_iec_cast(std::string_view str, std::string *err)
135 {
136 if (str.empty()) {
137 *err = "strict_iecstrtoll: value not specified";
138 return 0;
139 }
140 // get a view of the unit and of the value
141 std::string_view unit;
142 std::string_view n = str;
143 size_t u = str.find_first_not_of("0123456789-+");
144 int m = 0;
145 // deal with unit prefix if there is one
146 if (u != std::string_view::npos) {
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);
189 if (ll < 0 && !std::numeric_limits<T>::is_signed) {
190 *err = "strict_iecstrtoll: value should not be negative";
191 return 0;
192 }
193 if (static_cast<unsigned>(m) >= sizeof(T) * CHAR_BIT) {
194 *err = ("strict_iecstrtoll: the IEC prefix is too large for the designated "
195 "type");
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) {
201 *err = "strict_iecstrtoll: value seems to be too small";
202 return 0;
203 }
204 if (static_cast<promoted_t>(ll) >
205 static_cast<promoted_t>(std::numeric_limits<T>::max()) >> m) {
206 *err = "strict_iecstrtoll: value seems to be too large";
207 return 0;
208 }
209 return (ll << m);
210 }
211
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);
217
218 uint64_t strict_iecstrtoll(std::string_view str, std::string *err)
219 {
220 return strict_iec_cast<uint64_t>(str, err);
221 }
222
223 template<typename T>
224 T strict_si_cast(std::string_view str, std::string *err)
225 {
226 if (str.empty()) {
227 *err = "strict_sistrtoll: value not specified";
228 return 0;
229 }
230 std::string_view n = str;
231 int m = 0;
232 // deal with unit prefix is there is one
233 if (str.find_first_not_of("0123456789+-") != std::string_view::npos) {
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;
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";
270 return 0;
271 }
272 return v * coefficient;
273 }
274
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);