2 * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3 * Copyright (C) 2015 - Scilab Enterprises - Clement DAVID
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
13 package org.scilab.modules.xcos.io;
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;
37 import java.util.logging.Level;
38 import java.util.logging.Logger;
41 * Encode and decode using a var2vec / vec2var compatible encoding.
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.
47 public class ScilabTypeCoder {
49 private static final Logger LOG = Logger.getLogger("org.scilab.modules.xcos.io");
51 class JavaScilabType {
52 final ScilabTypeEnum type;
53 final ScilabIntegerTypeEnum intType;
55 public JavaScilabType(ScilabTypeEnum type, ScilabIntegerTypeEnum intType) {
57 this.intType = intType;
61 /** current position in the vec buffer */
64 public ScilabTypeCoder() {
68 * var2vec implementation
72 * Encode any scilab type to a buffer
75 * the Scilab value to encode
76 * @return the encoded buffer
78 public VectorOfDouble var2vec(ScilabType var) {
79 final ScilabType value;
81 value = new ScilabDouble();
86 if (LOG.isLoggable(Level.FINER)) {
87 LOG.entering(ScilabTypeCoder.class.getCanonicalName(), "var2vec");
90 VectorOfDouble vec = new VectorOfDouble();
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)});
98 if (LOG.isLoggable(Level.FINER)) {
99 LOG.exiting(ScilabTypeCoder.class.getCanonicalName(), "var2vec");
105 @SuppressWarnings("unchecked")
106 private VectorOfDouble encode(ScilabType var, VectorOfDouble vec) {
107 switch (var.getType()) {
109 encode((ScilabDouble) var, vec);
112 encode((ScilabInteger) var, vec);
115 encode((ScilabBoolean) var, vec);
118 encode((ScilabString) var, vec);
121 encode((ArrayList<ScilabType>) var, vec, var.getType());
124 encode((ArrayList<ScilabType>) var, vec, var.getType());
127 encode((ArrayList<ScilabType>) var, vec, var.getType());
137 * Encode the double data
142 * the resulting buffer
144 private void encode(ScilabDouble var, VectorOfDouble vec) {
146 encodeHeader(var, vec, ScilabTypeEnum.sci_matrix);
148 // specific flag for managing the complex case
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));
161 // push the complex data
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));
169 private void encode(ScilabInteger var, VectorOfDouble vec) {
170 // pre-processing: retrieve the raw data per type
172 long[][] longData = null;
173 short[][] shortData = null;
174 int[][] intData = null;
175 byte[][] byteData = null;
176 switch (var.getPrec()) {
180 longData = var.getDataAsLong();
184 sizeof = Integer.BYTES;
185 intData = var.getDataAsInt();
189 sizeof = Short.BYTES;
190 shortData = var.getDataAsShort();
195 byteData = var.getDataAsByte();
198 throw new IllegalArgumentException();
202 encodeHeader(var, vec, ScilabTypeEnum.sci_ints);
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);
211 for (int i = 0; i < var.getHeight(); i++) {
212 for (int j = 0; j < var.getWidth(); j++) {
213 switch (var.getPrec()) {
216 view.putLong(longData[i][j]);
220 view.putInt(intData[i][j]);
224 view.putShort(shortData[i][j]);
228 view.put(byteData[i][j]);
235 private void encode(ScilabBoolean var, VectorOfDouble vec) {
237 encodeHeader(var, vec, ScilabTypeEnum.sci_boolean);
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);
245 ByteBuffer buffer = vec.asByteBuffer(index, doubleLen);
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);
254 private void encode(ScilabString var, VectorOfDouble vec) {
256 encodeHeader(var, vec, ScilabTypeEnum.sci_strings);
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;
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;
275 offsetTableAccumulated += doubleLen;
276 vec.set(offsetTableStart++, offsetTableAccumulated);
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);
288 private void encode(ArrayList<ScilabType> var, VectorOfDouble vec, ScilabTypeEnum as) {
290 encodeHeader(var, vec, as);
292 // encode list content
293 for (ScilabType localVar : var) {
294 encode(localVar, vec);
299 * Helper method to add an header of the detected type
302 * the scilab matrix type to encode
304 * the raw encoded data container
310 @SuppressWarnings({ "unchecked", "fallthrough" })
311 private void encodeHeader(Object var, VectorOfDouble vec, final ScilabTypeEnum as) {
312 ScilabType matrix = null;
313 ArrayList<ScilabType> list = null;
315 // defensive programming
321 matrix = (ScilabType) var;
326 list = (ArrayList<ScilabType>) var;
329 throw new IllegalArgumentException();
332 vec.add(as.swigValue());
333 if (matrix instanceof ScilabInteger) {
334 vec.add(((ScilabInteger) matrix).getPrec().swigValue());
336 if (matrix != null) {
338 vec.add(matrix.getHeight());
339 vec.add(matrix.getWidth());
340 } else if (list != null) {
341 vec.add(list.size());
343 throw new IllegalArgumentException();
348 * vec2var implementation
352 * Decode a scilab type from a buffer
355 * 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");
380 @SuppressWarnings("unchecked")
381 private ScilabType decode(VectorOfDouble vec, ScilabType var) {
382 switch (var.getType()) {
384 decode(vec, (ScilabDouble) var);
387 decode(vec, (ScilabInteger) var);
390 decode(vec, (ScilabBoolean) var);
393 decode(vec, (ScilabString) var);
396 decode(vec, (ArrayList<ScilabType>) var);
399 decode(vec, (ArrayList<ScilabType>) var);
402 decode(vec, (ArrayList<ScilabType>) var);
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++);
418 double[][] imaginaryPart = var.getImaginaryPart();
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++);
428 private ScilabType decode(VectorOfDouble vec, ScilabInteger var) {
430 long[][] longData = null;
431 short[][] shortData = null;
432 int[][] intData = null;
433 byte[][] byteData = null;
435 switch (var.getPrec()) {
439 longData = var.getDataAsLong();
443 sizeof = Integer.BYTES;
444 intData = var.getDataAsInt();
448 sizeof = Short.BYTES;
449 shortData = var.getDataAsShort();
454 byteData = var.getDataAsByte();
457 throw new IllegalArgumentException();
460 final int doubleLen = (sizeof * var.getHeight() * var.getWidth()) / Double.BYTES + 1;
461 ByteBuffer view = vec.asByteBuffer(position, doubleLen);
463 for (int i = 0; i < var.getHeight(); i++) {
464 for (int j = 0; j < var.getWidth(); j++) {
465 switch (var.getPrec()) {
468 longData[i][j] = view.getLong();
472 intData[i][j] = view.getInt();
476 shortData[i][j] = view.getShort();
480 byteData[i][j] = view.get();
485 position += doubleLen;
490 private ScilabType decode(VectorOfDouble vec, ScilabBoolean var) {
491 final boolean[][] data = var.getData();
493 final int doubleLen = (Integer.BYTES * var.getHeight() * var.getWidth()) / Double.BYTES + 1;
494 ByteBuffer view = vec.asByteBuffer(position, doubleLen);
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;
501 position += doubleLen;
505 private ScilabType decode(VectorOfDouble vec, ScilabString var) {
506 final String[][] data = var.getData();
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++);
516 Charset utf8 = Charset.forName("UTF-8");
517 int accumulatedOffset = 0;
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];
528 // to avoid mis-decoding we have to pass the len of bytes
530 while (bytes[strlen] != 0) {
534 data[i][j] = new String(bytes, 0, strlen, utf8);
536 accumulatedOffset += nbOfDouble;
537 position += nbOfDouble;
544 private ScilabType decode(VectorOfDouble vec, ArrayList<ScilabType> var) {
545 for (int i = 0; i < var.size(); i++) {
546 ScilabType sub = decodeHeader(vec);
551 return (ScilabType) var;
554 @SuppressWarnings("fallthrough")
555 private ScilabType decodeHeader(VectorOfDouble vec) {
556 int nativeScilabType = (int) vec.get(position++);
558 // specific integer sub-type
561 // for data[][]-based type
565 // for ArrayList-based type
568 final ScilabTypeEnum type = ScilabTypeEnum.swigToEnum(nativeScilabType);
571 // special case for integer precision
572 precision = (int) vec.get(position++);
576 position++; // n-Dims not managed
577 height = (int) vec.get(position++);
578 width = (int) vec.get(position++);
583 listLen = (int) vec.get(position++);
586 throw new IllegalArgumentException();
589 // special case for complex double matrix
590 double[][] imagData = null;
591 if (type == ScilabTypeEnum.sci_matrix) {
592 boolean isComplex = vec.get(position++) != 0;
595 imagData = new double[height][width];
599 // allocate the right type with the decoded properties
602 if (height * width == 0) {
603 return new ScilabDouble();
605 return new ScilabDouble(new double[height][width], imagData);
608 if (height * width == 0) {
609 return new ScilabBoolean();
611 return new ScilabBoolean(new boolean[height][width]);
614 if (height * width == 0) {
615 return new ScilabInteger();
617 switch (ScilabIntegerTypeEnum.swigToEnum(precision)) {
619 return new ScilabInteger(new byte[height][width], false);
621 return new ScilabInteger(new short[height][width], false);
623 return new ScilabInteger(new int[height][width], false);
625 return new ScilabInteger(new long[height][width], false);
627 return new ScilabInteger(new byte[height][width], true);
629 return new ScilabInteger(new short[height][width], true);
631 return new ScilabInteger(new int[height][width], true);
633 return new ScilabInteger(new long[height][width], true);
637 if (height * width == 0) {
638 return new ScilabString();
640 return new ScilabString(new String[height][width]);
643 return new ScilabList(Collections.nCopies(listLen, null));
645 return new ScilabMList(new String[listLen - 1], Collections.nCopies(listLen - 1, null));
647 return new ScilabTList(new String[listLen - 1], Collections.nCopies(listLen - 1, null));
649 throw new IllegalArgumentException();
655 * Utility to display a vec on debug
657 * @param vec the vector to convert
658 * @return a representative string
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);
668 * Utility to display a vec on debug
670 * @param vec the vector to convert
671 * @return a representative string
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);
681 * Utility to display a vec on debug
683 * @param vec the vector to convert
684 * @return a representative string
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);