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);
151 if (var.getHeight() * var.getWidth() == 0) {
155 // specific flag for managing the complex case
163 for (int i = 0; i < var.getHeight(); i++) {
164 for (int j = 0; j < var.getWidth(); j++) {
165 vec.add(var.getRealElement(i, j));
169 // push the complex data
171 for (int i = 0; i < var.getHeight(); i++) {
172 for (int j = 0; j < var.getWidth(); j++) {
173 vec.add(var.getImaginaryElement(i, j));
179 private void encode(ScilabInteger var, VectorOfDouble vec) {
180 // pre-processing: retrieve the raw data per type
182 long[][] longData = null;
183 short[][] shortData = null;
184 int[][] intData = null;
185 byte[][] byteData = null;
186 switch (var.getPrec()) {
190 longData = var.getDataAsLong();
194 sizeof = Integer.BYTES;
195 intData = var.getDataAsInt();
199 sizeof = Short.BYTES;
200 shortData = var.getDataAsShort();
205 byteData = var.getDataAsByte();
208 throw new IllegalArgumentException();
212 encodeHeader(var, vec, ScilabTypeEnum.sci_ints);
213 if (var.getHeight() * var.getWidth() == 0) {
217 // push the data on a pre-allocated space
218 final int requiredBytes = sizeof * var.getHeight() * var.getWidth();
219 final int doubleLen = (requiredBytes + Double.BYTES - 1) / Double.BYTES;
220 final int index = vec.size();
221 vec.resize(index + doubleLen);
222 ByteBuffer view = vec.asByteBuffer(index, doubleLen);
224 for (int i = 0; i < var.getHeight(); i++) {
225 for (int j = 0; j < var.getWidth(); j++) {
226 switch (var.getPrec()) {
229 view.putLong(longData[i][j]);
233 view.putInt(intData[i][j]);
237 view.putShort(shortData[i][j]);
241 view.put(byteData[i][j]);
248 private void encode(ScilabBoolean var, VectorOfDouble vec) {
250 encodeHeader(var, vec, ScilabTypeEnum.sci_boolean);
251 if (var.getHeight() * var.getWidth() == 0) {
255 // put all the boolean as int accordingly to Scilab 6 implementation
256 final int requiredBytes = Integer.BYTES * var.getHeight() * var.getWidth();
257 final int doubleLen = (requiredBytes + Double.BYTES - 1) / Double.BYTES;
258 int index = vec.size();
259 vec.resize(index + doubleLen);
261 ByteBuffer buffer = vec.asByteBuffer(index, doubleLen);
263 for (int i = 0; i < var.getHeight(); i++) {
264 for (int j = 0; j < var.getWidth(); j++) {
265 buffer.putInt(var.getData()[i][j] ? 1 : 0);
270 private void encode(ScilabString var, VectorOfDouble vec) {
272 encodeHeader(var, vec, ScilabTypeEnum.sci_strings);
273 if (var.getHeight() * var.getWidth() == 0) {
277 // add the offset table which contains the offset of each UTF-8 encoded strings
278 int offsetTableStart = vec.size();
279 vec.resize(offsetTableStart + var.getHeight() * var.getWidth());
280 int offsetTableAccumulated = 0;
282 // encode the strings as UTF-8 and store the associated offset
283 Charset utf8 = Charset.forName("UTF-8");
284 for (int i = 0; i < var.getHeight(); i++) {
285 for (int j = 0; j < var.getWidth(); j++) {
286 String str = var.getData()[i][j];
287 byte[] bytes = str.getBytes(utf8);
288 // append the terminal '\0'
289 final int requiredBytes = bytes.length + 1;
290 final int doubleLen = (requiredBytes + Double.BYTES - 1) / Double.BYTES;
293 offsetTableAccumulated += doubleLen;
294 vec.set(offsetTableStart++, offsetTableAccumulated);
296 // push the data through a temporary byte buffer
297 int index = vec.size();
298 vec.resize(index + doubleLen);
299 vec.asByteBuffer(index, doubleLen).put(bytes).put((byte) 0);
305 private void encode(ArrayList<ScilabType> var, VectorOfDouble vec, ScilabTypeEnum as) {
307 encodeHeader(var, vec, as);
309 // encode list content
310 for (ScilabType localVar : var) {
311 encode(localVar, vec);
316 * Helper method to add an header of the detected type
318 * @param var the scilab matrix type to encode
319 * @param vec the raw encoded data container
320 * @param as the type to encode
321 * @param detected the detected type
323 @SuppressWarnings({"unchecked", "fallthrough"})
324 private void encodeHeader(Object var, VectorOfDouble vec, final ScilabTypeEnum as) {
325 ScilabType matrix = null;
326 ArrayList<ScilabType> list = null;
328 // defensive programming
334 matrix = (ScilabType) var;
339 list = (ArrayList<ScilabType>) var;
342 throw new IllegalArgumentException();
345 // corner-case [] is a ScilabDouble
346 if (matrix != null && matrix.getHeight() * matrix.getWidth() == 0) {
347 vec.add(ScilabTypeEnum.sci_matrix.swigValue());
351 vec.add(0); // isComplex is part of []
355 vec.add(as.swigValue());
356 if (matrix instanceof ScilabInteger) {
357 vec.add(((ScilabInteger) matrix).getPrec().swigValue());
359 if (matrix != null) {
361 vec.add(matrix.getHeight());
362 vec.add(matrix.getWidth());
363 } else if (list != null) {
364 vec.add(list.size());
366 throw new IllegalArgumentException();
371 * vec2var implementation
374 * Decode a scilab type from a buffer
376 * @param vec the buffer containing encoded scilab types
377 * @return the decoded scilab type
379 public ScilabType vec2var(VectorOfDouble vec) {
382 if (LOG.isLoggable(Level.FINER)) {
383 LOG.entering(ScilabTypeCoder.class.getName(), "vec2var", vec);
386 ScilabType var = decodeHeader(vec);
389 // System.err.println("vec2var:" + toString(vec) + ":" + var.toString());
390 if (LOG.isLoggable(Level.FINE)) {
391 LOG.log(Level.FINE, "vec2var:{0}:{1}", new Object[] {toString(vec), var.toString()});
394 if (LOG.isLoggable(Level.FINER)) {
395 LOG.exiting(ScilabTypeCoder.class.getName(), "vec2var");
402 private static interface StoreFunction<R, C, V> {
403 public void apply(R r, C c, V v);
406 @SuppressWarnings("unchecked")
407 private ScilabType decode(VectorOfDouble vec, ScilabType var) {
408 switch (var.getType()) {
410 decode(vec, (ScilabDouble) var);
413 decode(vec, (ScilabInteger) var);
416 decode(vec, (ScilabBoolean) var);
419 decode(vec, (ScilabString) var);
422 decode(vec, (ArrayList<ScilabType>) var);
425 decode(vec, (ArrayList<ScilabType>) var);
428 decode(vec, (ArrayList<ScilabType>) var);
436 private ScilabType decode(VectorOfDouble vec, ScilabDouble var) {
437 double[][] realPart = var.getRealPart();
438 for (int i = 0; i < var.getHeight(); i++) {
439 for (int j = 0; j < var.getWidth(); j++) {
440 realPart[i][j] = vec.get(position++);
445 double[][] imaginaryPart = var.getImaginaryPart();
447 for (int i = 0; i < var.getHeight(); i++) {
448 for (int j = 0; j < var.getWidth(); j++) {
449 imaginaryPart[i][j] = vec.get(position++);
456 private ScilabType decode(VectorOfDouble vec, ScilabInteger var) {
458 long[][] longData = null;
459 short[][] shortData = null;
460 int[][] intData = null;
461 byte[][] byteData = null;
463 switch (var.getPrec()) {
467 longData = var.getDataAsLong();
471 sizeof = Integer.BYTES;
472 intData = var.getDataAsInt();
476 sizeof = Short.BYTES;
477 shortData = var.getDataAsShort();
482 byteData = var.getDataAsByte();
485 throw new IllegalArgumentException();
488 final int doubleLen = (sizeof * var.getHeight() * var.getWidth()) / Double.BYTES + 1;
489 ByteBuffer view = vec.asByteBuffer(position, doubleLen);
491 for (int i = 0; i < var.getHeight(); i++) {
492 for (int j = 0; j < var.getWidth(); j++) {
493 switch (var.getPrec()) {
496 longData[i][j] = view.getLong();
500 intData[i][j] = view.getInt();
504 shortData[i][j] = view.getShort();
508 byteData[i][j] = view.get();
513 position += doubleLen;
518 private ScilabType decode(VectorOfDouble vec, ScilabBoolean var) {
519 final boolean[][] data = var.getData();
521 final int doubleLen = (Integer.BYTES * var.getHeight() * var.getWidth()) / Double.BYTES + 1;
522 ByteBuffer view = vec.asByteBuffer(position, doubleLen);
524 for (int i = 0; i < var.getHeight(); i++) {
525 for (int j = 0; j < var.getWidth(); j++) {
526 data[i][j] = view.getInt() != 0;
529 position += doubleLen;
533 private ScilabType decode(VectorOfDouble vec, ScilabString var) {
534 final String[][] data = var.getData();
535 decodeString(var.getHeight(), var.getWidth(), vec, (i, j, str) -> data[i][j] = str);
539 private void decodeString(int height, int width, VectorOfDouble vec, StoreFunction<Integer, Integer, String> store) {
540 // reconstruct the offset
541 int[][] offset = new int[height][width];
542 for (int i = 0; i < height; i++) {
543 for (int j = 0; j < width; j++) {
544 offset[i][j] = (int) vec.get(position++);
548 Charset utf8 = Charset.forName("UTF-8");
549 int accumulatedOffset = 0;
550 // reconstruct each String object
551 for (int i = 0; i < height; i++) {
552 for (int j = 0; j < width; j++) {
553 final int nbOfDouble = offset[i][j] - accumulatedOffset;
554 ByteBuffer view = vec.asByteBuffer(position, nbOfDouble);
555 byte[] bytes = new byte[nbOfDouble * Double.BYTES];
559 // to avoid mis-decoding we have to pass the len of bytes
561 while (bytes[strlen] != 0) {
565 store.apply(i, j, new String(bytes, 0, strlen, utf8));
567 accumulatedOffset += nbOfDouble;
568 position += nbOfDouble;
573 private ScilabType decode(VectorOfDouble vec, ArrayList<ScilabType> var) {
574 for (int i = 0; i < var.size(); i++) {
575 ScilabType sub = decodeHeader(vec);
580 return (ScilabType) var;
583 @SuppressWarnings("fallthrough")
584 private ScilabType decodeHeader(VectorOfDouble vec) {
585 int nativeScilabType = (int) vec.get(position++);
587 // specific integer sub-type
590 // for data[][]-based type
594 // for ArrayList-based type
597 final ScilabTypeEnum type = ScilabTypeEnum.swigToEnum(nativeScilabType);
600 // special case for integer precision
601 precision = (int) vec.get(position++);
605 position++; // n-Dims not managed
606 height = (int) vec.get(position++);
607 width = (int) vec.get(position++);
612 listLen = (int) vec.get(position++);
615 throw new IllegalArgumentException();
618 // special case for complex double matrix
619 double[][] imagData = null;
620 if (type == ScilabTypeEnum.sci_matrix) {
621 boolean isComplex = vec.get(position++) != 0;
624 imagData = new double[height][width];
628 // allocate the right type with the decoded properties
631 if (height * width == 0) {
632 return new ScilabDouble();
634 return new ScilabDouble(new double[height][width], imagData);
637 if (height * width == 0) {
638 return new ScilabDouble();
640 return new ScilabBoolean(new boolean[height][width]);
643 if (height * width == 0) {
644 return new ScilabDouble();
646 switch (ScilabIntegerTypeEnum.swigToEnum(precision)) {
648 return new ScilabInteger(new byte[height][width], false);
650 return new ScilabInteger(new short[height][width], false);
652 return new ScilabInteger(new int[height][width], false);
654 return new ScilabInteger(new long[height][width], false);
656 return new ScilabInteger(new byte[height][width], true);
658 return new ScilabInteger(new short[height][width], true);
660 return new ScilabInteger(new int[height][width], true);
662 return new ScilabInteger(new long[height][width], true);
667 if (height * width == 0) {
668 return new ScilabDouble();
670 return new ScilabString(new String[height][width]);
673 return new ScilabList(Collections.nCopies(listLen, null));
675 return new ScilabMList(new String[listLen - 1], Collections.nCopies(listLen - 1, null));
677 return new ScilabTList(new String[listLen - 1], Collections.nCopies(listLen - 1, null));
679 throw new IllegalArgumentException();
685 * Format the encoded value accordingly to the Java format string
688 * {@link String#format(java.lang.String, java.lang.Object...)} compatible
690 * @param vec the buffer containing encoded scilab types
691 * @throws IllegalFormatException when the underlying formatting failed
692 * @throws IllegalArgumentException when the vec argument is wrongly encoded
693 * @return A formatted string
695 public String format(String format, VectorOfDouble vec) throws IllegalFormatException, IllegalArgumentException {
696 ArrayList<Object> arguments = new ArrayList<>();
697 decodeToJava(vec, arguments);
698 return String.format(format, arguments.toArray());
701 private void decodeToJava(VectorOfDouble vec, ArrayList<Object> arguments) {
702 int nativeScilabType = (int) vec.get(position++);
704 // specific integer sub-type
707 // for data[][]-based type
711 // for ArrayList-based type
714 final ScilabTypeEnum type = ScilabTypeEnum.swigToEnum(nativeScilabType);
717 // special case for integer precision
718 precision = (int) vec.get(position++);
722 position++; // n-Dims not managed
723 height = (int) vec.get(position++);
724 width = (int) vec.get(position++);
729 listLen = (int) vec.get(position++);
732 throw new IllegalArgumentException();
735 // special case for complex double matrix
736 boolean isComplex = false;
737 if (type == ScilabTypeEnum.sci_matrix) {
738 isComplex = vec.get(position++) != 0;
741 // allocate the right type with the decoded properties
744 for (int i = 0; i < height; i++) {
745 for (int j = 0; j < width; j++) {
746 arguments.add(vec.get(position++));
751 for (int i = 0; i < height; i++) {
752 for (int j = 0; j < width; j++) {
753 arguments.add(vec.get(position++));
759 final int doubleLen = (Integer.BYTES * height * width) / Double.BYTES + 1;
760 ByteBuffer view = vec.asByteBuffer(position, doubleLen);
761 for (int i = 0; i < height; i++) {
762 for (int j = 0; j < width; j++) {
763 arguments.add(view.getInt() != 0);
766 position += doubleLen;
770 switch (ScilabIntegerTypeEnum.swigToEnum(precision)) {
773 final int sizeof = Byte.BYTES;
774 final int doubleLen = (sizeof * height * width) / Double.BYTES + 1;
775 ByteBuffer view = vec.asByteBuffer(position, doubleLen);
776 for (int i = 0; i < height; i++) {
777 for (int j = 0; j < width; j++) {
778 arguments.add(view.get());
785 final int sizeof = Short.BYTES;
786 final int doubleLen = (sizeof * height * width) / Double.BYTES + 1;
787 ByteBuffer view = vec.asByteBuffer(position, doubleLen);
788 for (int i = 0; i < height; i++) {
789 for (int j = 0; j < width; j++) {
790 arguments.add(view.getShort());
797 final int sizeof = Integer.BYTES;
798 final int doubleLen = (sizeof * height * width) / Double.BYTES + 1;
799 ByteBuffer view = vec.asByteBuffer(position, doubleLen);
800 for (int i = 0; i < height; i++) {
801 for (int j = 0; j < width; j++) {
802 arguments.add(view.getInt());
809 final int sizeof = Long.BYTES;
810 final int doubleLen = (sizeof * height * width) / Double.BYTES + 1;
811 ByteBuffer view = vec.asByteBuffer(position, doubleLen);
812 for (int i = 0; i < height; i++) {
813 for (int j = 0; j < width; j++) {
814 arguments.add(view.getLong());
822 decodeString(height, width, vec, (i, j, v) -> arguments.add(v));
827 for (int i = 0; i < listLen; i++) {
828 decodeToJava(vec, arguments);
833 throw new IllegalArgumentException();
838 * Utility to display a vec on debug
840 * @param vec the vector to convert
841 * @return a representative string
843 public static String toString(VectorOfDouble vec) {
844 int len = vec.size();
845 double[] copy = new double[len];
846 DoubleBuffer.wrap(copy).put(vec.asByteBuffer(0, len).asDoubleBuffer());
847 return Arrays.toString(copy);
851 * Utility to display a vec on debug
853 * @param vec the vector to convert
854 * @return a representative string
856 public static String toString(VectorOfScicosID vec) {
857 int len = vec.size();
858 long[] copy = new long[len];
859 LongBuffer.wrap(copy).put(vec.asByteBuffer(0, len).asLongBuffer());
860 return Arrays.toString(copy);
864 * Utility to display a vec on debug
866 * @param vec the vector to convert
867 * @return a representative string
869 public static String toString(VectorOfInt vec) {
870 int len = vec.size();
871 int[] copy = new int[len];
872 IntBuffer.wrap(copy).put(vec.asByteBuffer(0, len).asIntBuffer());
873 return Arrays.toString(copy);