1 /*
2  * Copyright (c) 2011-2014, Intel Corporation
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without modification,
6  * are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice, this
9  * list of conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright notice,
12  * this list of conditions and the following disclaimer in the documentation and/or
13  * other materials provided with the distribution.
14  *
15  * 3. Neither the name of the copyright holder nor the names of its contributors
16  * may be used to endorse or promote products derived from this software without
17  * specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
23  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #pragma once
32 
33 #include <limits>
34 #include <sstream>
35 #include <string>
36 #include <stdint.h>
37 #include <cmath>
38 
39 /* details namespace is here to hide implementation details to header end user. It
40  * is NOT intended to be used outside. */
41 namespace details
42 {
43 
44 /** Helper class to limit instantiation of templates */
45 template<typename T>
46 struct ConvertionAllowed;
47 
48 /* List of allowed types for conversion */
49 template<> struct ConvertionAllowed<bool> {};
50 template<> struct ConvertionAllowed<uint64_t> {};
51 template<> struct ConvertionAllowed<int64_t> {};
52 template<> struct ConvertionAllowed<uint32_t> {};
53 template<> struct ConvertionAllowed<int32_t> {};
54 template<> struct ConvertionAllowed<uint16_t> {};
55 template<> struct ConvertionAllowed<int16_t> {};
56 template<> struct ConvertionAllowed<float> {};
57 template<> struct ConvertionAllowed<double> {};
58 
59 template<typename T>
convertTo(const std::string & str,T & result)60 static inline bool convertTo(const std::string &str, T &result)
61 {
62     /* Check that conversion to that type is allowed.
63      * If this fails, this means that this template was not intended to be used
64      * with this type, thus that the result is undefined. */
65     ConvertionAllowed<T>();
66 
67     if (str.find_first_of(std::string("\r\n\t\v ")) != std::string::npos) {
68         return false;
69     }
70 
71     /* Check for a '-' in string. If type is unsigned and a - is found, the
72      * parsing fails. This is made necessary because "-1" is read as 65535 for
73      * uint16_t, for example */
74     if (str.find("-") != std::string::npos
75         && !std::numeric_limits<T>::is_signed) {
76         return false;
77     }
78 
79     std::stringstream ss(str);
80 
81     /* Sadly, the stream conversion does not handle hexadecimal format, thus
82      * check is done manually */
83     if (str.substr(0, 2) == "0x") {
84         if (std::numeric_limits<T>::is_integer) {
85             ss >> std::hex >> result;
86         }
87         else {
88             /* Conversion undefined for non integers */
89             return false;
90         }
91     } else {
92         ss >> result;
93     }
94 
95     return ss.eof() && !ss.fail() && !ss.bad();
96 }
97 } // namespace details
98 
99 /**
100  * Convert a string to a given type.
101  *
102  * This template function read the value of the type T in the given string.
103  * The function does not allow to have white spaces around the value to parse
104  * and tries to parse the whole string, which means that if some bytes were not
105  * read in the string, the function fails.
106  * Hexadecimal representation (ie numbers starting with 0x) is supported only
107  * for integral types conversions.
108  * Result may be modified, even in case of failure.
109  *
110  * @param[in]  str    the string to parse.
111  * @param[out] result reference to object where to store the result.
112  *
113  * @return true if conversion was successful, false otherwise.
114  */
115 template<typename T>
convertTo(const std::string & str,T & result)116 static inline bool convertTo(const std::string &str, T &result)
117 {
118     return details::convertTo<T>(str, result);
119 }
120 
121 /**
122  * Specialization for int16_t of convertTo template function.
123  *
124  * This function follows the same paradigm than it's generic version.
125  *
126  * The specific implementation is made necessary because the stlport version of
127  * string streams is bugged and does not fail when giving overflowed values.
128  * This specialisation can be safely removed when stlport behaviour is fixed.
129  *
130  * @param[in]  str    the string to parse.
131  * @param[out] result reference to object where to store the result.
132  *
133  * @return true if conversion was successful, false otherwise.
134  */
135 template<>
convertTo(const std::string & str,int16_t & result)136 inline bool convertTo<int16_t>(const std::string &str, int16_t &result)
137 {
138     int64_t res;
139 
140     if (!convertTo<int64_t>(str, res)) {
141         return false;
142     }
143 
144     if (res > std::numeric_limits<int16_t>::max() || res < std::numeric_limits<int16_t>::min()) {
145         return false;
146     }
147 
148     result = static_cast<int16_t>(res);
149     return true;
150 }
151 
152 /**
153  * Specialization for float of convertTo template function.
154  *
155  * This function follows the same paradigm than it's generic version and is
156  * based on it but makes furthers checks on the returned value.
157  *
158  * The specific implementation is made necessary because the stlport conversion
159  * from string to float behaves differently than GNU STL: overflow produce
160  * +/-Infinity rather than an error.
161  *
162  * @param[in]  str    the string to parse.
163  * @param[out] result reference to object where to store the result.
164  *
165  * @return true if conversion was successful, false otherwise.
166  */
167 template<>
convertTo(const std::string & str,float & result)168 inline bool convertTo<float>(const std::string &str, float &result)
169 {
170     if (!details::convertTo(str, result)) {
171         return false;
172     }
173 
174     if (std::abs(result) == std::numeric_limits<float>::infinity() ||
175         result == std::numeric_limits<float>::quiet_NaN()) {
176         return false;
177     }
178 
179     return true;
180 }
181 
182 /**
183  * Specialization for double of convertTo template function.
184  *
185  * This function follows the same paradigm than it's generic version and is
186  * based on it but makes furthers checks on the returned value.
187  *
188  * The specific implementation is made necessary because the stlport conversion
189  * from string to double behaves differently than GNU STL: overflow produce
190  * +/-Infinity rather than an error.
191  *
192  * @param[in]  str    the string to parse.
193  * @param[out] result reference to object where to store the result.
194  *
195  * @return true if conversion was successful, false otherwise.
196  */
197 template<>
convertTo(const std::string & str,double & result)198 inline bool convertTo<double>(const std::string &str, double &result)
199 {
200     if (!details::convertTo(str, result)) {
201         return false;
202     }
203 
204     if (std::abs(result) == std::numeric_limits<double>::infinity() ||
205         result == std::numeric_limits<double>::quiet_NaN()) {
206         return false;
207     }
208 
209     return true;
210 }
211 
212 /**
213  * Specialization for boolean of convertTo template function.
214  *
215  * This function follows the same paradigm than it's generic version.
216  * This function accepts to parse boolean as "0/1" or "false/true" or
217  * "FALSE/TRUE".
218  * The specific implementation is made necessary because the behaviour of
219  * string streams when parsing boolean values is not sufficient to fit our
220  * requirements. Indeed, parsing "true" will correctly parse the value, but the
221  * end of stream is not reached which makes the ss.eof() fails in the generic
222  * implementation.
223  *
224  * @param[in]  str    the string to parse.
225  * @param[out] result reference to object where to store the result.
226  *
227  * @return true if conversion was successful, false otherwise.
228  */
229 template<>
convertTo(const std::string & str,bool & result)230 inline bool convertTo<bool>(const std::string &str, bool &result)
231 {
232     if (str == "0" || str == "FALSE" || str == "false") {
233         result = false;
234         return true;
235     }
236 
237     if (str == "1" || str == "TRUE" || str == "true") {
238         result = true;
239         return true;
240     }
241 
242     return false;
243 }
244