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