604eba13529701835900c7c581f1de8fdde9adb4
[scilab.git] / scilab / modules / xcos / src / java / org / scilab / modules / xcos / io / ScilabTypeCoder.java
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2015 - Scilab Enterprises - Clement DAVID
4  *
5  * This file must be used under the terms of the CeCILL.
6  * This source file is licensed as described in the file COPYING, which
7  * you should have received as part of this distribution.  The terms
8  * are also available at
9  * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt
10  *
11  */
12
13 package org.scilab.modules.xcos.io;
14
15 import java.io.UnsupportedEncodingException;
16 import java.nio.ByteBuffer;
17 import java.nio.DoubleBuffer;
18 import java.nio.IntBuffer;
19 import java.nio.LongBuffer;
20 import java.nio.charset.Charset;
21 import java.util.ArrayList;
22 import java.util.Arrays;
23 import java.util.Collections;
24 import org.scilab.modules.types.ScilabBoolean;
25 import org.scilab.modules.types.ScilabDouble;
26 import org.scilab.modules.types.ScilabInteger;
27 import org.scilab.modules.types.ScilabIntegerTypeEnum;
28 import org.scilab.modules.types.ScilabList;
29 import org.scilab.modules.types.ScilabMList;
30 import org.scilab.modules.types.ScilabString;
31 import org.scilab.modules.types.ScilabTList;
32 import org.scilab.modules.types.ScilabType;
33 import org.scilab.modules.types.ScilabTypeEnum;
34 import org.scilab.modules.xcos.VectorOfDouble;
35 import org.scilab.modules.xcos.VectorOfInt;
36 import org.scilab.modules.xcos.VectorOfScicosID;
37
38 import java.util.logging.Level;
39 import java.util.logging.Logger;
40
41 /**
42  * Encode and decode using a var2vec / vec2var compatible encoding.
43  *
44  * <p>
45  * This encoder is used to store arbitrary data to the model. Usually some properties are hard to map to both Java and C++ STL type system, using a shared encoding let us provide an implementation
46  * whatever the language is without sharing too low-level information.
47  */
48 public class ScilabTypeCoder {
49
50     private static final Logger LOG = Logger.getLogger("org.scilab.modules.xcos.io");
51
52     class JavaScilabType {
53         final ScilabTypeEnum type;
54         final ScilabIntegerTypeEnum intType;
55
56         public JavaScilabType(ScilabTypeEnum type, ScilabIntegerTypeEnum intType) {
57             this.type = type;
58             this.intType = intType;
59         }
60     }
61
62     /** current position in the vec buffer */
63     int position = 0;
64
65     public ScilabTypeCoder() {
66     }
67
68     /*
69      * var2vec implementation
70      */
71
72     /**
73      * Encode any scilab type to a buffer
74      *
75      * @param var
76      *            the Scilab value to encode
77      * @return the encoded buffer
78      */
79     public VectorOfDouble var2vec(ScilabType var) {
80         final ScilabType value;
81         if (var == null) {
82             value = new ScilabDouble();
83         } else {
84             value = var;
85         }
86
87         if (LOG.isLoggable(Level.FINER)) {
88             LOG.entering(ScilabTypeCoder.class.getCanonicalName(), "var2vec");
89         }
90
91         VectorOfDouble vec = new VectorOfDouble();
92         encode(value, vec);
93
94         if (LOG.isLoggable(Level.FINE)) {
95             LOG.fine("var2vec:" + var.toString() + ":" + toString(vec));
96         }
97
98         if (LOG.isLoggable(Level.FINER)) {
99             LOG.exiting(ScilabTypeCoder.class.getCanonicalName(), "var2vec");
100         }
101
102         return vec;
103     }
104
105     @SuppressWarnings("unchecked")
106     private VectorOfDouble encode(ScilabType var, VectorOfDouble vec) {
107         switch (var.getType()) {
108             case sci_matrix:
109                 encode((ScilabDouble) var, vec);
110                 break;
111             case sci_ints:
112                 encode((ScilabInteger) var, vec);
113                 break;
114             case sci_boolean:
115                 encode((ScilabBoolean) var, vec);
116                 break;
117             case sci_strings:
118                 encode((ScilabString) var, vec);
119                 break;
120             case sci_list:
121                 encode((ArrayList<ScilabType>) var, vec, var.getType());
122                 break;
123             case sci_mlist:
124                 encode((ArrayList<ScilabType>) var, vec, var.getType());
125                 break;
126             case sci_tlist:
127                 encode((ArrayList<ScilabType>) var, vec, var.getType());
128                 break;
129             default:
130                 break;
131         }
132
133         return vec;
134     }
135
136     /**
137      * Encode the double data
138      *
139      * @param var
140      *            the data to encode
141      * @param vec
142      *            the resulting buffer
143      */
144     private void encode(ScilabDouble var, VectorOfDouble vec) {
145         // Header
146         encodeHeader(var, vec, ScilabTypeEnum.sci_matrix);
147
148         // specific flag for managing the complex case
149         if (var.isReal()) {
150             vec.add(0f);
151         } else {
152             vec.add(1f);
153         }
154
155         // push the data
156         for (int i = 0; i < var.getHeight(); i++)
157             for (int j = 0; j < var.getWidth(); j++) {
158                 vec.add(var.getRealElement(i, j));
159             }
160
161         // push the complex data
162         if (!var.isReal())
163             for (int i = 0; i < var.getHeight(); i++)
164                 for (int j = 0; j < var.getWidth(); j++) {
165                     vec.add(var.getImaginaryElement(i, j));
166                 }
167     }
168
169     private void encode(ScilabInteger var, VectorOfDouble vec) {
170         // pre-processing: retrieve the raw data per type
171         int sizeof;
172         long[][] longData = null;
173         short[][] shortData = null;
174         int[][] intData = null;
175         byte[][] byteData = null;
176         switch (var.getPrec()) {
177             case sci_int64:
178             case sci_uint64:
179                 sizeof = Long.BYTES;
180                 longData = var.getDataAsLong();
181                 break;
182             case sci_int32:
183             case sci_uint32:
184                 sizeof = Integer.BYTES;
185                 intData = var.getDataAsInt();
186                 break;
187             case sci_int16:
188             case sci_uint16:
189                 sizeof = Short.BYTES;
190                 shortData = var.getDataAsShort();
191                 break;
192             case sci_int8:
193             case sci_uint8:
194                 sizeof = Byte.BYTES;
195                 byteData = var.getDataAsByte();
196                 break;
197             default:
198                 throw new IllegalArgumentException();
199         }
200
201         // Header
202         encodeHeader(var, vec, ScilabTypeEnum.sci_ints);
203
204         // push the data on a pre-allocated space
205         final int requiredBytes = sizeof * var.getHeight() * var.getWidth();
206         final int doubleLen = (requiredBytes + Double.BYTES - 1) / Double.BYTES;
207         final int position = vec.size();
208         vec.resize(position + doubleLen);
209         ByteBuffer view = vec.asByteBuffer(position, doubleLen);
210
211         for (int i = 0; i < var.getHeight(); i++) {
212             for (int j = 0; j < var.getWidth(); j++) {
213                 switch (var.getPrec()) {
214                     case sci_int64:
215                     case sci_uint64:
216                         view.putLong(longData[i][j]);
217                         break;
218                     case sci_int32:
219                     case sci_uint32:
220                         view.putInt(intData[i][j]);
221                         break;
222                     case sci_int16:
223                     case sci_uint16:
224                         view.putShort(shortData[i][j]);
225                         break;
226                     case sci_int8:
227                     case sci_uint8:
228                         view.put(byteData[i][j]);
229                         break;
230                 }
231             }
232         }
233     }
234
235     private void encode(ScilabBoolean var, VectorOfDouble vec) {
236         // header
237         encodeHeader(var, vec, ScilabTypeEnum.sci_boolean);
238
239         // put all the boolean as int accordingly to Scilab 6 implementation
240         final int requiredBytes = Integer.BYTES * var.getHeight() * var.getWidth();
241         final int doubleLen = (requiredBytes + Double.BYTES - 1) / Double.BYTES;
242         int position = vec.size();
243         vec.resize(position + doubleLen);
244
245         ByteBuffer buffer = vec.asByteBuffer(position, doubleLen);
246
247         for (int i = 0; i < var.getHeight(); i++) {
248             for (int j = 0; j < var.getWidth(); j++) {
249                 buffer.putInt(var.getData()[i][j] ? 1 : 0);
250             }
251         }
252     }
253
254     private void encode(ScilabString var, VectorOfDouble vec) {
255         // header
256         encodeHeader(var, vec, ScilabTypeEnum.sci_strings);
257
258         // add the offset table which contains the offset of each UTF-8 encoded strings
259         int offsetTableStart = vec.size();
260         vec.resize(offsetTableStart + var.getHeight() * var.getWidth());
261
262         // encode the strings as UTF-8 and store the associated offset
263         for (int i = 0; i < var.getHeight(); i++) {
264             for (int j = 0; j < var.getWidth(); j++) {
265                 String str = var.getData()[i][j];
266                 byte[] bytes = str.getBytes(Charset.forName("UTF-8"));
267                 // append the terminal '\0'
268                 final int requiredBytes = ((bytes.length + 1) * Byte.BYTES);
269                 final int doubleLen = (requiredBytes + Double.BYTES - 1) / Double.BYTES;
270
271                 // set the offset
272                 vec.set(offsetTableStart++, doubleLen);
273
274                 // push the data through a temporary byte buffer
275                 int position = vec.size();
276                 vec.resize(position + doubleLen);
277                 vec.asByteBuffer(position, doubleLen).put(bytes);
278             }
279         }
280     }
281
282     private void encode(ArrayList<ScilabType> var, VectorOfDouble vec, ScilabTypeEnum as) {
283         // header
284         encodeHeader(var, vec, as);
285
286         // encode list content
287         for (ScilabType localVar : var) {
288             encode(localVar, vec);
289         }
290     }
291
292     /**
293      * Helper method to add an header of the detected type
294      *
295      * @param var
296      *            the scilab matrix type to encode
297      * @param vec
298      *            the raw encoded data container
299      * @param as
300      *            the type to encode
301      * @param detected
302      *            the detected type
303      */
304     @SuppressWarnings({ "unchecked", "fallthrough" })
305     private void encodeHeader(Object var, VectorOfDouble vec, final ScilabTypeEnum as) {
306         ScilabType matrix = null;
307         ArrayList<ScilabType> list = null;
308
309         // defensive programming
310         switch (as) {
311             case sci_boolean:
312             case sci_ints:
313             case sci_matrix:
314             case sci_strings:
315                 matrix = (ScilabType) var;
316                 break;
317             case sci_list:
318             case sci_mlist:
319             case sci_tlist:
320                 list = (ArrayList<ScilabType>) var;
321                 break;
322             default:
323                 throw new IllegalArgumentException();
324         }
325
326         vec.add(as.swigValue());
327         if (matrix instanceof ScilabInteger) {
328             vec.add(((ScilabInteger) matrix).getPrec().swigValue());
329         }
330         if (matrix != null) {
331             vec.add(2);
332             vec.add(matrix.getHeight());
333             vec.add(matrix.getWidth());
334         } else if (list != null) {
335             vec.add(list.size());
336         } else {
337             throw new IllegalArgumentException();
338         }
339     }
340
341     /*
342      * vec2var implementation
343      */
344
345     /**
346      * Decode a scilab type from a buffer
347      *
348      * @param vec
349      *            the buffer containing encoded scilab types
350      * @return the decoded scilab type
351      */
352     public ScilabType vec2var(VectorOfDouble vec) {
353         position = 0;
354
355         if (LOG.isLoggable(Level.FINER)) {
356             LOG.entering(ScilabTypeCoder.class.getName(), "vec2var");
357         }
358
359         ScilabType var = decodeHeader(vec);
360         decode(vec, var);
361
362         if (LOG.isLoggable(Level.FINE)) {
363             LOG.fine("vec2var:" + toString(vec) + ":" + var.toString());
364         }
365
366         if (LOG.isLoggable(Level.FINER)) {
367             LOG.exiting(ScilabTypeCoder.class.getName(), "vec2var");
368         }
369
370         return var;
371     }
372
373     @SuppressWarnings("unchecked")
374     private ScilabType decode(VectorOfDouble vec, ScilabType var) {
375         switch (var.getType()) {
376             case sci_matrix:
377                 decode(vec, (ScilabDouble) var);
378                 break;
379             case sci_ints:
380                 decode(vec, (ScilabInteger) var);
381                 break;
382             case sci_boolean:
383                 decode(vec, (ScilabBoolean) var);
384                 break;
385             case sci_strings:
386                 decode(vec, (ScilabString) var);
387                 break;
388             case sci_list:
389                 decode(vec, (ArrayList<ScilabType>) var);
390                 break;
391             case sci_mlist:
392                 decode(vec, (ArrayList<ScilabType>) var);
393                 break;
394             case sci_tlist:
395                 decode(vec, (ArrayList<ScilabType>) var);
396                 break;
397             default:
398                 break;
399         }
400         return var;
401     }
402
403     private ScilabType decode(VectorOfDouble vec, ScilabDouble var) {
404         double[][] realPart = var.getRealPart();
405         for (int i = 0; i < var.getHeight(); i++)
406             for (int j = 0; j < var.getWidth(); j++) {
407                 realPart[i][j] = vec.get(position++);
408             }
409
410         if (!var.isReal()) {
411             double[][] imaginaryPart = var.getImaginaryPart();
412
413             for (int i = 0; i < var.getHeight(); i++)
414                 for (int j = 0; j < var.getWidth(); j++) {
415                     imaginaryPart[i][j] = vec.get(position++);
416                 }
417         }
418         return var;
419     }
420
421     private ScilabType decode(VectorOfDouble vec, ScilabInteger var) {
422         final int sizeof;
423         long[][] longData = null;
424         short[][] shortData = null;
425         int[][] intData = null;
426         byte[][] byteData = null;
427
428         switch (var.getPrec()) {
429             case sci_int64:
430             case sci_uint64:
431                 sizeof = Long.BYTES;
432                 longData = var.getDataAsLong();
433                 break;
434             case sci_int32:
435             case sci_uint32:
436                 sizeof = Integer.BYTES;
437                 intData = var.getDataAsInt();
438                 break;
439             case sci_int16:
440             case sci_uint16:
441                 sizeof = Short.BYTES;
442                 shortData = var.getDataAsShort();
443                 break;
444             case sci_int8:
445             case sci_uint8:
446                 sizeof = Byte.BYTES;
447                 byteData = var.getDataAsByte();
448                 break;
449             default:
450                 throw new IllegalArgumentException();
451         }
452
453         final int doubleLen = (sizeof * var.getHeight() * var.getWidth()) / Double.BYTES + 1;
454         ByteBuffer view = vec.asByteBuffer(position, doubleLen);
455
456         for (int i = 0; i < var.getHeight(); i++) {
457             for (int j = 0; j < var.getWidth(); j++) {
458                 switch (var.getPrec()) {
459                     case sci_int64:
460                     case sci_uint64:
461                         longData[i][j] = view.getLong();
462                         break;
463                     case sci_int32:
464                     case sci_uint32:
465                         intData[i][j] = view.getInt();
466                         break;
467                     case sci_int16:
468                     case sci_uint16:
469                         shortData[i][j] = view.getShort();
470                         break;
471                     case sci_int8:
472                     case sci_uint8:
473                         byteData[i][j] = view.get();
474                         break;
475                 }
476             }
477         }
478         position += doubleLen;
479
480         return var;
481     }
482
483     private ScilabType decode(VectorOfDouble vec, ScilabBoolean var) {
484         final boolean[][] data = var.getData();
485
486         final int doubleLen = (Integer.BYTES * var.getHeight() * var.getWidth()) / Double.BYTES + 1;
487         ByteBuffer view = vec.asByteBuffer(position, doubleLen);
488
489         for (int i = 0; i < var.getHeight(); i++) {
490             for (int j = 0; j < var.getWidth(); j++) {
491                 data[i][j] = view.getInt() != 0;
492             }
493         }
494         position += doubleLen;
495         return var;
496     }
497
498     private ScilabType decode(VectorOfDouble vec, ScilabString var) {
499         final String[][] data = var.getData();
500
501         // reconstruct the offset
502         int[][] offset = new int[var.getHeight()][var.getWidth()];
503         for (int i = 0; i < var.getHeight(); i++) {
504             for (int j = 0; j < var.getWidth(); j++) {
505                 offset[i][j] = (int) vec.get(position++);
506             }
507         }
508
509         // reconstruct each String object
510         try {
511             for (int i = 0; i < var.getHeight(); i++) {
512                 for (int j = 0; j < var.getWidth(); j++) {
513                     ByteBuffer view = vec.asByteBuffer(position, offset[i][j]);
514                     byte[] bytes = new byte[offset[i][j] * Double.BYTES];
515
516                     view.get(bytes);
517                     data[i][j] = new String(bytes, "UTF-8");
518
519                     position += offset[i][j];
520                 }
521             }
522         } catch (UnsupportedEncodingException e) {
523             // ignore as UTF-8 is always available
524         }
525         return var;
526     }
527
528     private ScilabType decode(VectorOfDouble vec, ArrayList<ScilabType> var) {
529         for (int i = 0; i < var.size(); i++) {
530             var.set(i, vec2var(vec));
531         }
532         return (ScilabType) var;
533     }
534
535     @SuppressWarnings("fallthrough")
536     private ScilabType decodeHeader(VectorOfDouble vec) {
537         int nativeScilabType = (int) vec.get(position++);
538
539         // specific integer sub-type
540         int precision = 0;
541
542         // for data[][]-based type
543         int height = 0;
544         int width = 0;
545
546         // for ArrayList-based type
547         int listLen = 0;
548
549         final ScilabTypeEnum type = ScilabTypeEnum.swigToEnum(nativeScilabType);
550         switch (type) {
551             case sci_ints:
552                 // special case for integer precision
553                 precision = (int) vec.get(position++);
554             case sci_matrix:
555             case sci_boolean:
556             case sci_strings:
557                 position++; // n-Dims not managed
558                 height = (int) vec.get(position++);
559                 width = (int) vec.get(position++);
560                 break;
561             case sci_list:
562             case sci_mlist:
563             case sci_tlist:
564                 listLen = (int) vec.get(position++);
565                 break;
566             default:
567                 throw new IllegalArgumentException();
568         }
569
570         // special case for complex double matrix
571         double[][] imagData = null;
572         if (type == ScilabTypeEnum.sci_matrix) {
573             boolean isComplex = vec.get(position++) != 0;
574
575             if (isComplex) {
576                 imagData = new double[height][width];
577             }
578         }
579
580         // allocate the right type with the decoded properties
581         switch (type) {
582             case sci_matrix:
583                 if (height * width == 0) {
584                     return new ScilabDouble();
585                 } else {
586                     return new ScilabDouble(new double[height][width], imagData);
587                 }
588             case sci_boolean:
589                 if (height * width == 0) {
590                     return new ScilabBoolean();
591                 } else {
592                     return new ScilabBoolean(new boolean[height][width]);
593                 }
594             case sci_ints:
595                 if (height * width == 0) {
596                     return new ScilabInteger();
597                 } else
598                     switch (ScilabIntegerTypeEnum.swigToEnum(precision)) {
599                         case sci_int8:
600                             return new ScilabInteger(new byte[height][width], false);
601                         case sci_int16:
602                             return new ScilabInteger(new short[height][width], false);
603                         case sci_int32:
604                             return new ScilabInteger(new int[height][width], false);
605                         case sci_int64:
606                             return new ScilabInteger(new long[height][width], false);
607                         case sci_uint8:
608                             return new ScilabInteger(new byte[height][width], true);
609                         case sci_uint16:
610                             return new ScilabInteger(new short[height][width], true);
611                         case sci_uint32:
612                             return new ScilabInteger(new int[height][width], true);
613                         case sci_uint64:
614                             return new ScilabInteger(new long[height][width], true);
615
616                     }
617             case sci_strings:
618                 if (height * width == 0) {
619                     return new ScilabString();
620                 } else {
621                     return new ScilabString(new String[height][width]);
622                 }
623             case sci_list:
624                 return new ScilabList(Collections.nCopies(listLen, null));
625             case sci_mlist:
626                 return new ScilabMList(new String[listLen], Collections.nCopies(listLen, null));
627             case sci_tlist:
628                 return new ScilabTList(new String[listLen], Collections.nCopies(listLen, null));
629             default:
630                 throw new IllegalArgumentException();
631
632         }
633     }
634
635     /**
636      * Utility to display a vec on debug
637      *
638      * @param vec the vector to convert
639      * @return a representative string
640      */
641     private static String toString(VectorOfDouble vec) {
642         int len = vec.size();
643         double[] copy = new double[len];
644         DoubleBuffer.wrap(copy).put(vec.asByteBuffer(0, len).asDoubleBuffer());
645         return Arrays.toString(copy);
646     }
647
648     /**
649      * Utility to display a vec on debug
650      *
651      * @param vec the vector to convert
652      * @return a representative string
653      */
654     private static String toString(VectorOfScicosID vec) {
655         int len = vec.size();
656         long[] copy = new long[len];
657         LongBuffer.wrap(copy).put(vec.asByteBuffer(0, len).asLongBuffer());
658         return Arrays.toString(copy);
659     }
660
661     /**
662      * Utility to display a vec on debug
663      *
664      * @param vec the vector to convert
665      * @return a representative string
666      */
667     private static String toString(VectorOfInt vec) {
668         int len = vec.size();
669         int[] copy = new int[len];
670         IntBuffer.wrap(copy).put(vec.asByteBuffer(0, len).asIntBuffer());
671         return Arrays.toString(copy);
672     }
673 }