1/*
2 * Copyright (c) 2014-2015, 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, Value, 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#include "FloatingPointParameterType.h"
31#include <sstream>
32#include <iomanip>
33#include "ParameterAccessContext.h"
34#include "ConfigurationAccessContext.h"
35#include <limits>
36#include <climits>
37#include "convert.hpp"
38#include "Utility.h"
39#include "BinaryCopy.hpp"
40
41using std::string;
42
43CFloatingPointParameterType::CFloatingPointParameterType(const string &strName) : base(strName)
44{
45}
46
47string CFloatingPointParameterType::getKind() const
48{
49    return "FloatingPointParameter";
50}
51
52// Element properties
53void CFloatingPointParameterType::showProperties(string &strResult) const
54{
55    base::showProperties(strResult);
56
57    strResult += "Min:" + std::to_string(_fMin) + "\n" + "Max:" + std::to_string(_fMax) + "\n";
58}
59
60void CFloatingPointParameterType::handleValueSpaceAttribute(
61    CXmlElement &xmlConfigurableElementSettingsElement,
62    CConfigurationAccessContext &configurationAccessContext) const
63{
64    if (!configurationAccessContext.serializeOut()) {
65
66        string strValueSpace;
67
68        if (xmlConfigurableElementSettingsElement.getAttribute("ValueSpace", strValueSpace)) {
69
70            configurationAccessContext.setValueSpaceRaw(strValueSpace == "Raw");
71        } else {
72
73            configurationAccessContext.setValueSpaceRaw(false);
74        }
75    } else {
76        // Set the value space only if it is raw (i.e. not the default one)
77        if (configurationAccessContext.valueSpaceIsRaw()) {
78
79            xmlConfigurableElementSettingsElement.setAttribute("ValueSpace", "Raw");
80        }
81    }
82}
83
84bool CFloatingPointParameterType::fromXml(const CXmlElement &xmlElement,
85                                          CXmlSerializingContext &serializingContext)
86{
87    // Size. The XSD fixes it to 32
88    size_t sizeInBits = 32;
89    xmlElement.getAttribute("Size", sizeInBits);
90
91    // Size support check: only floats are supported
92    // (e.g. doubles are not supported)
93    if (sizeInBits != sizeof(float) * CHAR_BIT) {
94
95        serializingContext.setError("Unsupported size (" + std::to_string(sizeInBits) + ") for " +
96                                    getKind() + " " + xmlElement.getPath() +
97                                    ". For now, only 32 is supported.");
98
99        return false;
100    }
101
102    setSize(sizeInBits / CHAR_BIT);
103
104    xmlElement.getAttribute("Min", _fMin);
105    xmlElement.getAttribute("Max", _fMax);
106
107    if (_fMin > _fMax) {
108        serializingContext.setError("Min (" + std::to_string(_fMin) +
109                                    ") can't be greater than Max (" + std::to_string(_fMax) + ")");
110        return false;
111    }
112
113    return base::fromXml(xmlElement, serializingContext);
114}
115
116bool CFloatingPointParameterType::toBlackboard(
117    const string &strValue, uint32_t &uiValue,
118    CParameterAccessContext &parameterAccessContext) const
119{
120    // Check Value integrity
121    if (utility::isHexadecimal(strValue) && !parameterAccessContext.valueSpaceIsRaw()) {
122
123        parameterAccessContext.setError("Hexadecimal values are not supported for " + getKind() +
124                                        " when selected value space is real: " + strValue);
125
126        return false;
127    }
128
129    if (parameterAccessContext.valueSpaceIsRaw()) {
130        // Raw value: interpret the user input as the memory content of the
131        // parameter
132        if (!convertTo(strValue, uiValue)) {
133
134            parameterAccessContext.setError("Value '" + strValue + "' is invalid");
135            return false;
136        }
137
138        auto fData = utility::binaryCopy<float>(uiValue);
139
140        // Check against NaN or infinity
141        if (!std::isfinite(fData)) {
142
143            parameterAccessContext.setError("Value " + strValue + " is not a finite number");
144            return false;
145        }
146
147        if (!checkValueAgainstRange(fData)) {
148
149            setOutOfRangeError(strValue, parameterAccessContext);
150            return false;
151        }
152        return true;
153    } else {
154
155        float fValue = 0.0f;
156
157        // Interpret the user input as float
158        if (!convertTo(strValue, fValue)) {
159
160            parameterAccessContext.setError("Value " + strValue + " is invalid");
161            return false;
162        }
163
164        if (!checkValueAgainstRange(fValue)) {
165
166            setOutOfRangeError(strValue, parameterAccessContext);
167            return false;
168        }
169
170        // Move to the "raw memory" value space
171        uiValue = utility::binaryCopy<decltype(uiValue)>(fValue);
172        return true;
173    }
174}
175
176void CFloatingPointParameterType::setOutOfRangeError(
177    const string &strValue, CParameterAccessContext &parameterAccessContext) const
178{
179    // error message buffer
180    std::ostringstream ostrStream;
181
182    ostrStream << "Value " << strValue << " standing out of admitted ";
183
184    if (!parameterAccessContext.valueSpaceIsRaw()) {
185
186        ostrStream << "real range [" << _fMin << ", " << _fMax << "]";
187    } else {
188
189        auto uiMin = utility::binaryCopy<uint32_t>(_fMin);
190        auto uiMax = utility::binaryCopy<uint32_t>(_fMax);
191
192        if (utility::isHexadecimal(strValue)) {
193
194            ostrStream << std::showbase << std::hex << std::setw(static_cast<int>(getSize() * 2))
195                       << std::setfill('0');
196        }
197
198        ostrStream << "raw range [" << uiMin << ", " << uiMax << "]";
199    }
200    ostrStream << " for " << getKind();
201
202    parameterAccessContext.setError(ostrStream.str());
203}
204
205bool CFloatingPointParameterType::fromBlackboard(
206    string &strValue, const uint32_t &uiValue,
207    CParameterAccessContext &parameterAccessContext) const
208{
209    std::ostringstream ostrStream;
210
211    if (parameterAccessContext.valueSpaceIsRaw()) {
212
213        if (parameterAccessContext.outputRawFormatIsHex()) {
214
215            ostrStream << std::showbase << std::hex << std::setw(static_cast<int>(getSize() * 2))
216                       << std::setfill('0');
217        }
218
219        ostrStream << uiValue;
220    } else {
221
222        // Move from "raw memory" value space to real space
223        auto fValue = utility::binaryCopy<float>(uiValue);
224
225        ostrStream << fValue;
226    }
227
228    strValue = ostrStream.str();
229
230    return true;
231}
232
233// Value access
234bool CFloatingPointParameterType::toBlackboard(
235    double dUserValue, uint32_t &uiValue, CParameterAccessContext &parameterAccessContext) const
236{
237    if (!checkValueAgainstRange(dUserValue)) {
238
239        parameterAccessContext.setError("Value out of range");
240        return false;
241    }
242
243    // Cast is fine because dValue has been checked against the value range
244    float fValue = static_cast<float>(dUserValue);
245    uiValue = utility::binaryCopy<decltype(uiValue)>(fValue);
246    return true;
247}
248
249bool CFloatingPointParameterType::fromBlackboard(double &dUserValue, uint32_t uiValue,
250                                                 CParameterAccessContext & /*ctx*/) const
251{
252    // Move from "raw memory" value space to real space
253    auto fValue = utility::binaryCopy<float>(uiValue);
254
255    dUserValue = fValue;
256    return true;
257}
258
259bool CFloatingPointParameterType::checkValueAgainstRange(double dValue) const
260{
261    // Check that dValue can safely be cast to a float
262    // (otherwise, behaviour is undefined)
263    if ((dValue < -std::numeric_limits<float>::max()) ||
264        (dValue > std::numeric_limits<float>::max())) {
265        return false;
266    }
267
268    return checkValueAgainstRange(static_cast<float>(dValue));
269}
270
271bool CFloatingPointParameterType::checkValueAgainstRange(float fValue) const
272{
273    return fValue <= _fMax && fValue >= _fMin;
274}
275
276void CFloatingPointParameterType::toXml(CXmlElement &xmlElement,
277                                        CXmlSerializingContext &serializingContext) const
278{
279    xmlElement.setAttribute("Size", getSize() * CHAR_BIT);
280    xmlElement.setAttribute("Min", _fMin);
281    xmlElement.setAttribute("Max", _fMax);
282
283    base::toXml(xmlElement, serializingContext);
284}
285