* Bug 16365 fixed: median(m,'r'|'c') was wrong after 5dc990
[scilab.git] / scilab / modules / scicos / src / cpp / var2vec.cpp
1 /*
2 *  Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3 *  Copyright (C) 2015 - Scilab Enterprises - Paul Bignier
4 *
5 * Copyright (C) 2012 - 2016 - Scilab Enterprises
6 *
7 * This file is hereby licensed under the terms of the GNU GPL v2.0,
8 * pursuant to article 5.3.4 of the CeCILL v.2.1.
9 * This file was originally licensed under the terms of the CeCILL v2.1,
10 * and continues to be available under such terms.
11 * For more information, see the COPYING file which you should have received
12 * along with this program.
13 *
14 */
15
16 #include <vector>
17 #include <string>
18 #include <cwchar>
19 #include <cstring>
20
21 #include "var2vec.hxx"
22
23 #include "types.hxx"
24 #include "internal.hxx"
25 #include "double.hxx"
26 #include "int.hxx"
27 #include "bool.hxx"
28 #include "string.hxx"
29 #include "list.hxx"
30 #include "tlist.hxx"
31 #include "mlist.hxx"
32 //#include "struct.hxx"
33
34 extern "C"
35 {
36 #include "api_scilab.h"
37
38 #include "charEncoding.h"
39 #include "Scierror.h"
40 #include "localization.h"
41 }
42
43 static const std::string var2vecName = "var2vec";
44
45 static void computeDims(types::GenericType* input, int &iDims, int* &pDims, int &iElements, int &totalSize)
46 {
47     iDims = input->getDims();
48     pDims = input->getDimsArray();
49     iElements = 1;
50     for (int i = 0; i < iDims; ++i)
51     {
52         iElements *= pDims[i];
53     }
54
55     // Type + number of dimensions + dimensions
56     totalSize = 2 + iDims;
57 }
58
59 /**
60  * Calculate the length increment depending on the ::value_type of 'V' and the type of the Scilab type 'T'
61  *
62  * @param V variable that must have a 'value_type' field
63  * @param T Scilab type
64  * @param v the instance of the Scilab type
65  * @return the number of V elements used to store the data
66  */
67 template<typename V, typename T>
68 size_t required_length(const V /*it*/, T* v)
69 {
70     const size_t sizeof_ret_value = sizeof(typename V::value_type);
71     if (sizeof(typename T::type) >= sizeof_ret_value)
72     {
73         return v->getSize() * sizeof(typename T::type) / sizeof_ret_value;
74     }
75     else
76     {
77         // Increase the size to contain enough space, manage the size_t rounding issue
78         return (v->getSize() * sizeof(typename T::type) + (sizeof_ret_value - 1)) / sizeof_ret_value;
79     }
80 }
81
82 template <typename T>
83 void encode(T* input, std::vector<double> &ret)
84 {
85     int iDims, iElements, totalSize;
86     int* pDims;
87     computeDims(input, iDims, pDims, iElements, totalSize);
88
89     const int nDoubleNeeded = static_cast<int>(required_length(ret, input));
90     totalSize += nDoubleNeeded;
91
92     // Allocation for type + number of dimensions + each dimension + each element
93     ret.reserve(ret.size() + totalSize);
94
95     int iType = 0;
96     getVarType(nullptr, (int*) input, &iType);
97     ret.push_back(iType);
98     if (iType != sci_boolean)
99     {
100         int iPrec = 0;
101         getMatrixOfIntegerPrecision(nullptr, (int*) input, &iPrec);
102         ret.push_back(iPrec);
103     }
104
105     ret.push_back(iDims);
106     for (int i = 0; i < iDims; ++i)
107     {
108         ret.push_back(pDims[i]);
109     }
110
111     // Using contiguity of the memory, we save the input into 'ret'
112     // Use a buffer to fill the entirety of 'ret'
113     size_t size = ret.size();
114     ret.resize(size + nDoubleNeeded);
115     double* data = ret.data() + size;
116     memcpy(data, input->get(), iElements * sizeof(typename T::type));
117 }
118
119 static void encode(types::Double* input, std::vector<double> &ret)
120 {
121     int iDims, iElements, totalSize;
122     int* pDims;
123     computeDims(input, iDims, pDims, iElements, totalSize);
124
125     const int isComplex = (input->isComplex()) ? 1 : 0;
126     totalSize++; // Add the complex boolean
127     totalSize += (isComplex + 1) * iElements; // Size of the required data buffer
128
129     // Allocation for type + number of dimensions + each dimension + complex boolean + each element (doubled if complex)
130     ret.reserve(ret.size() + totalSize);
131
132     ret.push_back(sci_matrix);
133     ret.push_back(iDims);
134     for (int i = 0; i < iDims; ++i)
135     {
136         ret.push_back(pDims[i]);
137     }
138     ret.push_back(isComplex);
139
140     size_t size = ret.size();
141     ret.resize(size + iElements * (isComplex + 1));
142     double* data = ret.data() + size;
143     memcpy(data, input->getReal(), iElements * sizeof(double));
144
145     if (isComplex == 1)
146     {
147         memcpy(data + iElements, input->getImg(), iElements * sizeof(double));
148     }
149
150     // An empty matrix input will return [12; 2; 0; 0; 0]
151 }
152
153 static void encode(types::String* input, std::vector<double> &ret)
154 {
155     int iDims, iElements, totalSize;
156     int* pDims;
157     computeDims(input, iDims, pDims, iElements, totalSize);
158
159     totalSize += iElements;
160
161     // Reserve space for the string offsets and contents
162     char** utf8 = new char*[iElements];
163     size_t* pLengths = new size_t[iElements];
164     int* offsets = new int[iElements];
165     int offset_cur = 0, offset_acc = 0;
166     for (int i = 0; i < iElements; ++i)
167     {
168         char* str = wide_string_to_UTF8(input->get(i));
169         utf8[i] = str;
170         // Adding the '\0' byte to the length
171         const size_t len = strlen(str) + 1;
172         pLengths[i] = len;
173
174         offset_cur = static_cast<int>((len * sizeof(char) + sizeof(double) - 1) / sizeof(double));
175         totalSize += offset_cur;
176         offset_acc += offset_cur;
177         offsets[i] = offset_acc;
178     }
179
180     // Allocation for type + number of dimensions + each dimension + each element length + each element
181     ret.reserve(ret.size() + totalSize);
182
183     ret.push_back(sci_strings);
184     ret.push_back(iDims);
185     for (int i = 0; i < iDims; ++i)
186     {
187         ret.push_back(pDims[i]);
188     }
189     for (int i = 0; i < iElements; ++i)
190     {
191         ret.push_back(offsets[i]);
192     }
193
194     if (iElements > 0)
195     {
196         size_t size = ret.size();
197         ret.resize(size + offsets[iElements - 1]);
198         double* data = ret.data() + size;
199
200         size_t len = pLengths[0];
201         memcpy(data, utf8[0], len * sizeof(char));
202         data += offsets[0];
203         for (int i = 1; i < iElements; ++i)
204         {
205             size_t len = pLengths[i];
206             memcpy(data, utf8[i], len * sizeof(char));
207             data += offsets[i] - offsets[i - 1];
208         }
209     }
210
211     // Free all the strings, after being copied
212     for (int i = 0; i < iElements; ++i)
213     {
214         FREE(utf8[i]);
215     }
216     delete[] utf8;
217     delete[] offsets;
218     delete[] pLengths;
219 }
220
221 static void encode(types::List* input, std::vector<double> &ret)
222 {
223     const int iElements = input->getSize();
224
225     int iType = 0;
226     getVarType(nullptr, (int*) input, &iType);
227     ret.push_back(iType);
228     ret.push_back(iElements);
229     for (int i = 0; i < iElements; ++i)
230     {
231         // Recursively call var2vec on each element and extract the obtained results
232         if (!var2vec(input->get(i), ret))
233         {
234             ret.back() = -1;
235             break;
236         }
237     }
238
239     // An empty list input will return [22; 0], a tlist [23; 0] and an mlist [24; 0]
240 }
241
242 // Structs are not used yet
243 //static void encode(types::Struct* input, std::vector<double> &ret)
244 //{
245 //    const bool isEmpty = input->getSize() == 0;
246
247 //    types::String* fields = nullptr;
248 //    int iElements = 0;
249 //    if (!isEmpty)
250 //    {
251 //        fields = input->getFieldNames();
252 //        iElements = fields->getSize();
253 //    }
254
255 //    ret.push_back(input->getType());
256 //    ret.push_back(iElements);
257 //    if (!isEmpty)
258 //    {
259 //        // Call var2vec on the struct's fields to extract a header
260 //        var2vec(fields, ret);
261
262 //        // Now extract the fields' content, assuming this is not a multidimensional struct
263 //        types::SingleStruct* content = input->get(0);
264 //        for (int i = 0; i < iElements; ++i)
265 //        {
266 //            var2vec(content->get(fields->get(i)), ret);
267 //        }
268
269
270 //        fields->killMe();
271 //    }
272
273 //    // An empty struct input will return [26; 0]
274 //}
275
276 bool var2vec(types::InternalType* in, std::vector<double> &out)
277 {
278     int iType = 0;
279     getVarType(nullptr, (int*) in, &iType);
280     switch (iType)
281     {
282         // Reuse scicos model encoding for 'model.opar' and 'model.odstate' fields
283         case sci_matrix  :
284             encode(in->getAs<types::Double>(), out);
285             break;
286
287         case sci_ints    :
288             switch (in->getType())
289             {
290                 case types::InternalType::ScilabInt8 :
291                     encode(in->getAs<types::Int8>(), out);
292                     break;
293                 case types::InternalType::ScilabUInt8  :
294                     encode(in->getAs<types::UInt8>(), out);
295                     break;
296                 case types::InternalType::ScilabInt16  :
297                     encode(in->getAs<types::Int16>(), out);
298                     break;
299                 case types::InternalType::ScilabUInt16 :
300                     encode(in->getAs<types::UInt16>(), out);
301                     break;
302                 case types::InternalType::ScilabInt32  :
303                     encode(in->getAs<types::Int32>(), out);
304                     break;
305                 case types::InternalType::ScilabUInt32 :
306                     encode(in->getAs<types::UInt32>(), out);
307                     break;
308                 case types::InternalType::ScilabInt64  :
309                     encode(in->getAs<types::Int64>(), out);
310                     break;
311                 case types::InternalType::ScilabUInt64 :
312                     encode(in->getAs<types::UInt64>(), out);
313                     break;
314                 default :
315                     Scierror(999, _("%s: Wrong type for input argument #%d: unknown integer type.\n"), var2vecName.c_str(), 1);
316                     return false;
317             }
318             break;
319         case sci_boolean :
320             encode(in->getAs<types::Bool>(), out);
321             break;
322
323         case sci_strings :
324             encode(in->getAs<types::String>(), out);
325             break;
326
327         case sci_list    :
328             encode(in->getAs<types::List>(), out);
329             break;
330         case sci_tlist   :
331             encode(in->getAs<types::List>(), out);
332             break;
333         case sci_mlist   :
334             switch (in->getType())
335             {
336                 case types::InternalType::ScilabMList :
337                     encode(in->getAs<types::List>(), out);
338                     if (out.back() == -1)
339                     {
340                         Scierror(999, _("%s: Wrong value for input argument #%d: Could not read its content.\n"), var2vecName.c_str(), 1);
341                         return false;
342                     }
343                     break;
344                 default : // types::InternalType::ScilabStruct
345                     //encode(in->getAs<types::Struct>(), out);
346                     Scierror(999, _("%s: Wrong type for input argument #%d: %s, %s, %s, %s or %s type.\n"), var2vecName.c_str(), 1, "Double", "Integer", "Boolean", "String", "List");
347                     return false;
348             }
349             break;
350
351         default :
352             //Scierror(999, _("%s: Wrong type for input argument #%d: %s, %s, %s, %s, %s or %s type.\n"), var2vecName.c_str(), 1, "Double", "Integer", "Boolean", "String", "List", "Struct");
353             Scierror(999, _("%s: Wrong type for input argument #%d: %s, %s, %s, %s or %s type.\n"), var2vecName.c_str(), 1, "Double", "Integer", "Boolean", "String", "List");
354             return false;
355     }
356
357     return true;
358 }