1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #pragma once
18 
19 #include <limits>
20 #include <sstream>
21 #include <string>
22 #include <vector>
23 #include <stdint.h>
24 #include <cmath>
25 
26 namespace android
27 {
28 
29 namespace utilities
30 {
31 
32 /**
33  * Convert a given source type to a given destination type.
34  *
35  * String conversion to T reads the value of the type T in the given string.
36  * The function does not allow to have white spaces around the value to parse
37  * and tries to parse the whole string, which means that if some bytes were not
38  * read in the string, the function fails.
39  * Hexadecimal representation (ie. numbers starting with 0x) is supported only
40  * for integral types conversions.
41  *
42  * Numeric conversion to string formats the source value to decimal space.
43  *
44  * Vector to vector conversion calls convertTo on each element.
45  *
46  * @tparam srcType source type, default value is string type
47  * @tparam dstType destination type
48  * @param[in] input The source to convert from.
49  * @param[out] result Converted value if success, undefined on failure.
50  *
51  * @return true if conversion was successful, false otherwise.
52  */
53 template <typename srcType, typename dstType>
54 static inline bool convertTo(const srcType &input, dstType &result);
55 
56 /* details namespace is here to hide implementation details to header end user. It
57  * is NOT intended to be used outside. */
58 namespace details
59 {
60 
61 /** Helper class to limit instantiation of templates */
62 template <typename T>
63 struct ConversionFromStringAllowed;
64 template <typename T>
65 struct ConversionToStringAllowed;
66 
67 /* List of allowed types for conversion */
68 template <>
69 struct ConversionFromStringAllowed<bool> {};
70 template <>
71 struct ConversionFromStringAllowed<uint64_t> {};
72 template <>
73 struct ConversionFromStringAllowed<int64_t> {};
74 template <>
75 struct ConversionFromStringAllowed<uint32_t> {};
76 template <>
77 struct ConversionFromStringAllowed<int32_t> {};
78 template <>
79 struct ConversionFromStringAllowed<uint16_t> {};
80 template <>
81 struct ConversionFromStringAllowed<int16_t> {};
82 template <>
83 struct ConversionFromStringAllowed<float> {};
84 template <>
85 struct ConversionFromStringAllowed<double> {};
86 
87 template <>
88 struct ConversionToStringAllowed<int64_t> {};
89 template <>
90 struct ConversionToStringAllowed<uint64_t> {};
91 template <>
92 struct ConversionToStringAllowed<uint32_t> {};
93 template <>
94 struct ConversionToStringAllowed<int32_t> {};
95 template <>
96 struct ConversionToStringAllowed<double> {};
97 template <>
98 struct ConversionToStringAllowed<float> {};
99 
100 /**
101  * Set the decimal precision to 10 digits.
102  * Note that this setting is aligned with Android Audio Parameter
103  * policy concerning float storage into string.
104  */
105 static const uint32_t gFloatPrecision = 10;
106 
107 template <typename T>
108 static inline bool fromString(const std::string &str, T &result)
109 {
110     /* Check that conversion to that type is allowed.
111      * If this fails, this means that this template was not intended to be used
112      * with this type, thus that the result is undefined. */
113     ConversionFromStringAllowed<T>();
114 
115     if (str.find_first_of(std::string("\r\n\t\v ")) != std::string::npos) {
116         return false;
117     }
118 
119     /* Check for a '-' in string. If type is unsigned and a - is found, the
120      * parsing fails. This is made necessary because "-1" is read as 65535 for
121      * uint16_t, for example */
122     if (str.find("-") != std::string::npos
123         && !std::numeric_limits<T>::is_signed) {
124         return false;
125     }
126 
127     std::stringstream ss(str);
128 
129     /* Sadly, the stream conversion does not handle hexadecimal format, thus
130      * check is done manually */
131     if (str.substr(0, 2) == "0x") {
132         if (std::numeric_limits<T>::is_integer) {
133             ss >> std::hex >> result;
134         } else {
135             /* Conversion undefined for non integers */
136             return false;
137         }
138     } else {
139         ss >> result;
140     }
141 
142     return ss.eof() && !ss.fail() && !ss.bad();
143 }
144 
145 template <typename T>
146 static inline bool toString(const T &value, std::string &str)
147 {
148     /* Check that conversion from that type is allowed.
149      * If this fails, this means that this template was not intended to be used
150      * with this type, thus that the result is undefined. */
151     ConversionToStringAllowed<T>();
152 
153     std::stringstream oss;
154     oss.precision(gFloatPrecision);
155     oss << value;
156     str = oss.str();
157     return !oss.fail() && !oss.bad();
158 }
159 
160 template <typename srcType, typename dstType>
161 class Converter;
162 
163 template <typename dstType>
164 class Converter<std::string, dstType>
165 {
166 public:
167     static inline bool run(const std::string &str, dstType &result)
168     {
169         return fromString<dstType>(str, result);
170     }
171 };
172 
173 template <typename srcType>
174 class Converter<srcType, std::string>
175 {
176 public:
177     static inline bool run(const srcType &str, std::string &result)
178     {
179         return toString<srcType>(str, result);
180     }
181 };
182 
183 /** Convert a vector by applying convertTo on each element.
184  *
185  * @tparam SrcElem Type of the src elements.
186  * @tparam DstElem Type of the destination elements.
187  */
188 template <typename SrcElem, typename DstElem>
189 class Converter<std::vector<SrcElem>, std::vector<DstElem> >
190 {
191 public:
192     typedef const std::vector<SrcElem> Src;
193     typedef std::vector<DstElem> Dst;
194 
195     static inline bool run(Src &src, Dst &dst)
196     {
197         typedef typename Src::const_iterator SrcIt;
198         dst.clear();
199         dst.reserve(src.size());
200         for (SrcIt it = src.begin(); it != src.end(); ++it) {
201             DstElem dstElem;
202             if (not convertTo(*it, dstElem)) {
203                 return false;
204             }
205             dst.push_back(dstElem);
206         }
207         return true;
208     }
209 };
210 
211 } // namespace details
212 
213 template <typename srcType, typename dstType>
214 static inline bool convertTo(const srcType &input, dstType &result)
215 {
216     return details::Converter<srcType, dstType>::run(input, result);
217 }
218 
219 /**
220  * Specialization for int16_t of convertTo template function.
221  *
222  * This function follows the same paradigm than it's generic version.
223  *
224  * The specific implementation is made necessary because the stlport version of
225  * string streams is bugged and does not fail when giving overflowed values.
226  * This specialisation can be safely removed when stlport behaviour is fixed.
227  *
228  * @param[in]  str    the string to parse.
229  * @param[out] result reference to object where to store the result.
230  *
231  * @return true if conversion was successful, false otherwise.
232  */
233 template <>
234 inline bool convertTo<std::string, int16_t>(const std::string &str, int16_t &result)
235 {
236     int64_t res;
237 
238     if (!convertTo<std::string, int64_t>(str, res)) {
239         return false;
240     }
241 
242     if (res > std::numeric_limits<int16_t>::max() || res < std::numeric_limits<int16_t>::min()) {
243         return false;
244     }
245 
246     result = static_cast<int16_t>(res);
247     return true;
248 }
249 
250 /**
251  * Specialization for float of convertTo template function.
252  *
253  * This function follows the same paradigm than it's generic version and is
254  * based on it but makes furthers checks on the returned value.
255  *
256  * The specific implementation is made necessary because the stlport conversion
257  * from string to float behaves differently than GNU STL: overflow produce
258  * +/-Infinity rather than an error.
259  *
260  * @param[in]  str    the string to parse.
261  * @param[out] result reference to object where to store the result.
262  *
263  * @return true if conversion was successful, false otherwise.
264  */
265 template <>
266 inline bool convertTo<std::string, float>(const std::string &str, float &result)
267 {
268     if (!details::Converter<std::string, float>::run(str, result)) {
269         return false;
270     }
271 
272     if (std::abs(result) == std::numeric_limits<float>::infinity() ||
273         result == std::numeric_limits<float>::quiet_NaN()) {
274         return false;
275     }
276 
277     return true;
278 }
279 
280 /**
281  * Specialization for double of convertTo template function.
282  *
283  * This function follows the same paradigm than it's generic version and is
284  * based on it but makes furthers checks on the returned value.
285  *
286  * The specific implementation is made necessary because the stlport conversion
287  * from string to double behaves differently than GNU STL: overflow produce
288  * +/-Infinity rather than an error.
289  *
290  * @param[in]  str    the string to parse.
291  * @param[out] result reference to object where to store the result.
292  *
293  * @return true if conversion was successful, false otherwise.
294  */
295 template <>
296 inline bool convertTo<std::string, double>(const std::string &str, double &result)
297 {
298     if (!details::Converter<std::string, double>::run(str, result)) {
299         return false;
300     }
301 
302     if (std::abs(result) == std::numeric_limits<double>::infinity() ||
303         result == std::numeric_limits<double>::quiet_NaN()) {
304         return false;
305     }
306 
307     return true;
308 }
309 
310 /**
311  * Specialization for boolean of convertTo template function.
312  *
313  * This function follows the same paradigm than it's generic version.
314  * This function accepts to parse boolean as "0/1" or "false/true" or
315  * "FALSE/TRUE".
316  * The specific implementation is made necessary because the behaviour of
317  * string streams when parsing boolean values is not sufficient to fit our
318  * requirements. Indeed, parsing "true" will correctly parse the value, but the
319  * end of stream is not reached which makes the ss.eof() fails in the generic
320  * implementation.
321  *
322  * @param[in]  str    the string to parse.
323  * @param[out] result reference to object where to store the result.
324  *
325  * @return true if conversion was successful, false otherwise.
326  */
327 template <>
328 inline bool convertTo<std::string, bool>(const std::string &str, bool &result)
329 {
330     if (str == "0" || str == "FALSE" || str == "false") {
331         result = false;
332         return true;
333     }
334 
335     if (str == "1" || str == "TRUE" || str == "true") {
336         result = true;
337         return true;
338     }
339 
340     return false;
341 }
342 
343 /**
344  * Specialization for boolean to string of convertTo template function.
345  *
346  * This function follows the same paradigm than it's generic version.
347  * This function arbitrarily decides to return "false/true".
348  * It is compatible with the specialization from string to boolean.
349  *
350  * @param[in]  isSet  boolean to convert to a string.
351  * @param[out] result reference to object where to store the result.
352  *
353  * @return true if conversion was successful, false otherwise.
354  */
355 template <>
356 inline bool convertTo<bool, std::string>(const bool &isSet, std::string &result)
357 {
358     result = isSet ? "true" : "false";
359     return true;
360 }
361 
362 /**
363  * Specialization for string to string of convertTo template function.
364  *
365  * This function is a dummy conversion from string to string.
366  * In case of clients using template as well, this implementation avoids adding extra
367  * specialization to bypass the conversion from string to string.
368  *
369  * @param[in]  str    the string to parse.
370  * @param[out] result reference to object where to store the result.
371  *
372  * @return true if conversion was successful, false otherwise.
373  */
374 template <>
375 inline bool convertTo<std::string, std::string>(const std::string &str, std::string &result)
376 {
377     result = str;
378     return true;
379 }
380 
381 } // namespace utilities
382 
383 } // namespace android
384