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. */
41namespace details
42{
43
44/** Helper class to limit instantiation of templates */
45template<typename T>
46struct ConvertionAllowed;
47
48/* List of allowed types for conversion */
49template<> struct ConvertionAllowed<bool> {};
50template<> struct ConvertionAllowed<uint64_t> {};
51template<> struct ConvertionAllowed<int64_t> {};
52template<> struct ConvertionAllowed<uint32_t> {};
53template<> struct ConvertionAllowed<int32_t> {};
54template<> struct ConvertionAllowed<uint16_t> {};
55template<> struct ConvertionAllowed<int16_t> {};
56template<> struct ConvertionAllowed<float> {};
57template<> struct ConvertionAllowed<double> {};
58
59template<typename T>
60static 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 */
115template<typename T>
116static 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 */
135template<>
136inline 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 */
167template<>
168inline 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 */
197template<>
198inline 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 */
229template<>
230inline 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