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