2 * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3 * Copyright (C) 2015 - Scilab Enterprises - Clement DAVID
5 * Copyright (C) 2012 - 2016 - Scilab Enterprises
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.
15 package org.scilab.modules.xcos.io;
17 import java.nio.ByteBuffer;
18 import java.nio.DoubleBuffer;
19 import java.nio.IntBuffer;
20 import java.nio.LongBuffer;
21 import java.nio.charset.Charset;
22 import java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.Collections;
25 import java.util.IllegalFormatException;
26 import org.scilab.modules.types.ScilabBoolean;
27 import org.scilab.modules.types.ScilabDouble;
28 import org.scilab.modules.types.ScilabInteger;
29 import org.scilab.modules.types.ScilabIntegerTypeEnum;
30 import org.scilab.modules.types.ScilabList;
31 import org.scilab.modules.types.ScilabMList;
32 import org.scilab.modules.types.ScilabString;
33 import org.scilab.modules.types.ScilabTList;
34 import org.scilab.modules.types.ScilabType;
35 import org.scilab.modules.types.ScilabTypeEnum;
36 import org.scilab.modules.xcos.VectorOfDouble;
37 import org.scilab.modules.xcos.VectorOfInt;
38 import org.scilab.modules.xcos.VectorOfScicosID;
40 import java.util.logging.Level;
41 import java.util.logging.Logger;
44 * Encode and decode using a var2vec / vec2var compatible encoding.
47 * This encoder is used to store arbitrary data to the model. Usually some
48 * properties are hard to map to both Java and C++ STL type system, using a
49 * shared encoding let us provide an implementation whatever the language is
50 * without sharing too low-level information.
52 public class ScilabTypeCoder {
54 private static final Logger LOG = Logger.getLogger("org.scilab.modules.xcos.io");
56 class JavaScilabType {
58 final ScilabTypeEnum type;
59 final ScilabIntegerTypeEnum intType;
61 public JavaScilabType(ScilabTypeEnum type, ScilabIntegerTypeEnum intType) {
63 this.intType = intType;
68 * current position in the vec buffer
72 public ScilabTypeCoder() {
76 * var2vec implementation
79 * Encode any scilab type to a buffer
81 * @param var the Scilab value to encode
82 * @return the encoded buffer
84 public VectorOfDouble var2vec(ScilabType var) {
85 final ScilabType value;
87 value = new ScilabDouble();
92 if (LOG.isLoggable(Level.FINER)) {
93 LOG.entering(ScilabTypeCoder.class.getCanonicalName(), "var2vec");
96 VectorOfDouble vec = new VectorOfDouble();
99 // System.err.println("var2vec:" + var.toString() + ":" + toString(vec));
100 if (LOG.isLoggable(Level.FINE)) {
101 LOG.log(Level.FINE, "var2vec:{0}:{1}", new Object[] {var.toString(), toString(vec)});
104 if (LOG.isLoggable(Level.FINER)) {
105 LOG.exiting(ScilabTypeCoder.class.getCanonicalName(), "var2vec");
111 @SuppressWarnings("unchecked")
112 private VectorOfDouble encode(ScilabType var, VectorOfDouble vec) {
113 switch (var.getType()) {
115 encode((ScilabDouble) var, vec);
118 encode((ScilabInteger) var, vec);
121 encode((ScilabBoolean) var, vec);
124 encode((ScilabString) var, vec);
127 encode((ArrayList<ScilabType>) var, vec, var.getType());
130 encode((ArrayList<ScilabType>) var, vec, var.getType());
133 encode((ArrayList<ScilabType>) var, vec, var.getType());
143 * Encode the double data
145 * @param var the data to encode
146 * @param vec the resulting buffer
148 private void encode(ScilabDouble var, VectorOfDouble vec) {
150 encodeHeader(var, vec, ScilabTypeEnum.sci_matrix);
152 // specific flag for managing the complex case
160 for (int i = 0; i < var.getHeight(); i++) {
161 for (int j = 0; j < var.getWidth(); j++) {
162 vec.add(var.getRealElement(i, j));
166 // push the complex data
168 for (int i = 0; i < var.getHeight(); i++) {
169 for (int j = 0; j < var.getWidth(); j++) {
170 vec.add(var.getImaginaryElement(i, j));
176 private void encode(ScilabInteger var, VectorOfDouble vec) {
177 // pre-processing: retrieve the raw data per type
179 long[][] longData = null;
180 short[][] shortData = null;
181 int[][] intData = null;
182 byte[][] byteData = null;
183 switch (var.getPrec()) {
187 longData = var.getDataAsLong();
191 sizeof = Integer.BYTES;
192 intData = var.getDataAsInt();
196 sizeof = Short.BYTES;
197 shortData = var.getDataAsShort();
202 byteData = var.getDataAsByte();
205 throw new IllegalArgumentException();
209 encodeHeader(var, vec, ScilabTypeEnum.sci_ints);
211 // push the data on a pre-allocated space
212 final int requiredBytes = sizeof * var.getHeight() * var.getWidth();
213 final int doubleLen = (requiredBytes + Double.BYTES - 1) / Double.BYTES;
214 final int index = vec.size();
215 vec.resize(index + doubleLen);
216 ByteBuffer view = vec.asByteBuffer(index, doubleLen);
218 for (int i = 0; i < var.getHeight(); i++) {
219 for (int j = 0; j < var.getWidth(); j++) {
220 switch (var.getPrec()) {
223 view.putLong(longData[i][j]);
227 view.putInt(intData[i][j]);
231 view.putShort(shortData[i][j]);
235 view.put(byteData[i][j]);
242 private void encode(ScilabBoolean var, VectorOfDouble vec) {
244 encodeHeader(var, vec, ScilabTypeEnum.sci_boolean);
246 // put all the boolean as int accordingly to Scilab 6 implementation
247 final int requiredBytes = Integer.BYTES * var.getHeight() * var.getWidth();
248 final int doubleLen = (requiredBytes + Double.BYTES - 1) / Double.BYTES;
249 int index = vec.size();
250 vec.resize(index + doubleLen);
252 ByteBuffer buffer = vec.asByteBuffer(index, doubleLen);
254 for (int i = 0; i < var.getHeight(); i++) {
255 for (int j = 0; j < var.getWidth(); j++) {
256 buffer.putInt(var.getData()[i][j] ? 1 : 0);
261 private void encode(ScilabString var, VectorOfDouble vec) {
263 encodeHeader(var, vec, ScilabTypeEnum.sci_strings);
265 // add the offset table which contains the offset of each UTF-8 encoded strings
266 int offsetTableStart = vec.size();
267 vec.resize(offsetTableStart + var.getHeight() * var.getWidth());
268 int offsetTableAccumulated = 0;
270 // encode the strings as UTF-8 and store the associated offset
271 Charset utf8 = Charset.forName("UTF-8");
272 for (int i = 0; i < var.getHeight(); i++) {
273 for (int j = 0; j < var.getWidth(); j++) {
274 String str = var.getData()[i][j];
275 byte[] bytes = str.getBytes(utf8);
276 // append the terminal '\0'
277 final int requiredBytes = bytes.length + 1;
278 final int doubleLen = (requiredBytes + Double.BYTES - 1) / Double.BYTES;
281 offsetTableAccumulated += doubleLen;
282 vec.set(offsetTableStart++, offsetTableAccumulated);
284 // push the data through a temporary byte buffer
285 int index = vec.size();
286 vec.resize(index + doubleLen);
287 vec.asByteBuffer(index, doubleLen).put(bytes).put((byte) 0);
293 private void encode(ArrayList<ScilabType> var, VectorOfDouble vec, ScilabTypeEnum as) {
295 encodeHeader(var, vec, as);
297 // encode list content
298 for (ScilabType localVar : var) {
299 encode(localVar, vec);
304 * Helper method to add an header of the detected type
306 * @param var the scilab matrix type to encode
307 * @param vec the raw encoded data container
308 * @param as the type to encode
309 * @param detected the detected type
311 @SuppressWarnings({"unchecked", "fallthrough"})
312 private void encodeHeader(Object var, VectorOfDouble vec, final ScilabTypeEnum as) {
313 ScilabType matrix = null;
314 ArrayList<ScilabType> list = null;
316 // defensive programming
322 matrix = (ScilabType) var;
327 list = (ArrayList<ScilabType>) var;
330 throw new IllegalArgumentException();
333 vec.add(as.swigValue());
334 if (matrix instanceof ScilabInteger) {
335 vec.add(((ScilabInteger) matrix).getPrec().swigValue());
337 if (matrix != null) {
339 vec.add(matrix.getHeight());
340 vec.add(matrix.getWidth());
341 } else if (list != null) {
342 vec.add(list.size());
344 throw new IllegalArgumentException();
349 * vec2var implementation
352 * Decode a scilab type from a buffer
354 * @param vec the buffer containing encoded scilab types
355 * @return the decoded scilab type
357 public ScilabType vec2var(VectorOfDouble vec) {
360 if (LOG.isLoggable(Level.FINER)) {
361 LOG.entering(ScilabTypeCoder.class.getName(), "vec2var", vec);
364 ScilabType var = decodeHeader(vec);
367 // System.err.println("vec2var:" + toString(vec) + ":" + var.toString());
368 if (LOG.isLoggable(Level.FINE)) {
369 LOG.log(Level.FINE, "vec2var:{0}:{1}", new Object[] {toString(vec), var.toString()});
372 if (LOG.isLoggable(Level.FINER)) {
373 LOG.exiting(ScilabTypeCoder.class.getName(), "vec2var");
380 private static interface StoreFunction<R, C, V> {
381 public void apply(R r, C c, V v);
384 @SuppressWarnings("unchecked")
385 private ScilabType decode(VectorOfDouble vec, ScilabType var) {
386 switch (var.getType()) {
388 decode(vec, (ScilabDouble) var);
391 decode(vec, (ScilabInteger) var);
394 decode(vec, (ScilabBoolean) var);
397 decode(vec, (ScilabString) var);
400 decode(vec, (ArrayList<ScilabType>) var);
403 decode(vec, (ArrayList<ScilabType>) var);
406 decode(vec, (ArrayList<ScilabType>) var);
414 private ScilabType decode(VectorOfDouble vec, ScilabDouble var) {
415 double[][] realPart = var.getRealPart();
416 for (int i = 0; i < var.getHeight(); i++) {
417 for (int j = 0; j < var.getWidth(); j++) {
418 realPart[i][j] = vec.get(position++);
423 double[][] imaginaryPart = var.getImaginaryPart();
425 for (int i = 0; i < var.getHeight(); i++) {
426 for (int j = 0; j < var.getWidth(); j++) {
427 imaginaryPart[i][j] = vec.get(position++);
434 private ScilabType decode(VectorOfDouble vec, ScilabInteger var) {
436 long[][] longData = null;
437 short[][] shortData = null;
438 int[][] intData = null;
439 byte[][] byteData = null;
441 switch (var.getPrec()) {
445 longData = var.getDataAsLong();
449 sizeof = Integer.BYTES;
450 intData = var.getDataAsInt();
454 sizeof = Short.BYTES;
455 shortData = var.getDataAsShort();
460 byteData = var.getDataAsByte();
463 throw new IllegalArgumentException();
466 final int doubleLen = (sizeof * var.getHeight() * var.getWidth()) / Double.BYTES + 1;
467 ByteBuffer view = vec.asByteBuffer(position, doubleLen);
469 for (int i = 0; i < var.getHeight(); i++) {
470 for (int j = 0; j < var.getWidth(); j++) {
471 switch (var.getPrec()) {
474 longData[i][j] = view.getLong();
478 intData[i][j] = view.getInt();
482 shortData[i][j] = view.getShort();
486 byteData[i][j] = view.get();
491 position += doubleLen;
496 private ScilabType decode(VectorOfDouble vec, ScilabBoolean var) {
497 final boolean[][] data = var.getData();
499 final int doubleLen = (Integer.BYTES * var.getHeight() * var.getWidth()) / Double.BYTES + 1;
500 ByteBuffer view = vec.asByteBuffer(position, doubleLen);
502 for (int i = 0; i < var.getHeight(); i++) {
503 for (int j = 0; j < var.getWidth(); j++) {
504 data[i][j] = view.getInt() != 0;
507 position += doubleLen;
511 private ScilabType decode(VectorOfDouble vec, ScilabString var) {
512 final String[][] data = var.getData();
513 decodeString(var.getHeight(), var.getWidth(), vec, (i, j, str) -> data[i][j] = str);
517 private void decodeString(int height, int width, VectorOfDouble vec, StoreFunction<Integer, Integer, String> store) {
518 // reconstruct the offset
519 int[][] offset = new int[height][width];
520 for (int i = 0; i < height; i++) {
521 for (int j = 0; j < width; j++) {
522 offset[i][j] = (int) vec.get(position++);
526 Charset utf8 = Charset.forName("UTF-8");
527 int accumulatedOffset = 0;
528 // reconstruct each String object
529 for (int i = 0; i < height; i++) {
530 for (int j = 0; j < width; j++) {
531 final int nbOfDouble = offset[i][j] - accumulatedOffset;
532 ByteBuffer view = vec.asByteBuffer(position, nbOfDouble);
533 byte[] bytes = new byte[nbOfDouble * Double.BYTES];
537 // to avoid mis-decoding we have to pass the len of bytes
539 while (bytes[strlen] != 0) {
543 store.apply(i, j, new String(bytes, 0, strlen, utf8));
545 accumulatedOffset += nbOfDouble;
546 position += nbOfDouble;
551 private ScilabType decode(VectorOfDouble vec, ArrayList<ScilabType> var) {
552 for (int i = 0; i < var.size(); i++) {
553 ScilabType sub = decodeHeader(vec);
558 return (ScilabType) var;
561 @SuppressWarnings("fallthrough")
562 private ScilabType decodeHeader(VectorOfDouble vec) {
563 int nativeScilabType = (int) vec.get(position++);
565 // specific integer sub-type
568 // for data[][]-based type
572 // for ArrayList-based type
575 final ScilabTypeEnum type = ScilabTypeEnum.swigToEnum(nativeScilabType);
578 // special case for integer precision
579 precision = (int) vec.get(position++);
583 position++; // n-Dims not managed
584 height = (int) vec.get(position++);
585 width = (int) vec.get(position++);
590 listLen = (int) vec.get(position++);
593 throw new IllegalArgumentException();
596 // special case for complex double matrix
597 double[][] imagData = null;
598 if (type == ScilabTypeEnum.sci_matrix) {
599 boolean isComplex = vec.get(position++) != 0;
602 imagData = new double[height][width];
606 // allocate the right type with the decoded properties
609 if (height * width == 0) {
610 return new ScilabDouble();
612 return new ScilabDouble(new double[height][width], imagData);
615 if (height * width == 0) {
616 return new ScilabBoolean();
618 return new ScilabBoolean(new boolean[height][width]);
621 if (height * width == 0) {
622 return new ScilabInteger();
624 switch (ScilabIntegerTypeEnum.swigToEnum(precision)) {
626 return new ScilabInteger(new byte[height][width], false);
628 return new ScilabInteger(new short[height][width], false);
630 return new ScilabInteger(new int[height][width], false);
632 return new ScilabInteger(new long[height][width], false);
634 return new ScilabInteger(new byte[height][width], true);
636 return new ScilabInteger(new short[height][width], true);
638 return new ScilabInteger(new int[height][width], true);
640 return new ScilabInteger(new long[height][width], true);
645 if (height * width == 0) {
646 return new ScilabString();
648 return new ScilabString(new String[height][width]);
651 return new ScilabList(Collections.nCopies(listLen, null));
653 return new ScilabMList(new String[listLen - 1], Collections.nCopies(listLen - 1, null));
655 return new ScilabTList(new String[listLen - 1], Collections.nCopies(listLen - 1, null));
657 throw new IllegalArgumentException();
663 * Format the encoded value accordingly to the Java format string
666 * {@link String#format(java.lang.String, java.lang.Object...)} compatible
668 * @param vec the buffer containing encoded scilab types
669 * @throws IllegalFormatException when the underlying formatting failed
670 * @throws IllegalArgumentException when the vec argument is wrongly encoded
671 * @return A formatted string
673 public String format(String format, VectorOfDouble vec) throws IllegalFormatException, IllegalArgumentException {
674 ArrayList<Object> arguments = new ArrayList<>();
675 decodeToJava(vec, arguments);
676 return String.format(format, arguments.toArray());
679 private void decodeToJava(VectorOfDouble vec, ArrayList<Object> arguments) {
680 int nativeScilabType = (int) vec.get(position++);
682 // specific integer sub-type
685 // for data[][]-based type
689 // for ArrayList-based type
692 final ScilabTypeEnum type = ScilabTypeEnum.swigToEnum(nativeScilabType);
695 // special case for integer precision
696 precision = (int) vec.get(position++);
700 position++; // n-Dims not managed
701 height = (int) vec.get(position++);
702 width = (int) vec.get(position++);
707 listLen = (int) vec.get(position++);
710 throw new IllegalArgumentException();
713 // special case for complex double matrix
714 boolean isComplex = false;
715 if (type == ScilabTypeEnum.sci_matrix) {
716 isComplex = vec.get(position++) != 0;
719 // allocate the right type with the decoded properties
722 for (int i = 0; i < height; i++) {
723 for (int j = 0; j < width; j++) {
724 arguments.add(vec.get(position++));
729 for (int i = 0; i < height; i++) {
730 for (int j = 0; j < width; j++) {
731 arguments.add(vec.get(position++));
737 final int doubleLen = (Integer.BYTES * height * width) / Double.BYTES + 1;
738 ByteBuffer view = vec.asByteBuffer(position, doubleLen);
739 for (int i = 0; i < height; i++) {
740 for (int j = 0; j < width; j++) {
741 arguments.add(view.getInt() != 0);
744 position += doubleLen;
748 switch (ScilabIntegerTypeEnum.swigToEnum(precision)) {
751 final int sizeof = Byte.BYTES;
752 final int doubleLen = (sizeof * height * width) / Double.BYTES + 1;
753 ByteBuffer view = vec.asByteBuffer(position, doubleLen);
754 for (int i = 0; i < height; i++) {
755 for (int j = 0; j < width; j++) {
756 arguments.add(view.get());
763 final int sizeof = Short.BYTES;
764 final int doubleLen = (sizeof * height * width) / Double.BYTES + 1;
765 ByteBuffer view = vec.asByteBuffer(position, doubleLen);
766 for (int i = 0; i < height; i++) {
767 for (int j = 0; j < width; j++) {
768 arguments.add(view.getShort());
775 final int sizeof = Integer.BYTES;
776 final int doubleLen = (sizeof * height * width) / Double.BYTES + 1;
777 ByteBuffer view = vec.asByteBuffer(position, doubleLen);
778 for (int i = 0; i < height; i++) {
779 for (int j = 0; j < width; j++) {
780 arguments.add(view.getInt());
787 final int sizeof = Long.BYTES;
788 final int doubleLen = (sizeof * height * width) / Double.BYTES + 1;
789 ByteBuffer view = vec.asByteBuffer(position, doubleLen);
790 for (int i = 0; i < height; i++) {
791 for (int j = 0; j < width; j++) {
792 arguments.add(view.getLong());
800 decodeString(height, width, vec, (i, j, v) -> arguments.add(v));
805 for (int i = 0; i < listLen; i++) {
806 decodeToJava(vec, arguments);
811 throw new IllegalArgumentException();
816 * Utility to display a vec on debug
818 * @param vec the vector to convert
819 * @return a representative string
821 public static String toString(VectorOfDouble vec) {
822 int len = vec.size();
823 double[] copy = new double[len];
824 DoubleBuffer.wrap(copy).put(vec.asByteBuffer(0, len).asDoubleBuffer());
825 return Arrays.toString(copy);
829 * Utility to display a vec on debug
831 * @param vec the vector to convert
832 * @return a representative string
834 public static String toString(VectorOfScicosID vec) {
835 int len = vec.size();
836 long[] copy = new long[len];
837 LongBuffer.wrap(copy).put(vec.asByteBuffer(0, len).asLongBuffer());
838 return Arrays.toString(copy);
842 * Utility to display a vec on debug
844 * @param vec the vector to convert
845 * @return a representative string
847 public static String toString(VectorOfInt vec) {
848 int len = vec.size();
849 int[] copy = new int[len];
850 IntBuffer.wrap(copy).put(vec.asByteBuffer(0, len).asIntBuffer());
851 return Arrays.toString(copy);