call: fix allocation and check with string argument
[scilab.git] / scilab / modules / dynamic_link / sci_gateway / cpp / sci_call.cpp
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) Scilab Enterprises - 2015 - Antoine ELIAS
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 "api_scilab.h"
18 #include "configvariable.hxx"
19
20 extern "C"
21 {
22 #include "gw_dynamic_link.h"
23 #include "Scierror.h"
24 #include "localization.h"
25 }
26
27 bool isOut(scilabEnv env, scilabVar var);
28
29 typedef void(*fct)(void* p0, void* p1, void* p2, void* p3, void* p4, void* p5, void* p6, void* p7, void* p8, void* p9,
30                    void* p10, void* p11, void* p12, void* p13, void* p14, void* p15, void* p16, void* p17, void* p18, void* p19,
31                    void* p20, void* p21, void* p22, void* p23, void* p24, void* p25, void* p26, void* p27, void* p28, void* p29);
32
33
34 struct Parameter
35 {
36     Parameter() : data(nullptr), type(L'\0'), alloc(false), row(0), col(0) {}
37     ~Parameter()
38     {
39         if (alloc)
40         {
41             free(data);
42         }
43     }
44
45     void* data;
46     wchar_t type;
47     bool alloc;
48     int row;
49     int col;
50 };
51
52 static const char fname[] = "call";
53
54 int sci_call(scilabEnv env, int nin, scilabVar* in, int nopt, scilabOpt opt, int nout, scilabVar* out)
55 {
56     std::vector<Parameter> params(30);
57     std::vector<int> output_order(nout);
58     wchar_t* interf = NULL;
59     if (nin < 1)
60     {
61         Scierror(77, _("%s: Wrong number of input argument(s): %d expected.\n"), fname, 1);
62         return 1;
63     }
64
65     //1st is the interface name
66     if (scilab_isString(env, in[0]) == 0 || scilab_isScalar(env, in[0]) == 0)
67     {
68         Scierror(999, _("%s: Wrong type for input argument #%d: String expected.\n"), fname, 1);
69         return 1;
70     }
71
72     scilab_getString(env, in[0], &interf);
73
74     ConfigVariable::EntryPointStr* func = ConfigVariable::getEntryPoint(interf);
75     if (func == NULL)
76     {
77         Scierror(999, _("%s: unable to find entry point %ls.\n"), fname, interf);
78         return 1;
79     }
80
81     int pos = 1;
82     bool hasOutputs = false;
83     //inputs
84     while (pos < nin)
85     {
86         //check "out" to break loop
87         if (isOut(env, in[pos]))
88         {
89             hasOutputs = true;
90             break;
91         }
92
93         if (pos > nin)
94         {
95             break;
96         }
97
98         int type = 0;
99         if (nin < pos + 2)
100         {
101             Scierror(77, _("%s: Wrong number of input argument(s).\n"), fname);
102             return 1;
103         }
104
105         type = scilab_getType(env, in[pos]);
106         if (type != sci_matrix && type != sci_strings)
107         {
108             Scierror(77, _("%s: Wrong type for input argument #%d: A real matrix or a string expected.\n"), fname, pos + 1);
109             return 1;
110         }
111
112         //data
113
114         //position
115         if (scilab_isDouble(env, in[pos + 1]) == 0 || scilab_isScalar(env, in[pos + 1]) == 0)
116         {
117             Scierror(77, _("%s: Wrong type for input argument #%d : A real scalar expected.\n"), fname, pos + 2);
118             return 1;
119         }
120
121         double param_pos = 0;
122         scilab_getDouble(env, in[pos + 1], &param_pos);
123
124         //type
125         if (scilab_isString(env, in[pos + 2]) == 0 || scilab_isScalar(env, in[pos + 2]) == 0)
126         {
127             Scierror(77, _("%s: Wrong type for input argument #%d : string expected.\n"), fname, pos + 3);
128             return 1;
129         }
130
131         void* data = NULL;
132         int row = 0;
133         int col = 0;
134
135         wchar_t* param_type = NULL;
136         scilab_getString(env, in[pos + 2], &param_type);
137
138         if (param_type[0] == L'c' || type == sci_strings)
139         {
140             if (param_type[0] != L'c' || type != sci_strings)
141             {
142                 Scierror(77, _("%s: Wrong type for input argument #%d : string expected.\n"), fname, pos + 1);
143                 return 1;
144             }
145         }
146
147         bool alloc = false;
148         switch (param_type[0])
149         {
150             case L'c':
151             {
152                 wchar_t* strs = NULL;
153                 scilab_getString(env, in[pos], &strs);
154                 char* c = wide_string_to_UTF8(strs);
155                 data = c;
156                 alloc = true;
157                 break;
158             }
159             case L'd':
160             {
161                 double* dbls = NULL;
162                 scilab_getDoubleArray(env, in[pos], &dbls);
163                 data = dbls;
164                 break;
165             }
166             case L'r':
167             {
168                 double* dbls = NULL;
169                 int size = scilab_getSize(env, in[pos]);
170                 scilab_getDoubleArray(env, in[pos], &dbls);
171                 float* f = (float*)malloc(size * sizeof(float));
172                 for (int i = 0; i < size; ++i)
173                 {
174                     f[i] = (float)dbls[i];
175                 }
176
177                 data = f;
178                 alloc = true;
179                 break;
180             }
181             case L'i':
182             {
183                 double* dbls = NULL;
184                 int size = scilab_getSize(env, in[pos]);
185                 scilab_getDoubleArray(env, in[pos], &dbls);
186                 int* ints = (int*)malloc(size * sizeof(int));
187                 for (int i = 0; i < size; ++i)
188                 {
189                     ints[i] = (int)dbls[i];
190                 }
191
192                 data = ints;
193                 alloc = true;
194                 break;
195             }
196             default:
197             {
198                 Scierror(77, _("%s: Wrong value for input argument #%d: '%s', '%s', '%s' or '%s' expected.\n"), fname, pos + 3, "d", "r", "i", "c");
199                 return 1;
200             }
201         }
202
203         scilab_getDim2d(env, in[pos], &row, &col);
204
205         Parameter& p = params[(int)param_pos - 1];
206         p.alloc = alloc;
207         p.data = data;
208         p.row = row;
209         p.col = col;
210         p.type = param_type[0];
211
212         pos += 3;
213     }
214
215     int output_pos = 0;
216     //outputs
217     if (hasOutputs)
218     {
219         ++pos; //avoid "out"
220         while (1)
221         {
222             //check if is 3 or 1 arg ...
223             if (scilab_isDouble(env, in[pos]) == 0)
224             {
225                 Scierror(77, _("%s: Wrong type for input argument #%d: A real matrix expected.\n"), fname, pos + 1);
226                 return 1;
227             }
228
229             if (scilab_isScalar(env, in[pos]))
230             {
231                 double dorder = 0;
232                 scilab_getDouble(env, in[pos], &dorder);
233                 int order = (int)dorder;
234                 if (params[order - 1].data == nullptr)
235                 {
236                     Scierror(77, _("%s: Wrong value for input argument #%d.\n"), fname, pos + 1);
237                     return 1;
238                 }
239
240                 //need to clone double input data to avoid modification
241                 if (params[order - 1].type == L'd')
242                 {
243                     int size = params[order - 1].row * params[order - 1].col * sizeof(double);
244                     double* dbls = (double*)malloc(size);
245                     memcpy(dbls, params[order - 1].data, size);
246                     params[order - 1].data = dbls;
247                     params[order - 1].alloc = true;
248                 }
249
250                 pos += 1;
251                 output_order[output_pos] = order - 1;
252             }
253             else
254             {
255                 //dims
256                 double* dims = 0;
257                 scilab_getDoubleArray(env, in[pos], &dims);
258                 int size = (int)dims[0] * (int)dims[1];
259
260                 //pos
261                 if (scilab_isDouble(env, in[pos + 1]) == 0 || scilab_isScalar(env, in[pos + 1]) == 0)
262                 {
263                     Scierror(77, _("%s: Wrong type for input argument #%d : A real scalar expected.\n"), fname, pos + 2);
264                     return 1;
265                 }
266
267                 double param_pos = 0;
268                 scilab_getDouble(env, in[pos + 1], &param_pos);
269
270                 //type
271                 if (scilab_isString(env, in[pos + 2]) == 0 || scilab_isScalar(env, in[pos + 2]) == 0)
272                 {
273                     Scierror(77, _("%s: Wrong type for input argument #%d : string expected.\n"), fname, pos + 3);
274                     return 1;
275                 }
276
277                 wchar_t* param_type = NULL;
278                 scilab_getString(env, in[pos + 2], &param_type);
279
280                 void* data = NULL;
281                 Parameter& p = params[(int)param_pos - 1];
282
283                 if (p.data != nullptr) // Reuse the input slot if it is passed as output
284                 {
285                     if (p.type != param_type[0])
286                     {
287                         Scierror(999, _("%s: incompatible type between input and output variables.\n"), fname);
288                         return 1;
289                     }
290
291                     if (p.type == 'c')
292                     {
293                         
294                         if (strlen((char*)p.data) != dims[0] * dims[1])
295                         {
296                             Scierror(999, _("%s: incompatible sizes between input and output variables.\n"), fname);
297                             return 1;
298                         }
299                     }
300                     else
301                     {
302                         if (p.row != dims[0] || p.col != dims[1])
303                         {
304                             Scierror(999, _("%s: incompatible sizes between input and output variables.\n"), fname);
305                             return 1;
306                         }
307                     }
308
309                     //need to clone double input data to avoid modification
310                     if (p.type == L'd')
311                     {
312                         int size = p.row * p.col * sizeof(double);
313                         double* dbls = (double*)malloc(size);
314                         memcpy(dbls, p.data, size);
315                         p.data = dbls;
316                         p.alloc = true;
317                     }
318                 }
319                 else // Otherwise allocate one
320                 {
321                     switch (param_type[0])
322                     {
323                         case L'c':
324                         {
325                             data = malloc((size + 1) * sizeof(char));
326                             break;
327                         }
328                         case L'd':
329                         {
330                             data = malloc(size * sizeof(double));
331                             break;
332                         }
333                         case L'r':
334                         {
335                             data = malloc(size * sizeof(float));
336                             break;
337                         }
338                         case L'i':
339                         {
340                             data = malloc(size * sizeof(int));
341                             break;
342                         }
343                     }
344                     p.row = (int)dims[0];
345                     p.col = (int)dims[1];
346                     p.alloc = true;
347                     p.type = param_type[0];
348                     p.data = data;
349                 }
350                 pos += 3;
351                 output_order[output_pos] = (int)param_pos - 1;
352             }
353
354             ++output_pos;
355
356             if (pos + 1 > nin)
357             {
358                 break;
359             }
360         }
361
362     }
363     //the unbelievable call !
364     ((fct)func->functionPtr)(params[0].data, params[1].data, params[2].data, params[3].data, params[4].data, params[5].data, params[6].data, params[7].data, params[8].data, params[9].data,
365                              params[10].data, params[11].data, params[12].data, params[13].data, params[14].data, params[15].data, params[16].data, params[17].data, params[18].data, params[19].data,
366                              params[20].data, params[21].data, params[22].data, params[23].data, params[24].data, params[25].data, params[26].data, params[27].data, params[28].data, params[29].data);
367
368     //create output variables
369     for (int i = 0; i < nout && hasOutputs; ++i)
370     {
371         Parameter& p = params[output_order[i]];
372
373         switch (p.type)
374         {
375             case L'c':
376             {
377                 wchar_t* w = to_wide_string((char*)p.data);
378                 scilabVar var = scilab_createString(env, w);
379                 out[i] = var;
380                 FREE(w);
381                 break;
382             }
383             case L'd':
384             {
385                 scilabVar var = scilab_createDoubleMatrix2d(env, p.row, p.col, 0);
386                 scilab_setDoubleArray(env, var, (double*)p.data);
387                 out[i] = var;
388                 break;
389             }
390             case L'r':
391             {
392                 double* d = NULL;
393                 scilabVar var = scilab_createDoubleMatrix2d(env, p.row, p.col, 0);
394                 scilab_getDoubleArray(env, var, &d);
395                 int size = p.row * p.col;
396                 for (int j = 0; j < size; ++j)
397                 {
398                     d[j] = (double)((float*)p.data)[j];
399                 }
400
401                 out[i] = var;
402                 break;
403             }
404             case L'i':
405             {
406                 double* d = NULL;
407                 scilabVar var = scilab_createDoubleMatrix2d(env, p.row, p.col, 0);
408                 scilab_getDoubleArray(env, var, &d);
409                 int size = p.row * p.col;
410                 for (int j = 0; j < size; ++j)
411                 {
412                     d[j] = (double)((int*)p.data)[j];
413                 }
414
415                 out[i] = var;
416                 break;
417             }
418         }
419     }
420
421     //allocated data will be clean by structure dtor
422     return STATUS_OK;
423 }
424
425 bool isOut(scilabEnv env, scilabVar var)
426 {
427     if (scilab_isString(env, var) && scilab_isScalar(env, var))
428     {
429         wchar_t* strs = NULL;
430         scilab_getString(env, var, &strs);
431         if (wcscmp(strs, L"out") == 0 || wcscmp(strs, L"sort") == 0)
432         {
433             return true;
434         }
435     }
436
437     return false;
438 }