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 java.util.function.Consumer;
27 import org.scilab.modules.types.ScilabBoolean;
28 import org.scilab.modules.types.ScilabDouble;
29 import org.scilab.modules.types.ScilabInteger;
30 import org.scilab.modules.types.ScilabIntegerTypeEnum;
31 import org.scilab.modules.types.ScilabList;
32 import org.scilab.modules.types.ScilabMList;
33 import org.scilab.modules.types.ScilabString;
34 import org.scilab.modules.types.ScilabTList;
35 import org.scilab.modules.types.ScilabType;
36 import org.scilab.modules.types.ScilabTypeEnum;
37 import org.scilab.modules.xcos.VectorOfDouble;
38 import org.scilab.modules.xcos.VectorOfInt;
39 import org.scilab.modules.xcos.VectorOfScicosID;
41 import java.util.logging.Level;
42 import java.util.logging.Logger;
45 * Encode and decode using a var2vec / vec2var compatible encoding.
48 * This encoder is used to store arbitrary data to the model. Usually some
49 * properties are hard to map to both Java and C++ STL type system, using a
50 * shared encoding let us provide an implementation whatever the language is
51 * without sharing too low-level information.
53 public class ScilabTypeCoder {
55 private static final Logger LOG = Logger.getLogger("org.scilab.modules.xcos.io");
57 class JavaScilabType {
59 final ScilabTypeEnum type;
60 final ScilabIntegerTypeEnum intType;
62 public JavaScilabType(ScilabTypeEnum type, ScilabIntegerTypeEnum intType) {
64 this.intType = intType;
69 * current position in the vec buffer
73 public ScilabTypeCoder() {
77 * var2vec implementation
80 * Encode any scilab type to a buffer
82 * @param var the Scilab value to encode
83 * @return the encoded buffer
85 public VectorOfDouble var2vec(ScilabType var) {
86 final ScilabType value;
88 value = new ScilabDouble();
93 if (LOG.isLoggable(Level.FINER)) {
94 LOG.entering(ScilabTypeCoder.class.getCanonicalName(), "var2vec");
97 VectorOfDouble vec = new VectorOfDouble();
100 // System.err.println("var2vec:" + var.toString() + ":" + toString(vec));
101 if (LOG.isLoggable(Level.FINE)) {
102 LOG.log(Level.FINE, "var2vec:{0}:{1}", new Object[] {var.toString(), toString(vec)});
105 if (LOG.isLoggable(Level.FINER)) {
106 LOG.exiting(ScilabTypeCoder.class.getCanonicalName(), "var2vec");
112 @SuppressWarnings("unchecked")
113 private VectorOfDouble encode(ScilabType var, VectorOfDouble vec) {
114 switch (var.getType()) {
116 encode((ScilabDouble) var, vec);
119 encode((ScilabInteger) var, vec);
122 encode((ScilabBoolean) var, vec);
125 encode((ScilabString) var, vec);
128 encode((ArrayList<ScilabType>) var, vec, var.getType());
131 encode((ArrayList<ScilabType>) var, vec, var.getType());
134 encode((ArrayList<ScilabType>) var, vec, var.getType());
144 * Encode the double data
146 * @param var the data to encode
147 * @param vec the resulting buffer
149 private void encode(ScilabDouble var, VectorOfDouble vec) {
151 encodeHeader(var, vec, ScilabTypeEnum.sci_matrix);
153 // specific flag for managing the complex case
161 for (int i = 0; i < var.getHeight(); i++) {
162 for (int j = 0; j < var.getWidth(); j++) {
163 vec.add(var.getRealElement(i, j));
167 // push the complex data
169 for (int i = 0; i < var.getHeight(); i++) {
170 for (int j = 0; j < var.getWidth(); j++) {
171 vec.add(var.getImaginaryElement(i, j));
177 private void encode(ScilabInteger var, VectorOfDouble vec) {
178 // pre-processing: retrieve the raw data per type
180 long[][] longData = null;
181 short[][] shortData = null;
182 int[][] intData = null;
183 byte[][] byteData = null;
184 switch (var.getPrec()) {
188 longData = var.getDataAsLong();
192 sizeof = Integer.BYTES;
193 intData = var.getDataAsInt();
197 sizeof = Short.BYTES;
198 shortData = var.getDataAsShort();
203 byteData = var.getDataAsByte();
206 throw new IllegalArgumentException();
210 encodeHeader(var, vec, ScilabTypeEnum.sci_ints);
212 // push the data on a pre-allocated space
213 final int requiredBytes = sizeof * var.getHeight() * var.getWidth();
214 final int doubleLen = (requiredBytes + Double.BYTES - 1) / Double.BYTES;
215 final int index = vec.size();
216 vec.resize(index + doubleLen);
217 ByteBuffer view = vec.asByteBuffer(index, doubleLen);
219 for (int i = 0; i < var.getHeight(); i++) {
220 for (int j = 0; j < var.getWidth(); j++) {
221 switch (var.getPrec()) {
224 view.putLong(longData[i][j]);
228 view.putInt(intData[i][j]);
232 view.putShort(shortData[i][j]);
236 view.put(byteData[i][j]);
243 private void encode(ScilabBoolean var, VectorOfDouble vec) {
245 encodeHeader(var, vec, ScilabTypeEnum.sci_boolean);
247 // put all the boolean as int accordingly to Scilab 6 implementation
248 final int requiredBytes = Integer.BYTES * var.getHeight() * var.getWidth();
249 final int doubleLen = (requiredBytes + Double.BYTES - 1) / Double.BYTES;
250 int index = vec.size();
251 vec.resize(index + doubleLen);
253 ByteBuffer buffer = vec.asByteBuffer(index, doubleLen);
255 for (int i = 0; i < var.getHeight(); i++) {
256 for (int j = 0; j < var.getWidth(); j++) {
257 buffer.putInt(var.getData()[i][j] ? 1 : 0);
262 private void encode(ScilabString var, VectorOfDouble vec) {
264 encodeHeader(var, vec, ScilabTypeEnum.sci_strings);
266 // add the offset table which contains the offset of each UTF-8 encoded strings
267 int offsetTableStart = vec.size();
268 vec.resize(offsetTableStart + var.getHeight() * var.getWidth());
269 int offsetTableAccumulated = 0;
271 // encode the strings as UTF-8 and store the associated offset
272 Charset utf8 = Charset.forName("UTF-8");
273 for (int i = 0; i < var.getHeight(); i++) {
274 for (int j = 0; j < var.getWidth(); j++) {
275 String str = var.getData()[i][j];
276 byte[] bytes = str.getBytes(utf8);
277 // append the terminal '\0'
278 final int requiredBytes = bytes.length + 1;
279 final int doubleLen = (requiredBytes + Double.BYTES - 1) / Double.BYTES;
282 offsetTableAccumulated += doubleLen;
283 vec.set(offsetTableStart++, offsetTableAccumulated);
285 // push the data through a temporary byte buffer
286 int index = vec.size();
287 vec.resize(index + doubleLen);
288 vec.asByteBuffer(index, doubleLen).put(bytes).put((byte) 0);
294 private void encode(ArrayList<ScilabType> var, VectorOfDouble vec, ScilabTypeEnum as) {
296 encodeHeader(var, vec, as);
298 // encode list content
299 for (ScilabType localVar : var) {
300 encode(localVar, vec);
305 * Helper method to add an header of the detected type
307 * @param var the scilab matrix type to encode
308 * @param vec the raw encoded data container
309 * @param as the type to encode
310 * @param detected the detected type
312 @SuppressWarnings({"unchecked", "fallthrough"})
313 private void encodeHeader(Object var, VectorOfDouble vec, final ScilabTypeEnum as) {
314 ScilabType matrix = null;
315 ArrayList<ScilabType> list = null;
317 // defensive programming
323 matrix = (ScilabType) var;
328 list = (ArrayList<ScilabType>) var;
331 throw new IllegalArgumentException();
334 vec.add(as.swigValue());
335 if (matrix instanceof ScilabInteger) {
336 vec.add(((ScilabInteger) matrix).getPrec().swigValue());
338 if (matrix != null) {
340 vec.add(matrix.getHeight());
341 vec.add(matrix.getWidth());
342 } else if (list != null) {
343 vec.add(list.size());
345 throw new IllegalArgumentException();
350 * vec2var implementation
353 * Decode a scilab type from a buffer
355 * @param vec the buffer containing encoded scilab types
356 * @return the decoded scilab type
358 public ScilabType vec2var(VectorOfDouble vec) {
361 if (LOG.isLoggable(Level.FINER)) {
362 LOG.entering(ScilabTypeCoder.class.getName(), "vec2var", vec);
365 ScilabType var = decodeHeader(vec);
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()});
373 if (LOG.isLoggable(Level.FINER)) {
374 LOG.exiting(ScilabTypeCoder.class.getName(), "vec2var");
381 private static interface StoreFunction<R, C, V> {
382 public void apply(R r, C c, V v);
385 @SuppressWarnings("unchecked")
386 private ScilabType decode(VectorOfDouble vec, ScilabType var) {
387 switch (var.getType()) {
389 decode(vec, (ScilabDouble) var);
392 decode(vec, (ScilabInteger) var);
395 decode(vec, (ScilabBoolean) var);
398 decode(vec, (ScilabString) var);
401 decode(vec, (ArrayList<ScilabType>) var);
404 decode(vec, (ArrayList<ScilabType>) var);
407 decode(vec, (ArrayList<ScilabType>) var);
415 private ScilabType decode(VectorOfDouble vec, ScilabDouble var) {
416 double[][] realPart = var.getRealPart();
417 for (int i = 0; i < var.getHeight(); i++) {
418 for (int j = 0; j < var.getWidth(); j++) {
419 realPart[i][j] = vec.get(position++);
424 double[][] imaginaryPart = var.getImaginaryPart();
426 for (int i = 0; i < var.getHeight(); i++) {
427 for (int j = 0; j < var.getWidth(); j++) {
428 imaginaryPart[i][j] = vec.get(position++);
435 private ScilabType decode(VectorOfDouble vec, ScilabInteger var) {
437 long[][] longData = null;
438 short[][] shortData = null;
439 int[][] intData = null;
440 byte[][] byteData = null;
442 switch (var.getPrec()) {
446 longData = var.getDataAsLong();
450 sizeof = Integer.BYTES;
451 intData = var.getDataAsInt();
455 sizeof = Short.BYTES;
456 shortData = var.getDataAsShort();
461 byteData = var.getDataAsByte();
464 throw new IllegalArgumentException();
467 final int doubleLen = (sizeof * var.getHeight() * var.getWidth()) / Double.BYTES + 1;
468 ByteBuffer view = vec.asByteBuffer(position, doubleLen);
470 for (int i = 0; i < var.getHeight(); i++) {
471 for (int j = 0; j < var.getWidth(); j++) {
472 switch (var.getPrec()) {
475 longData[i][j] = view.getLong();
479 intData[i][j] = view.getInt();
483 shortData[i][j] = view.getShort();
487 byteData[i][j] = view.get();
492 position += doubleLen;
497 private ScilabType decode(VectorOfDouble vec, ScilabBoolean var) {
498 final boolean[][] data = var.getData();
500 final int doubleLen = (Integer.BYTES * var.getHeight() * var.getWidth()) / Double.BYTES + 1;
501 ByteBuffer view = vec.asByteBuffer(position, doubleLen);
503 for (int i = 0; i < var.getHeight(); i++) {
504 for (int j = 0; j < var.getWidth(); j++) {
505 data[i][j] = view.getInt() != 0;
508 position += doubleLen;
512 private ScilabType decode(VectorOfDouble vec, ScilabString var) {
513 final String[][] data = var.getData();
514 decodeString(var.getHeight(), var.getWidth(), vec, (i, j, str) -> data[i][j] = str);
518 private void decodeString(int height, int width, VectorOfDouble vec, StoreFunction<Integer, Integer, String> store) {
519 // reconstruct the offset
520 int[][] offset = new int[height][width];
521 for (int i = 0; i < height; i++) {
522 for (int j = 0; j < width; j++) {
523 offset[i][j] = (int) vec.get(position++);
527 Charset utf8 = Charset.forName("UTF-8");
528 int accumulatedOffset = 0;
529 // reconstruct each String object
530 for (int i = 0; i < height; i++) {
531 for (int j = 0; j < width; j++) {
532 final int nbOfDouble = offset[i][j] - accumulatedOffset;
533 ByteBuffer view = vec.asByteBuffer(position, nbOfDouble);
534 byte[] bytes = new byte[nbOfDouble * Double.BYTES];
538 // to avoid mis-decoding we have to pass the len of bytes
540 while (bytes[strlen] != 0) {
544 store.apply(i, j, new String(bytes, 0, strlen, utf8));
546 accumulatedOffset += nbOfDouble;
547 position += nbOfDouble;
552 private ScilabType decode(VectorOfDouble vec, ArrayList<ScilabType> var) {
553 for (int i = 0; i < var.size(); i++) {
554 ScilabType sub = decodeHeader(vec);
559 return (ScilabType) var;
562 @SuppressWarnings("fallthrough")
563 private ScilabType decodeHeader(VectorOfDouble vec) {
564 int nativeScilabType = (int) vec.get(position++);
566 // specific integer sub-type
569 // for data[][]-based type
573 // for ArrayList-based type
576 final ScilabTypeEnum type = ScilabTypeEnum.swigToEnum(nativeScilabType);
579 // special case for integer precision
580 precision = (int) vec.get(position++);
584 position++; // n-Dims not managed
585 height = (int) vec.get(position++);
586 width = (int) vec.get(position++);
591 listLen = (int) vec.get(position++);
594 throw new IllegalArgumentException();
597 // special case for complex double matrix
598 double[][] imagData = null;
599 if (type == ScilabTypeEnum.sci_matrix) {
600 boolean isComplex = vec.get(position++) != 0;
603 imagData = new double[height][width];
607 // allocate the right type with the decoded properties
610 if (height * width == 0) {
611 return new ScilabDouble();
613 return new ScilabDouble(new double[height][width], imagData);
616 if (height * width == 0) {
617 return new ScilabBoolean();
619 return new ScilabBoolean(new boolean[height][width]);
622 if (height * width == 0) {
623 return new ScilabInteger();
625 switch (ScilabIntegerTypeEnum.swigToEnum(precision)) {
627 return new ScilabInteger(new byte[height][width], false);
629 return new ScilabInteger(new short[height][width], false);
631 return new ScilabInteger(new int[height][width], false);
633 return new ScilabInteger(new long[height][width], false);
635 return new ScilabInteger(new byte[height][width], true);
637 return new ScilabInteger(new short[height][width], true);
639 return new ScilabInteger(new int[height][width], true);
641 return new ScilabInteger(new long[height][width], true);
646 if (height * width == 0) {
647 return new ScilabString();
649 return new ScilabString(new String[height][width]);
652 return new ScilabList(Collections.nCopies(listLen, null));
654 return new ScilabMList(new String[listLen - 1], Collections.nCopies(listLen - 1, null));
656 return new ScilabTList(new String[listLen - 1], Collections.nCopies(listLen - 1, null));
658 throw new IllegalArgumentException();
664 * Format the encoded value accordingly to the Java format string
667 * {@link String#format(java.lang.String, java.lang.Object...)} compatible
669 * @param vec the buffer containing encoded scilab types
670 * @throws IllegalFormatException when the underlying formatting failed
671 * @throws IllegalArgumentException when the vec argument is wrongly encoded
672 * @return A formatted string
674 public String format(String format, VectorOfDouble vec) throws IllegalFormatException, IllegalArgumentException {
675 ArrayList<Object> arguments = new ArrayList<>();
676 decodeToJava(vec, arguments);
677 return String.format(format, arguments.toArray());
680 private void decodeToJava(VectorOfDouble vec, ArrayList<Object> arguments) {
681 int nativeScilabType = (int) vec.get(position++);
683 // specific integer sub-type
686 // for data[][]-based type
690 // for ArrayList-based type
693 final ScilabTypeEnum type = ScilabTypeEnum.swigToEnum(nativeScilabType);
696 // special case for integer precision
697 precision = (int) vec.get(position++);
701 position++; // n-Dims not managed
702 height = (int) vec.get(position++);
703 width = (int) vec.get(position++);
708 listLen = (int) vec.get(position++);
711 throw new IllegalArgumentException();
714 // special case for complex double matrix
715 boolean isComplex = false;
716 if (type == ScilabTypeEnum.sci_matrix) {
717 isComplex = vec.get(position++) != 0;
720 // allocate the right type with the decoded properties
723 for (int i = 0; i < height; i++) {
724 for (int j = 0; j < width; j++) {
725 arguments.add(vec.get(position++));
730 for (int i = 0; i < height; i++) {
731 for (int j = 0; j < width; j++) {
732 arguments.add(vec.get(position++));
738 final int doubleLen = (Integer.BYTES * height * width) / Double.BYTES + 1;
739 ByteBuffer view = vec.asByteBuffer(position, doubleLen);
740 for (int i = 0; i < height; i++) {
741 for (int j = 0; j < width; j++) {
742 arguments.add(view.getInt() != 0);
745 position += doubleLen;
749 switch (ScilabIntegerTypeEnum.swigToEnum(precision)) {
752 final int sizeof = Byte.BYTES;
753 final int doubleLen = (sizeof * height * width) / Double.BYTES + 1;
754 ByteBuffer view = vec.asByteBuffer(position, doubleLen);
755 for (int i = 0; i < height; i++) {
756 for (int j = 0; j < width; j++) {
757 arguments.add(view.get());
764 final int sizeof = Short.BYTES;
765 final int doubleLen = (sizeof * height * width) / Double.BYTES + 1;
766 ByteBuffer view = vec.asByteBuffer(position, doubleLen);
767 for (int i = 0; i < height; i++) {
768 for (int j = 0; j < width; j++) {
769 arguments.add(view.getShort());
776 final int sizeof = Integer.BYTES;
777 final int doubleLen = (sizeof * height * width) / Double.BYTES + 1;
778 ByteBuffer view = vec.asByteBuffer(position, doubleLen);
779 for (int i = 0; i < height; i++) {
780 for (int j = 0; j < width; j++) {
781 arguments.add(view.getInt());
788 final int sizeof = Long.BYTES;
789 final int doubleLen = (sizeof * height * width) / Double.BYTES + 1;
790 ByteBuffer view = vec.asByteBuffer(position, doubleLen);
791 for (int i = 0; i < height; i++) {
792 for (int j = 0; j < width; j++) {
793 arguments.add(view.getLong());
801 decodeString(height, width, vec, (i, j, v) -> arguments.add(v));
806 for (int i = 0; i < listLen; i++) {
807 decodeToJava(vec, arguments);
812 throw new IllegalArgumentException();
817 * Utility to display a vec on debug
819 * @param vec the vector to convert
820 * @return a representative string
822 public static String toString(VectorOfDouble vec) {
823 int len = vec.size();
824 double[] copy = new double[len];
825 DoubleBuffer.wrap(copy).put(vec.asByteBuffer(0, len).asDoubleBuffer());
826 return Arrays.toString(copy);
830 * Utility to display a vec on debug
832 * @param vec the vector to convert
833 * @return a representative string
835 public static String toString(VectorOfScicosID vec) {
836 int len = vec.size();
837 long[] copy = new long[len];
838 LongBuffer.wrap(copy).put(vec.asByteBuffer(0, len).asLongBuffer());
839 return Arrays.toString(copy);
843 * Utility to display a vec on debug
845 * @param vec the vector to convert
846 * @return a representative string
848 public static String toString(VectorOfInt vec) {
849 int len = vec.size();
850 int[] copy = new int[len];
851 IntBuffer.wrap(copy).put(vec.asByteBuffer(0, len).asIntBuffer());
852 return Arrays.toString(copy);