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
26namespace android
27{
28
29namespace 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 */
53template <typename srcType, typename dstType>
54static 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. */
58namespace details
59{
60
61/** Helper class to limit instantiation of templates */
62template <typename T>
63struct ConversionFromStringAllowed;
64template <typename T>
65struct ConversionToStringAllowed;
66
67/* List of allowed types for conversion */
68template <>
69struct ConversionFromStringAllowed<bool> {};
70template <>
71struct ConversionFromStringAllowed<uint64_t> {};
72template <>
73struct ConversionFromStringAllowed<int64_t> {};
74template <>
75struct ConversionFromStringAllowed<uint32_t> {};
76template <>
77struct ConversionFromStringAllowed<int32_t> {};
78template <>
79struct ConversionFromStringAllowed<uint16_t> {};
80template <>
81struct ConversionFromStringAllowed<int16_t> {};
82template <>
83struct ConversionFromStringAllowed<float> {};
84template <>
85struct ConversionFromStringAllowed<double> {};
86
87template <>
88struct ConversionToStringAllowed<int64_t> {};
89template <>
90struct ConversionToStringAllowed<uint64_t> {};
91template <>
92struct ConversionToStringAllowed<uint32_t> {};
93template <>
94struct ConversionToStringAllowed<int32_t> {};
95template <>
96struct ConversionToStringAllowed<double> {};
97template <>
98struct 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 */
105static const uint32_t gFloatPrecision = 10;
106
107template <typename T>
108static 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
145template <typename T>
146static 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
160template <typename srcType, typename dstType>
161class Converter;
162
163template <typename dstType>
164class Converter<std::string, dstType>
165{
166public:
167    static inline bool run(const std::string &str, dstType &result)
168    {
169        return fromString<dstType>(str, result);
170    }
171};
172
173template <typename srcType>
174class Converter<srcType, std::string>
175{
176public:
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 */
188template <typename SrcElem, typename DstElem>
189class Converter<std::vector<SrcElem>, std::vector<DstElem> >
190{
191public:
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
213template <typename srcType, typename dstType>
214static 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 */
233template <>
234inline 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 */
265template <>
266inline 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 */
295template <>
296inline 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 */
327template <>
328inline 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 */
355template <>
356inline 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 */
374template <>
375inline 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