39363f3069c7510fe9e25f9fab2c9d6e1267829a
[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.nio.ByteBuffer;
16 import java.nio.DoubleBuffer;
17 import java.nio.IntBuffer;
18 import java.nio.LongBuffer;
19 import java.nio.charset.Charset;
20 import java.util.ArrayList;
21 import java.util.Arrays;
22 import java.util.Collections;
23 import org.scilab.modules.types.ScilabBoolean;
24 import org.scilab.modules.types.ScilabDouble;
25 import org.scilab.modules.types.ScilabInteger;
26 import org.scilab.modules.types.ScilabIntegerTypeEnum;
27 import org.scilab.modules.types.ScilabList;
28 import org.scilab.modules.types.ScilabMList;
29 import org.scilab.modules.types.ScilabString;
30 import org.scilab.modules.types.ScilabTList;
31 import org.scilab.modules.types.ScilabType;
32 import org.scilab.modules.types.ScilabTypeEnum;
33 import org.scilab.modules.xcos.VectorOfDouble;
34 import org.scilab.modules.xcos.VectorOfInt;
35 import org.scilab.modules.xcos.VectorOfScicosID;
36
37 import java.util.logging.Level;
38 import java.util.logging.Logger;
39
40 /**
41  * Encode and decode using a var2vec / vec2var compatible encoding.
42  *
43  * <p>
44  * 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
45  * whatever the language is without sharing too low-level information.
46  */
47 public class ScilabTypeCoder {
48
49     private static final Logger LOG = Logger.getLogger("org.scilab.modules.xcos.io");
50
51     class JavaScilabType {
52         final ScilabTypeEnum type;
53         final ScilabIntegerTypeEnum intType;
54
55         public JavaScilabType(ScilabTypeEnum type, ScilabIntegerTypeEnum intType) {
56             this.type = type;
57             this.intType = intType;
58         }
59     }
60
61     /** current position in the vec buffer */
62     int position = 0;
63
64     public ScilabTypeCoder() {
65     }
66
67     /*
68      * var2vec implementation
69      */
70
71     /**
72      * Encode any scilab type to a buffer
73      *
74      * @param var
75      *            the Scilab value to encode
76      * @return the encoded buffer
77      */
78     public VectorOfDouble var2vec(ScilabType var) {
79         final ScilabType value;
80         if (var == null) {
81             value = new ScilabDouble();
82         } else {
83             value = var;
84         }
85
86         if (LOG.isLoggable(Level.FINER)) {
87             LOG.entering(ScilabTypeCoder.class.getCanonicalName(), "var2vec");
88         }
89
90         VectorOfDouble vec = new VectorOfDouble();
91         encode(value, vec);
92
93         // System.err.println("var2vec:" + var.toString() + ":" + toString(vec));
94         if (LOG.isLoggable(Level.FINE)) {
95             LOG.log(Level.FINE, "var2vec:{0}:{1}", new Object[] {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 index = vec.size();
208         vec.resize(index + doubleLen);
209         ByteBuffer view = vec.asByteBuffer(index, 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 index = vec.size();
243         vec.resize(index + doubleLen);
244
245         ByteBuffer buffer = vec.asByteBuffer(index, 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         int offsetTableAccumulated = 0;
262
263         // encode the strings as UTF-8 and store the associated offset
264         Charset utf8 = Charset.forName("UTF-8");
265         for (int i = 0; i < var.getHeight(); i++) {
266             for (int j = 0; j < var.getWidth(); j++) {
267                 String str = var.getData()[i][j];
268                 byte[] bytes = str.getBytes(utf8);
269                 // append the terminal '\0'
270                 final int requiredBytes = bytes.length + 1;
271                 final int doubleLen = (requiredBytes + Double.BYTES - 1) / Double.BYTES;
272
273                 // set the offset
274
275                 offsetTableAccumulated += doubleLen;
276                 vec.set(offsetTableStart++, offsetTableAccumulated);
277
278
279                 // push the data through a temporary byte buffer
280                 int index = vec.size();
281                 vec.resize(index + doubleLen);
282                 vec.asByteBuffer(index, doubleLen).put(bytes).put((byte) 0);
283
284             }
285         }
286     }
287
288     private void encode(ArrayList<ScilabType> var, VectorOfDouble vec, ScilabTypeEnum as) {
289         // header
290         encodeHeader(var, vec, as);
291
292         // encode list content
293         for (ScilabType localVar : var) {
294             encode(localVar, vec);
295         }
296     }
297
298     /**
299      * Helper method to add an header of the detected type
300      *
301      * @param var
302      *            the scilab matrix type to encode
303      * @param vec
304      *            the raw encoded data container
305      * @param as
306      *            the type to encode
307      * @param detected
308      *            the detected type
309      */
310     @SuppressWarnings({ "unchecked", "fallthrough" })
311     private void encodeHeader(Object var, VectorOfDouble vec, final ScilabTypeEnum as) {
312         ScilabType matrix = null;
313         ArrayList<ScilabType> list = null;
314
315         // defensive programming
316         switch (as) {
317             case sci_boolean:
318             case sci_ints:
319             case sci_matrix:
320             case sci_strings:
321                 matrix = (ScilabType) var;
322                 break;
323             case sci_list:
324             case sci_mlist:
325             case sci_tlist:
326                 list = (ArrayList<ScilabType>) var;
327                 break;
328             default:
329                 throw new IllegalArgumentException();
330         }
331
332         vec.add(as.swigValue());
333         if (matrix instanceof ScilabInteger) {
334             vec.add(((ScilabInteger) matrix).getPrec().swigValue());
335         }
336         if (matrix != null) {
337             vec.add(2);
338             vec.add(matrix.getHeight());
339             vec.add(matrix.getWidth());
340         } else if (list != null) {
341             vec.add(list.size());
342         } else {
343             throw new IllegalArgumentException();
344         }
345     }
346
347     /*
348      * vec2var implementation
349      */
350
351     /**
352      * Decode a scilab type from a buffer
353      *
354      * @param vec
355      *            the buffer containing encoded scilab types
356      * @return the decoded scilab type
357      */
358     public ScilabType vec2var(VectorOfDouble vec) {
359         position = 0;
360
361         if (LOG.isLoggable(Level.FINER)) {
362             LOG.entering(ScilabTypeCoder.class.getName(), "vec2var", vec);
363         }
364
365         ScilabType var = decodeHeader(vec);
366         decode(vec, var);
367
368         // System.err.println("vec2var:" + toString(vec) + ":" + var.toString());
369         if (LOG.isLoggable(Level.FINE)) {
370             LOG.log(Level.FINE, "vec2var:{0}:{1}", new Object[] {toString(vec), var.toString()});
371         }
372
373         if (LOG.isLoggable(Level.FINER)) {
374             LOG.exiting(ScilabTypeCoder.class.getName(), "vec2var");
375         }
376
377         return var;
378     }
379
380     @SuppressWarnings("unchecked")
381     private ScilabType decode(VectorOfDouble vec, ScilabType var) {
382         switch (var.getType()) {
383             case sci_matrix:
384                 decode(vec, (ScilabDouble) var);
385                 break;
386             case sci_ints:
387                 decode(vec, (ScilabInteger) var);
388                 break;
389             case sci_boolean:
390                 decode(vec, (ScilabBoolean) var);
391                 break;
392             case sci_strings:
393                 decode(vec, (ScilabString) var);
394                 break;
395             case sci_list:
396                 decode(vec, (ArrayList<ScilabType>) var);
397                 break;
398             case sci_mlist:
399                 decode(vec, (ArrayList<ScilabType>) var);
400                 break;
401             case sci_tlist:
402                 decode(vec, (ArrayList<ScilabType>) var);
403                 break;
404             default:
405                 break;
406         }
407         return var;
408     }
409
410     private ScilabType decode(VectorOfDouble vec, ScilabDouble var) {
411         double[][] realPart = var.getRealPart();
412         for (int i = 0; i < var.getHeight(); i++)
413             for (int j = 0; j < var.getWidth(); j++) {
414                 realPart[i][j] = vec.get(position++);
415             }
416
417         if (!var.isReal()) {
418             double[][] imaginaryPart = var.getImaginaryPart();
419
420             for (int i = 0; i < var.getHeight(); i++)
421                 for (int j = 0; j < var.getWidth(); j++) {
422                     imaginaryPart[i][j] = vec.get(position++);
423                 }
424         }
425         return var;
426     }
427
428     private ScilabType decode(VectorOfDouble vec, ScilabInteger var) {
429         final int sizeof;
430         long[][] longData = null;
431         short[][] shortData = null;
432         int[][] intData = null;
433         byte[][] byteData = null;
434
435         switch (var.getPrec()) {
436             case sci_int64:
437             case sci_uint64:
438                 sizeof = Long.BYTES;
439                 longData = var.getDataAsLong();
440                 break;
441             case sci_int32:
442             case sci_uint32:
443                 sizeof = Integer.BYTES;
444                 intData = var.getDataAsInt();
445                 break;
446             case sci_int16:
447             case sci_uint16:
448                 sizeof = Short.BYTES;
449                 shortData = var.getDataAsShort();
450                 break;
451             case sci_int8:
452             case sci_uint8:
453                 sizeof = Byte.BYTES;
454                 byteData = var.getDataAsByte();
455                 break;
456             default:
457                 throw new IllegalArgumentException();
458         }
459
460         final int doubleLen = (sizeof * var.getHeight() * var.getWidth()) / Double.BYTES + 1;
461         ByteBuffer view = vec.asByteBuffer(position, doubleLen);
462
463         for (int i = 0; i < var.getHeight(); i++) {
464             for (int j = 0; j < var.getWidth(); j++) {
465                 switch (var.getPrec()) {
466                     case sci_int64:
467                     case sci_uint64:
468                         longData[i][j] = view.getLong();
469                         break;
470                     case sci_int32:
471                     case sci_uint32:
472                         intData[i][j] = view.getInt();
473                         break;
474                     case sci_int16:
475                     case sci_uint16:
476                         shortData[i][j] = view.getShort();
477                         break;
478                     case sci_int8:
479                     case sci_uint8:
480                         byteData[i][j] = view.get();
481                         break;
482                 }
483             }
484         }
485         position += doubleLen;
486
487         return var;
488     }
489
490     private ScilabType decode(VectorOfDouble vec, ScilabBoolean var) {
491         final boolean[][] data = var.getData();
492
493         final int doubleLen = (Integer.BYTES * var.getHeight() * var.getWidth()) / Double.BYTES + 1;
494         ByteBuffer view = vec.asByteBuffer(position, doubleLen);
495
496         for (int i = 0; i < var.getHeight(); i++) {
497             for (int j = 0; j < var.getWidth(); j++) {
498                 data[i][j] = view.getInt() != 0;
499             }
500         }
501         position += doubleLen;
502         return var;
503     }
504
505     private ScilabType decode(VectorOfDouble vec, ScilabString var) {
506         final String[][] data = var.getData();
507
508         // reconstruct the offset
509         int[][] offset = new int[var.getHeight()][var.getWidth()];
510         for (int i = 0; i < var.getHeight(); i++) {
511             for (int j = 0; j < var.getWidth(); j++) {
512                 offset[i][j] = (int) vec.get(position++);
513             }
514         }
515
516         Charset utf8 = Charset.forName("UTF-8");
517         int accumulatedOffset = 0;
518
519         // reconstruct each String object
520         for (int i = 0; i < var.getHeight(); i++) {
521             for (int j = 0; j < var.getWidth(); j++) {
522                 final int nbOfDouble = offset[i][j] - accumulatedOffset;
523                 ByteBuffer view = vec.asByteBuffer(position, nbOfDouble);
524                 byte[] bytes = new byte[nbOfDouble * Double.BYTES];
525
526                 view.get(bytes);
527
528                 // to avoid mis-decoding we have to pass the len of bytes
529                 int strlen = 0;
530                 while (bytes[strlen] != 0) {
531                     strlen++;
532                 }
533
534                 data[i][j] = new String(bytes, 0, strlen, utf8);
535
536                 accumulatedOffset += nbOfDouble;
537                 position += nbOfDouble;
538             }
539         }
540
541         return var;
542     }
543
544     private ScilabType decode(VectorOfDouble vec, ArrayList<ScilabType> var) {
545         for (int i = 0; i < var.size(); i++) {
546             ScilabType sub = decodeHeader(vec);
547             decode(vec, sub);
548
549             var.set(i, sub);
550         }
551         return (ScilabType) var;
552     }
553
554     @SuppressWarnings("fallthrough")
555     private ScilabType decodeHeader(VectorOfDouble vec) {
556         int nativeScilabType = (int) vec.get(position++);
557
558         // specific integer sub-type
559         int precision = 0;
560
561         // for data[][]-based type
562         int height = 0;
563         int width = 0;
564
565         // for ArrayList-based type
566         int listLen = 0;
567
568         final ScilabTypeEnum type = ScilabTypeEnum.swigToEnum(nativeScilabType);
569         switch (type) {
570             case sci_ints:
571                 // special case for integer precision
572                 precision = (int) vec.get(position++);
573             case sci_matrix:
574             case sci_boolean:
575             case sci_strings:
576                 position++; // n-Dims not managed
577                 height = (int) vec.get(position++);
578                 width = (int) vec.get(position++);
579                 break;
580             case sci_list:
581             case sci_mlist:
582             case sci_tlist:
583                 listLen = (int) vec.get(position++);
584                 break;
585             default:
586                 throw new IllegalArgumentException();
587         }
588
589         // special case for complex double matrix
590         double[][] imagData = null;
591         if (type == ScilabTypeEnum.sci_matrix) {
592             boolean isComplex = vec.get(position++) != 0;
593
594             if (isComplex) {
595                 imagData = new double[height][width];
596             }
597         }
598
599         // allocate the right type with the decoded properties
600         switch (type) {
601             case sci_matrix:
602                 if (height * width == 0) {
603                     return new ScilabDouble();
604                 } else {
605                     return new ScilabDouble(new double[height][width], imagData);
606                 }
607             case sci_boolean:
608                 if (height * width == 0) {
609                     return new ScilabBoolean();
610                 } else {
611                     return new ScilabBoolean(new boolean[height][width]);
612                 }
613             case sci_ints:
614                 if (height * width == 0) {
615                     return new ScilabInteger();
616                 } else
617                     switch (ScilabIntegerTypeEnum.swigToEnum(precision)) {
618                         case sci_int8:
619                             return new ScilabInteger(new byte[height][width], false);
620                         case sci_int16:
621                             return new ScilabInteger(new short[height][width], false);
622                         case sci_int32:
623                             return new ScilabInteger(new int[height][width], false);
624                         case sci_int64:
625                             return new ScilabInteger(new long[height][width], false);
626                         case sci_uint8:
627                             return new ScilabInteger(new byte[height][width], true);
628                         case sci_uint16:
629                             return new ScilabInteger(new short[height][width], true);
630                         case sci_uint32:
631                             return new ScilabInteger(new int[height][width], true);
632                         case sci_uint64:
633                             return new ScilabInteger(new long[height][width], true);
634
635                     }
636             case sci_strings:
637                 if (height * width == 0) {
638                     return new ScilabString();
639                 } else {
640                     return new ScilabString(new String[height][width]);
641                 }
642             case sci_list:
643                 return new ScilabList(Collections.nCopies(listLen, null));
644             case sci_mlist:
645                 return new ScilabMList(new String[listLen - 1], Collections.nCopies(listLen - 1, null));
646             case sci_tlist:
647                 return new ScilabTList(new String[listLen - 1], Collections.nCopies(listLen - 1, null));
648             default:
649                 throw new IllegalArgumentException();
650
651         }
652     }
653
654     /**
655      * Utility to display a vec on debug
656      *
657      * @param vec the vector to convert
658      * @return a representative string
659      */
660     private static String toString(VectorOfDouble vec) {
661         int len = vec.size();
662         double[] copy = new double[len];
663         DoubleBuffer.wrap(copy).put(vec.asByteBuffer(0, len).asDoubleBuffer());
664         return Arrays.toString(copy);
665     }
666
667     /**
668      * Utility to display a vec on debug
669      *
670      * @param vec the vector to convert
671      * @return a representative string
672      */
673     private static String toString(VectorOfScicosID vec) {
674         int len = vec.size();
675         long[] copy = new long[len];
676         LongBuffer.wrap(copy).put(vec.asByteBuffer(0, len).asLongBuffer());
677         return Arrays.toString(copy);
678     }
679
680     /**
681      * Utility to display a vec on debug
682      *
683      * @param vec the vector to convert
684      * @return a representative string
685      */
686     private static String toString(VectorOfInt vec) {
687         int len = vec.size();
688         int[] copy = new int[len];
689         IntBuffer.wrap(copy).put(vec.asByteBuffer(0, len).asIntBuffer());
690         return Arrays.toString(copy);
691     }
692 }