1 /*
2  * Copyright (c) 2011-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, 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 #include "FixedPointParameterType.h"
31 #include <stdlib.h>
32 #include <sstream>
33 #include <iomanip>
34 #include <assert.h>
35 #include <math.h>
36 #include "Parameter.h"
37 #include "ParameterAccessContext.h"
38 #include "ConfigurationAccessContext.h"
39 #include "Utility.h"
40 #include <errno.h>
41 #include <convert.hpp>
42 
43 #define base CParameterType
44 
45 using std::string;
46 
CFixedPointParameterType(const string & strName)47 CFixedPointParameterType::CFixedPointParameterType(const string &strName) : base(strName)
48 {
49 }
50 
getKind() const51 string CFixedPointParameterType::getKind() const
52 {
53     return "FixedPointParameter";
54 }
55 
56 // Element properties
showProperties(string & strResult) const57 void CFixedPointParameterType::showProperties(string &strResult) const
58 {
59     base::showProperties(strResult);
60 
61     // Notation
62     strResult += "Notation: Q";
63     strResult += std::to_string(_uiIntegral);
64     strResult += ".";
65     strResult += std::to_string(_uiFractional);
66     strResult += "\n";
67 }
68 
69 // XML Serialization value space handling
70 // Value space handling for configuration import
handleValueSpaceAttribute(CXmlElement & xmlConfigurableElementSettingsElement,CConfigurationAccessContext & configurationAccessContext) const71 void CFixedPointParameterType::handleValueSpaceAttribute(
72     CXmlElement &xmlConfigurableElementSettingsElement,
73     CConfigurationAccessContext &configurationAccessContext) const
74 {
75     // Direction?
76     if (!configurationAccessContext.serializeOut()) {
77 
78         string strValueSpace;
79         xmlConfigurableElementSettingsElement.getAttribute("ValueSpace", strValueSpace);
80         configurationAccessContext.setValueSpaceRaw(strValueSpace == "Raw");
81     } else {
82         // Provide value space only if not the default one
83         if (configurationAccessContext.valueSpaceIsRaw()) {
84 
85             xmlConfigurableElementSettingsElement.setAttribute("ValueSpace", "Raw");
86         }
87     }
88 }
89 
fromXml(const CXmlElement & xmlElement,CXmlSerializingContext & serializingContext)90 bool CFixedPointParameterType::fromXml(const CXmlElement &xmlElement,
91                                        CXmlSerializingContext &serializingContext)
92 {
93     // Size
94     size_t sizeInBits = 0;
95     xmlElement.getAttribute("Size", sizeInBits);
96 
97     // Q notation
98     xmlElement.getAttribute("Integral", _uiIntegral);
99     xmlElement.getAttribute("Fractional", _uiFractional);
100 
101     // Size vs. Q notation integrity check
102     if (sizeInBits < getUtilSizeInBits()) {
103 
104         std::string size;
105         xmlElement.getAttribute("Size", size);
106         serializingContext.setError(
107             "Inconsistent Size vs. Q notation for " + getKind() + " " + xmlElement.getPath() +
108             ": Summing (Integral + _uiFractional + 1) should not exceed given Size (" + size + ")");
109 
110         return false;
111     }
112 
113     // Set the size
114     setSize(sizeInBits / 8);
115 
116     return base::fromXml(xmlElement, serializingContext);
117 }
118 
toBlackboard(const string & strValue,uint32_t & uiValue,CParameterAccessContext & parameterAccessContext) const119 bool CFixedPointParameterType::toBlackboard(const string &strValue, uint32_t &uiValue,
120                                             CParameterAccessContext &parameterAccessContext) const
121 {
122     bool bValueProvidedAsHexa = utility::isHexadecimal(strValue);
123 
124     // Check data integrity
125     if (bValueProvidedAsHexa && !parameterAccessContext.valueSpaceIsRaw()) {
126 
127         parameterAccessContext.setError("Hexadecimal values are not supported for " + getKind() +
128                                         " when selected value space is real:");
129 
130         return false;
131     }
132 
133     if (parameterAccessContext.valueSpaceIsRaw()) {
134 
135         if (bValueProvidedAsHexa) {
136 
137             return convertFromHexadecimal(strValue, uiValue, parameterAccessContext);
138         }
139         return convertFromDecimal(strValue, uiValue, parameterAccessContext);
140     }
141     return convertFromQnm(strValue, uiValue, parameterAccessContext);
142 }
143 
setOutOfRangeError(const string & strValue,CParameterAccessContext & parameterAccessContext) const144 void CFixedPointParameterType::setOutOfRangeError(
145     const string &strValue, CParameterAccessContext &parameterAccessContext) const
146 {
147     std::ostringstream stream;
148 
149     stream << "Value " << strValue << " standing out of admitted ";
150 
151     if (!parameterAccessContext.valueSpaceIsRaw()) {
152 
153         // Min/Max computation
154         double dMin = 0;
155         double dMax = 0;
156         getRange(dMin, dMax);
157 
158         stream << std::fixed << std::setprecision(_uiFractional) << "real range [" << dMin << ", "
159                << dMax << "]";
160     } else {
161 
162         // Min/Max computation
163         int32_t iMax = getMaxValue<uint32_t>();
164         int32_t iMin = -iMax - 1;
165 
166         stream << "raw range [";
167 
168         if (utility::isHexadecimal(strValue)) {
169 
170             stream << std::hex << std::uppercase << std::setw(static_cast<int>(getSize()) * 2)
171                    << std::setfill('0');
172 
173             // Format Min
174             stream << "0x" << makeEncodable(iMin);
175             // Format Max
176             stream << ", 0x" << makeEncodable(iMax);
177 
178         } else {
179 
180             stream << iMin << ", " << iMax;
181         }
182 
183         stream << "]";
184     }
185     stream << " for " << getKind();
186 
187     parameterAccessContext.setError(stream.str());
188 }
189 
fromBlackboard(string & strValue,const uint32_t & value,CParameterAccessContext & parameterAccessContext) const190 bool CFixedPointParameterType::fromBlackboard(string &strValue, const uint32_t &value,
191                                               CParameterAccessContext &parameterAccessContext) const
192 {
193     // Check encodability
194     assert(isEncodable(value, false));
195 
196     // Format
197     std::ostringstream stream;
198 
199     // Raw formatting?
200     if (parameterAccessContext.valueSpaceIsRaw()) {
201         // Hexa formatting?
202         if (parameterAccessContext.outputRawFormatIsHex()) {
203             uint32_t data = static_cast<uint32_t>(value);
204 
205             stream << "0x" << std::hex << std::uppercase
206                    << std::setw(static_cast<int>(getSize() * 2)) << std::setfill('0') << data;
207         } else {
208             int32_t data = value;
209 
210             // Sign extend
211             signExtend(data);
212 
213             stream << data;
214         }
215     } else {
216         int32_t data = value;
217 
218         // Sign extend
219         signExtend(data);
220 
221         // Conversion
222         stream << std::fixed << std::setprecision(_uiFractional) << binaryQnmToDouble(data);
223     }
224 
225     strValue = stream.str();
226 
227     return true;
228 }
229 
230 // Value access
toBlackboard(double dUserValue,uint32_t & uiValue,CParameterAccessContext & parameterAccessContext) const231 bool CFixedPointParameterType::toBlackboard(double dUserValue, uint32_t &uiValue,
232                                             CParameterAccessContext &parameterAccessContext) const
233 {
234     // Check that the value is within the allowed range for this type
235     if (!checkValueAgainstRange(dUserValue)) {
236 
237         // Illegal value provided
238         parameterAccessContext.setError("Value out of range");
239 
240         return false;
241     }
242 
243     // Do the conversion
244     int32_t iData = doubleToBinaryQnm(dUserValue);
245 
246     // Check integrity
247     assert(isEncodable((uint32_t)iData, true));
248 
249     uiValue = iData;
250 
251     return true;
252 }
253 
fromBlackboard(double & dUserValue,uint32_t uiValue,CParameterAccessContext &) const254 bool CFixedPointParameterType::fromBlackboard(double &dUserValue, uint32_t uiValue,
255                                               CParameterAccessContext & /*ctx*/) const
256 {
257     int32_t iData = uiValue;
258 
259     // Check unsigned value is encodable
260     assert(isEncodable(uiValue, false));
261 
262     // Sign extend
263     signExtend(iData);
264 
265     dUserValue = binaryQnmToDouble(iData);
266 
267     return true;
268 }
269 
270 // Util size
getUtilSizeInBits() const271 size_t CFixedPointParameterType::getUtilSizeInBits() const
272 {
273     return _uiIntegral + _uiFractional + 1;
274 }
275 
276 // Compute the range for the type (minimum and maximum values)
getRange(double & dMin,double & dMax) const277 void CFixedPointParameterType::getRange(double &dMin, double &dMax) const
278 {
279     dMax = ((1U << (_uiIntegral + _uiFractional)) - 1) / double(1U << _uiFractional);
280     dMin = -((1U << (_uiIntegral + _uiFractional)) / double(1U << _uiFractional));
281 }
282 
convertFromHexadecimal(const string & strValue,uint32_t & uiValue,CParameterAccessContext & parameterAccessContext) const283 bool CFixedPointParameterType::convertFromHexadecimal(
284     const string &strValue, uint32_t &uiValue,
285     CParameterAccessContext &parameterAccessContext) const
286 {
287     // For hexadecimal representation, we need full 32 bit range conversion.
288     if (!convertTo(strValue, uiValue) || !isEncodable(uiValue, false)) {
289 
290         setOutOfRangeError(strValue, parameterAccessContext);
291         return false;
292     }
293     signExtend(reinterpret_cast<int32_t &>(uiValue));
294 
295     // check that the data is encodable and can been safely written to the blackboard
296     assert(isEncodable(uiValue, true));
297 
298     return true;
299 }
300 
convertFromDecimal(const string & strValue,uint32_t & uiValue,CParameterAccessContext & parameterAccessContext) const301 bool CFixedPointParameterType::convertFromDecimal(
302     const string &strValue, uint32_t &uiValue,
303     CParameterAccessContext &parameterAccessContext) const
304 {
305     if (!convertTo(strValue, reinterpret_cast<int32_t &>(uiValue)) || !isEncodable(uiValue, true)) {
306 
307         setOutOfRangeError(strValue, parameterAccessContext);
308         return false;
309     }
310     return true;
311 }
312 
convertFromQnm(const string & strValue,uint32_t & uiValue,CParameterAccessContext & parameterAccessContext) const313 bool CFixedPointParameterType::convertFromQnm(const string &strValue, uint32_t &uiValue,
314                                               CParameterAccessContext &parameterAccessContext) const
315 {
316     double dData = 0;
317 
318     if (!convertTo(strValue, dData) || !checkValueAgainstRange(dData)) {
319 
320         setOutOfRangeError(strValue, parameterAccessContext);
321         return false;
322     }
323     uiValue = static_cast<uint32_t>(doubleToBinaryQnm(dData));
324 
325     // check that the data is encodable and has been safely written to the blackboard
326     assert(isEncodable(uiValue, true));
327 
328     return true;
329 }
330 
331 // Check that the value is within available range for this type
checkValueAgainstRange(double dValue) const332 bool CFixedPointParameterType::checkValueAgainstRange(double dValue) const
333 {
334     double dMin = 0;
335     double dMax = 0;
336     getRange(dMin, dMax);
337 
338     return (dValue <= dMax) && (dValue >= dMin);
339 }
340 
341 // Data conversion
doubleToBinaryQnm(double dValue) const342 int32_t CFixedPointParameterType::doubleToBinaryQnm(double dValue) const
343 {
344     // For Qn.m number, multiply by 2^n and round to the nearest integer
345     int32_t iData = static_cast<int32_t>(round(dValue * double(1UL << _uiFractional)));
346     // Left justify
347     // For a Qn.m number, shift 32 - (n + m + 1) bits to the left (the rest of
348     // the bits aren't used)
349     iData <<= getSize() * 8 - getUtilSizeInBits();
350 
351     return iData;
352 }
353 
binaryQnmToDouble(int32_t iValue) const354 double CFixedPointParameterType::binaryQnmToDouble(int32_t iValue) const
355 {
356     // Unjustify
357     iValue >>= getSize() * 8 - getUtilSizeInBits();
358     return static_cast<double>(iValue) / double(1UL << _uiFractional);
359 }
360 
361 // From IXmlSource
toXml(CXmlElement & xmlElement,CXmlSerializingContext & serializingContext) const362 void CFixedPointParameterType::toXml(CXmlElement &xmlElement,
363                                      CXmlSerializingContext &serializingContext) const
364 {
365     // Size
366     xmlElement.setAttribute("Size", getSize() * 8);
367 
368     // Integral
369     xmlElement.setAttribute("Integral", _uiIntegral);
370 
371     // Fractional
372     xmlElement.setAttribute("Fractional", _uiFractional);
373 
374     base::toXml(xmlElement, serializingContext);
375 }
376