All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.jmatio.io.MatFileReader Maven / Gradle / Ivy

There is a newer version: 3.1.1
Show newest version
/*
 * Code licensed under new-style BSD (see LICENSE).
 * All code up to tags/original: Copyright (c) 2006, Wojciech Gradkowski
 * All code after tags/original: Copyright (c) 2015, DiffPlug
 */
package com.jmatio.io;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.lang.ref.WeakReference;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.zip.InflaterInputStream;

import com.jmatio.common.MatDataTypes;
import com.jmatio.io.stream.BufferedOutputStream;
import com.jmatio.io.stream.ByteBufferInputStream;
import com.jmatio.io.stream.ByteBufferedOutputStream;
import com.jmatio.io.stream.MatFileInputStream;
import com.jmatio.types.ByteStorageSupport;
import com.jmatio.types.MLArray;
import com.jmatio.types.MLCell;
import com.jmatio.types.MLChar;
import com.jmatio.types.MLDouble;
import com.jmatio.types.MLEmptyArray;
import com.jmatio.types.MLInt16;
import com.jmatio.types.MLInt32;
import com.jmatio.types.MLInt64;
import com.jmatio.types.MLInt8;
import com.jmatio.types.MLJavaObject;
import com.jmatio.types.MLNumericArray;
import com.jmatio.types.MLObject;
import com.jmatio.types.MLSingle;
import com.jmatio.types.MLSparse;
import com.jmatio.types.MLStructure;
import com.jmatio.types.MLUInt16;
import com.jmatio.types.MLUInt32;
import com.jmatio.types.MLUInt64;
import com.jmatio.types.MLUInt8;

/**
 * MAT-file reader. Reads MAT-file into MLArray objects.
 * 
 * Usage:
 * 

 * //read in the file
 * MatFileReader mfr = new MatFileReader( "mat_file.mat" );
 * 
 * //get array of a name "my_array" from file
 * MLArray mlArrayRetrived = mfr.getMLArray( "my_array" );
 * 
 * //or get the collection of all arrays that were stored in the file
 * Map content = mfr.getContent();
 * 
* * @see com.jmatio.io.MatFileFilter * @author Wojciech Gradkowski ([email protected]) */ public class MatFileReader { public static final int MEMORY_MAPPED_FILE = 1; public static final int DIRECT_BYTE_BUFFER = 2; public static final int HEAP_BYTE_BUFFER = 4; /** * MAT-file header */ MatFileHeader matFileHeader; /** * Container for red MLArrays */ Map data; /** * Tells how bytes are organized in the buffer. */ ByteOrder byteOrder; /** * Array name filter */ private MatFileFilter filter; /** * Creates instance of MatFileReader and reads MAT-file * from location given as fileName. * * This method reads MAT-file without filtering. * * @param fileName the MAT-file path String * @throws IOException when error occurred while processing the file. */ public MatFileReader(String fileName) throws FileNotFoundException, IOException { this(new File(fileName), new MatFileFilter()); } /** * Creates instance of MatFileReader and reads MAT-file * from location given as fileName. * * Results are filtered by MatFileFilter. Arrays that do not meet * filter match condition will not be available in results. * * @param fileName the MAT-file path String * @param MatFileFilter array name filter. * @throws IOException when error occurred while processing the file. */ public MatFileReader(String fileName, MatFileFilter filter) throws IOException { this(new File(fileName), filter); } /** * Creates instance of MatFileReader and reads MAT-file * from file. * * This method reads MAT-file without filtering. * * @param file the MAT-file * @throws IOException when error occurred while processing the file. */ public MatFileReader(File file) throws IOException { this(file, new MatFileFilter()); } /** * Creates instance of MatFileReader and reads MAT-file from * file. *

* Results are filtered by MatFileFilter. Arrays that do not * meet filter match condition will not be available in results. *

* Note: this method reads file using the memory mapped file policy, see * notes to {@link #read(File, MatFileFilter, com.jmatio.io.MatFileReader.MallocPolicy)} * * @param file * the MAT-file * @param MatFileFilter * array name filter. * @throws IOException * when error occurred while processing the file. */ public MatFileReader(File file, MatFileFilter filter) throws IOException { this(); read(file, filter, MEMORY_MAPPED_FILE); } public MatFileReader() { filter = new MatFileFilter(); data = new LinkedHashMap(); } /** * Reads the content of a MAT-file and returns the mapped content. *

* This method calls * read(file, new MatFileFilter(), MallocPolicy.MEMORY_MAPPED_FILE). * * @param file * a valid MAT-file file to be read * @return the same as {@link #getContent()} * @throws IOException * if error occurs during file processing */ public synchronized Map read(File file) throws IOException { return read(file, new MatFileFilter(), MEMORY_MAPPED_FILE); } /** * Reads the content of a MAT-file and returns the mapped content. *

* This method calls * read(file, new MatFileFilter(), policy). * * @param file * a valid MAT-file file to be read * @param policy * the file memory allocation policy * @return the same as {@link #getContent()} * @throws IOException * if error occurs during file processing */ public synchronized Map read(File file, int policy) throws IOException { return read(file, new MatFileFilter(), policy); } /** * Reads the content of a MAT-file and returns the mapped content. *

* Because of java bug #4724038 * which disables releasing the memory mapped resource, additional different * allocation modes are available. *

    *
  • {@link #MEMORY_MAPPED_FILE} - a memory mapped file
  • *
  • {@link #DIRECT_BYTE_BUFFER} - a uses * {@link ByteBuffer#allocateDirect(int)} method to read in * the file contents
  • *
  • {@link #HEAP_BYTE_BUFFER} - a uses * {@link ByteBuffer#allocate(int)} method to read in the * file contents
  • *
* Note: memory mapped file will try to invoke a nasty code to relase * it's resources * * @param file * a valid MAT-file file to be read * @param filter * the array filter applied during reading * @param policy * the file memory allocation policy * @return the same as {@link #getContent()} * @see MatFileFilter * @throws IOException * if error occurs during file processing */ public synchronized Map read(File file, MatFileFilter filter, int policy) throws IOException { return read(new RandomAccessFile(file, "r"), filter, policy); } public synchronized Map read(RandomAccessFile raFile, MatFileFilter filter, int policy) throws IOException { this.filter = filter; //clear the results for (String key : data.keySet()) { data.remove(key); } FileChannel roChannel = null; ByteBuffer buf = null; WeakReference bufferWeakRef = null; try { //Create a read-only memory-mapped file roChannel = raFile.getChannel(); // until java bug #4715154 is fixed I am not using memory mapped files // The bug disables re-opening the memory mapped files for writing // or deleting until the VM stops working. In real life I need to open // and update files switch (policy) { case DIRECT_BYTE_BUFFER: buf = ByteBuffer.allocateDirect((int) roChannel.size()); roChannel.read(buf, 0); buf.rewind(); break; case HEAP_BYTE_BUFFER: int filesize = (int) roChannel.size(); System.gc(); buf = ByteBuffer.allocate(filesize); // The following two methods couldn't be used (at least under MS Windows) // since they are implemented in a suboptimal way. Each of them // allocates its own _direct_ buffer of exactly the same size, // the buffer passed as parameter has, reads data into it and // only afterwards moves data into the buffer passed as parameter. // roChannel.read(buf, 0); // ends up in outOfMemory // raFile.readFully(buf.array()); // ends up in outOfMemory int numberOfBlocks = filesize / DIRECT_BUFFER_LIMIT + ((filesize % DIRECT_BUFFER_LIMIT) > 0 ? 1 : 0); if (numberOfBlocks > 1) { ByteBuffer tempByteBuffer = ByteBuffer.allocateDirect(DIRECT_BUFFER_LIMIT); for (long block = 0; block < numberOfBlocks; block++) { tempByteBuffer.clear(); roChannel.read(tempByteBuffer, block * DIRECT_BUFFER_LIMIT); tempByteBuffer.flip(); buf.put(tempByteBuffer); } tempByteBuffer = null; } else roChannel.read(buf, 0); buf.rewind(); break; case MEMORY_MAPPED_FILE: buf = roChannel.map(FileChannel.MapMode.READ_ONLY, 0, (int) roChannel.size()); bufferWeakRef = new WeakReference((MappedByteBuffer) buf); break; default: throw new IllegalArgumentException("Unknown file allocation policy"); } //read in file header readHeader(buf); while (buf.remaining() > 0) { readData(buf); } return getContent(); } catch (IOException e) { throw e; } finally { if (roChannel != null) { roChannel.close(); } if (raFile != null) { raFile.close(); } if (buf != null && bufferWeakRef != null && policy == MEMORY_MAPPED_FILE) { try { clean(buf); } catch (Exception e) { int GC_TIMEOUT_MS = 1000; buf = null; long start = System.currentTimeMillis(); while (bufferWeakRef.get() != null) { if (System.currentTimeMillis() - start > GC_TIMEOUT_MS) { break; //a hell cannot be unmapped - hopefully GC will //do it's job later } System.gc(); Thread.yield(); } } } } } private static final int DIRECT_BUFFER_LIMIT = 1 << 25; /** * Workaround taken from bug #4724038 * to release the memory mapped byte buffer. *

* Little quote from SUN: This is highly inadvisable, to put it mildly. * It is exceedingly dangerous to forcibly unmap a mapped byte buffer that's * visible to Java code. Doing so risks both the security and stability of * the system *

* Since the memory byte buffer used to map the file is not exposed to the * outside world, maybe it's save to use it without being cursed by the SUN. * Since there is no other solution this will do (don't trust voodoo GC * invocation) * * @param buffer * the buffer to be unmapped * @throws Exception * all kind of evil stuff */ private void clean(final Object buffer) throws Exception { AccessController.doPrivileged(new PrivilegedAction() { public Object run() { try { Method getCleanerMethod = buffer.getClass().getMethod( "cleaner", new Class[0]); getCleanerMethod.setAccessible(true); sun.misc.Cleaner cleaner = (sun.misc.Cleaner) getCleanerMethod .invoke(buffer, new Object[0]); cleaner.clean(); } catch (Exception e) { e.printStackTrace(); } return null; } }); } /** * Gets MAT-file header * * @return - a MatFileHeader object */ public MatFileHeader getMatFileHeader() { return matFileHeader; } /** * Returns list of MLArray objects that were inside MAT-file * * @return - a ArrayList * @deprecated use getContent which returns a Map to provide * easier access to MLArrays contained in MAT-file */ public ArrayList getData() { return new ArrayList(data.values()); } /** * Returns the value to which the red file maps the specified array name. * * Returns null if the file contains no content for this name. * * @param - array name * @return - the MLArray to which this file maps the specified name, * or null if the file contains no content for this name. */ public MLArray getMLArray(String name) { return data.get(name); } /** * Returns a map of MLArray objects that were inside MAT-file. * * MLArrays are mapped with MLArrays' names * * @return - a Map of MLArrays mapped with their names. */ public Map getContent() { return data; } /** * Reads data form byte buffer. Searches for either * miCOMPRESSED data or miMATRIX data. * * Compressed data are inflated and the product is recursively passed back * to this same method. * * Modifies buf position. * * @param buf - * input byte buffer * @throws IOException when error occurs while reading the buffer. */ void readData(ByteBuffer buf) throws IOException { //read data ISMatTag tag = new ISMatTag(buf); switch (tag.type) { case MatDataTypes.miCOMPRESSED: long numOfBytes = tag.size; //inflate and recur if (buf.remaining() < numOfBytes) { throw new MatlabIOException("Compressed buffer length miscalculated!"); } //instead of standard Inlater class instance I use an inflater input //stream... gives a great boost to the performance InflaterInputStream iis = new InflaterInputStream(new ByteBufferInputStream(buf, numOfBytes)); //process data decompression byte[] result = new byte[1024]; // use direct buffer allocation // BufferedOutputStream dos = new FileBufferedOutputStream(); BufferedOutputStream dos = new ByteBufferedOutputStream(tag.size, false); try { int i; try { do { i = iis.read(result, 0, result.length); int len = Math.max(0, i); dos.write(result, 0, len); } while (i > 0); } catch (IOException e) { throw new MatlabIOException("Could not decompress data: " + e); } finally { iis.close(); dos.flush(); } //create a ByteBuffer from the deflated data ByteBuffer out = dos.buffer(); out.rewind(); //with proper byte ordering out.order(byteOrder); readData(out); } catch (IOException e) { throw e; } finally { dos.close(); } break; case MatDataTypes.miMATRIX: //read in the matrix int pos = buf.position(); MLArray element = readMatrix(buf, true); if (element != null && !data.containsKey(element.getName())) { data.put(element.getName(), element); } else { int red = buf.position() - pos; int toread = tag.size - red; buf.position(buf.position() + toread); } int red = buf.position() - pos; int toread = tag.size - red; if (toread != 0) { throw new MatlabIOException("Matrix was not red fully! " + toread + " remaining in the buffer."); } break; default: throw new MatlabIOException("Incorrect data tag: " + tag); } } /** * Reads miMATRIX from from input stream. * * If reading was not finished (which is normal for filtered results) * returns null. * * Modifies buf position to the position when reading * finished. * * Uses recursive processing for some ML**** data types. * * @param buf - * input byte buffer * @param isRoot - * when true informs that if this is a top level * matrix * @return - MLArray or null if matrix does * not match filter * @throws IOException when error occurs while reading the buffer. */ private MLArray readMatrix(ByteBuffer buf, boolean isRoot) throws IOException { //result MLArray mlArray; ISMatTag tag; //read flags int[] flags = readFlags(buf); int attributes = (flags.length != 0) ? flags[0] : 0; int nzmax = (flags.length != 0) ? flags[1] : 0; int type = attributes & 0xff; //read Array dimension int[] dims = readDimension(buf); //read array Name String name = readName(buf); //if this array is filtered out return immediately if (isRoot && !filter.matches(name)) { return null; } //read data >> consider changing it to stategy pattern switch (type) { case MLArray.mxSTRUCT_CLASS: MLStructure struct = new MLStructure(name, dims, type, attributes); //field name lenght - this subelement always uses the compressed data element format new ISMatTag(buf); int maxlen = buf.getInt(); //maximum field length ////// read fields data as Int8 tag = new ISMatTag(buf); //calculate number of fields int numOfFields = tag.size / maxlen; String[] fieldNames = new String[numOfFields]; for (int i = 0; i < numOfFields; i++) { byte[] names = new byte[maxlen]; buf.get(names); fieldNames[i] = zeroEndByteArrayToString(names); } buf.position(buf.position() + tag.padding); //read fields for (int index = 0; index < struct.getM() * struct.getN(); index++) { for (int i = 0; i < numOfFields; i++) { //read matrix recursively tag = new ISMatTag(buf); MLArray array; if (tag.size > 0) { array = readMatrix(buf, false); } else { array = new MLEmptyArray(); } array.name = fieldNames[i]; struct.setField(fieldNames[i], array, index); } } mlArray = struct; break; case MLArray.mxCELL_CLASS: MLCell cell = new MLCell(name, dims, type, attributes); for (int i = 0; i < cell.getM() * cell.getN(); i++) { tag = new ISMatTag(buf); if (tag.size > 0) { //read matrix recursively MLArray cellmatrix = readMatrix(buf, false); cell.set(cellmatrix, i); } else { cell.set(new MLEmptyArray(), i); } } mlArray = cell; break; case MLArray.mxDOUBLE_CLASS: mlArray = new MLDouble(name, dims, type, attributes); //read real tag = new ISMatTag(buf); tag.readToByteBuffer(((MLNumericArray) mlArray).getRealByteBuffer(), (MLNumericArray) mlArray); //read complex if (mlArray.isComplex()) { tag = new ISMatTag(buf); tag.readToByteBuffer(((MLNumericArray) mlArray).getImaginaryByteBuffer(), (MLNumericArray) mlArray); } break; case MLArray.mxSINGLE_CLASS: mlArray = new MLSingle(name, dims, type, attributes); //read real tag = new ISMatTag(buf); tag.readToByteBuffer(((MLNumericArray) mlArray).getRealByteBuffer(), (MLNumericArray) mlArray); //read complex if (mlArray.isComplex()) { tag = new ISMatTag(buf); tag.readToByteBuffer(((MLNumericArray) mlArray).getImaginaryByteBuffer(), (MLNumericArray) mlArray); } break; case MLArray.mxUINT8_CLASS: mlArray = new MLUInt8(name, dims, type, attributes); //read real tag = new ISMatTag(buf); tag.readToByteBuffer(((MLNumericArray) mlArray).getRealByteBuffer(), (MLNumericArray) mlArray); //read complex if (mlArray.isComplex()) { tag = new ISMatTag(buf); tag.readToByteBuffer(((MLNumericArray) mlArray).getImaginaryByteBuffer(), (MLNumericArray) mlArray); } break; case MLArray.mxINT8_CLASS: mlArray = new MLInt8(name, dims, type, attributes); //read real tag = new ISMatTag(buf); tag.readToByteBuffer(((MLNumericArray) mlArray).getRealByteBuffer(), (MLNumericArray) mlArray); //read complex if (mlArray.isComplex()) { tag = new ISMatTag(buf); tag.readToByteBuffer(((MLNumericArray) mlArray).getImaginaryByteBuffer(), (MLNumericArray) mlArray); } break; case MLArray.mxINT16_CLASS: mlArray = new MLInt16(name, dims, type, attributes); //read real tag = new ISMatTag(buf); tag.readToByteBuffer(((MLNumericArray) mlArray).getRealByteBuffer(), (MLNumericArray) mlArray); //read complex if (mlArray.isComplex()) { tag = new ISMatTag(buf); tag.readToByteBuffer(((MLNumericArray) mlArray).getImaginaryByteBuffer(), (MLNumericArray) mlArray); } break; case MLArray.mxUINT16_CLASS: mlArray = new MLUInt16(name, dims, type, attributes); //read real tag = new ISMatTag(buf); tag.readToByteBuffer(((MLNumericArray) mlArray).getRealByteBuffer(), (MLNumericArray) mlArray); //read complex if (mlArray.isComplex()) { tag = new ISMatTag(buf); tag.readToByteBuffer(((MLNumericArray) mlArray).getImaginaryByteBuffer(), (MLNumericArray) mlArray); } //System.out.println( new String( buf.array() ) ); break; case MLArray.mxINT32_CLASS: mlArray = new MLInt32(name, dims, type, attributes); //read real tag = new ISMatTag(buf); tag.readToByteBuffer(((MLNumericArray) mlArray).getRealByteBuffer(), (MLNumericArray) mlArray); //read complex if (mlArray.isComplex()) { tag = new ISMatTag(buf); tag.readToByteBuffer(((MLNumericArray) mlArray).getImaginaryByteBuffer(), (MLNumericArray) mlArray); } break; case MLArray.mxUINT32_CLASS: mlArray = new MLUInt32(name, dims, type, attributes); //read real tag = new ISMatTag(buf); tag.readToByteBuffer(((MLNumericArray) mlArray).getRealByteBuffer(), (MLNumericArray) mlArray); //read complex if (mlArray.isComplex()) { tag = new ISMatTag(buf); tag.readToByteBuffer(((MLNumericArray) mlArray).getImaginaryByteBuffer(), (MLNumericArray) mlArray); } break; case MLArray.mxINT64_CLASS: mlArray = new MLInt64(name, dims, type, attributes); //read real tag = new ISMatTag(buf); tag.readToByteBuffer(((MLNumericArray) mlArray).getRealByteBuffer(), (MLNumericArray) mlArray); //read complex if (mlArray.isComplex()) { tag = new ISMatTag(buf); tag.readToByteBuffer(((MLNumericArray) mlArray).getImaginaryByteBuffer(), (MLNumericArray) mlArray); } break; case MLArray.mxUINT64_CLASS: mlArray = new MLUInt64(name, dims, type, attributes); //read real tag = new ISMatTag(buf); tag.readToByteBuffer(((MLNumericArray) mlArray).getRealByteBuffer(), (MLNumericArray) mlArray); //read complex if (mlArray.isComplex()) { tag = new ISMatTag(buf); tag.readToByteBuffer(((MLNumericArray) mlArray).getImaginaryByteBuffer(), (MLNumericArray) mlArray); } break; case MLArray.mxCHAR_CLASS: MLChar mlchar = new MLChar(name, dims, type, attributes); //read real tag = new ISMatTag(buf); // char[] ac = tag.readToCharArray(); String str = tag.readToString(); for (int i = 0; i < str.length(); i++) { mlchar.setChar(str.charAt(i), i); } mlArray = mlchar; break; case MLArray.mxSPARSE_CLASS: MLSparse sparse = new MLSparse(name, dims, attributes, nzmax); //read ir (row indices) tag = new ISMatTag(buf); int[] ir = tag.readToIntArray(); //read jc (column count) tag = new ISMatTag(buf); int[] jc = tag.readToIntArray(); //read pr (real part) tag = new ISMatTag(buf); double[] ad1 = tag.readToDoubleArray(); int count = 0; for (int column = 0; column < sparse.getN(); column++) { while (count < jc[column + 1]) { sparse.setReal(ad1[count], ir[count], column); count++; } } //read pi (imaginary part) if (sparse.isComplex()) { tag = new ISMatTag(buf); double[] ad2 = tag.readToDoubleArray(); count = 0; for (int column = 0; column < sparse.getN(); column++) { while (count < jc[column + 1]) { sparse.setImaginary(ad2[count], ir[count], column); count++; } } } mlArray = sparse; break; case MLArray.mxOPAQUE_CLASS: //read class name tag = new ISMatTag(buf); // class name String className = tag.readToString(); // System.out.println( "Class name: " + className ); // should be "java" // System.out.println( "Array name: " + name ); // the stored array name // read array name stored in dims (!) byte[] nn = new byte[dims.length]; for (int i = 0; i < dims.length; i++) { nn[i] = (byte) dims[i]; } String arrName = new String(nn, MatDataTypes.CHARSET); //System.out.println( "Array name: " + arrName ); // next tag should be miMatrix ISMatTag contentTag = new ISMatTag(buf); if (contentTag.type == MatDataTypes.miMATRIX) { //should return UInt8 or UInt32, but MLNumericArray is the LCD MLArray wrappedContent = readMatrix(buf, false); //our first job is to find the binary content MLNumericArray binaryContent = null; if (wrappedContent instanceof MLCell) { //sometimes we'll get a cell array //in that case, we'll take the first NumericArray we can find MLCell cellContent = (MLCell) wrappedContent; for (MLArray candidate : cellContent.cells()) { if (candidate instanceof MLNumericArray) { binaryContent = (MLNumericArray) candidate; break; } } } else if (wrappedContent instanceof MLNumericArray) { binaryContent = (MLNumericArray) wrappedContent; } else if (wrappedContent instanceof MLStructure) { MLStructure structureContent = (MLStructure) wrappedContent; MLCell cellContent = (MLCell) structureContent.getField("Values", 0); binaryContent = (MLNumericArray) cellContent.get(0); } else { throw new IOException("Unexpected array type: " + wrappedContent.name); } mlArray = new MLJavaObject(arrName, className, binaryContent); } else { throw new IOException("Unexpected java object content"); } break; case MLArray.mxOBJECT_CLASS: //read class name tag = new ISMatTag(buf); // class name className = tag.readToString(); // TODO: currently copy pasted from structure struct = new MLStructure(name, dims, type, attributes); //field name lenght - this subelement always uses the compressed data element format new ISMatTag(buf); maxlen = buf.getInt(); //maximum field length ////// read fields data as Int8 tag = new ISMatTag(buf); //calculate number of fields numOfFields = tag.size / maxlen; fieldNames = new String[numOfFields]; for (int i = 0; i < numOfFields; i++) { byte[] names = new byte[maxlen]; buf.get(names); fieldNames[i] = zeroEndByteArrayToString(names); } buf.position(buf.position() + tag.padding); //read fields for (int index = 0; index < 1; index++) { for (int i = 0; i < numOfFields; i++) { //read matrix recursively tag = new ISMatTag(buf); if (tag.size > 0) { MLArray fieldValue = readMatrix(buf, false); struct.setField(fieldNames[i], fieldValue, index); } else { struct.setField(fieldNames[i], new MLEmptyArray(), index); } } } mlArray = new MLObject(name, className, struct); break; default: throw new MatlabIOException("Incorrect matlab array class: " + MLArray.typeToString(type)); } return mlArray; } /** * Converts byte array to String. * * It assumes that String ends with \0 value. * * @param bytes byte array containing the string. * @return String retrieved from byte array. * @throws IOException if reading error occurred. */ private String zeroEndByteArrayToString(byte[] bytes) throws IOException { int i = 0; for (i = 0; i < bytes.length && bytes[i] != 0; i++) ; return new String(bytes, 0, i, MatDataTypes.CHARSET); } /** * Reads Matrix flags. * * Modifies buf position. * * @param buf ByteBuffer * @return flags int array * @throws IOException if reading from buffer fails */ private int[] readFlags(ByteBuffer buf) throws IOException { ISMatTag tag = new ISMatTag(buf); int[] flags = tag.readToIntArray(); return flags; } /** * Reads Matrix dimensions. * * Modifies buf position. * * @param buf ByteBuffer * @return dimensions int array * @throws IOException if reading from buffer fails */ private int[] readDimension(ByteBuffer buf) throws IOException { ISMatTag tag = new ISMatTag(buf); int[] dims = tag.readToIntArray(); return dims; } /** * Reads Matrix name. * * Modifies buf position. * * @param buf ByteBuffer * @return name String * @throws IOException if reading from buffer fails */ private String readName(ByteBuffer buf) throws IOException { ISMatTag tag = new ISMatTag(buf); return tag.readToString(); } /** * Reads MAT-file header. * * Modifies buf position. * * @param buf * ByteBuffer * @throws IOException * if reading from buffer fails or if this is not a valid * MAT-file */ void readHeader(ByteBuffer buf) throws IOException { //header values String description; int version; byte[] endianIndicator = new byte[2]; //descriptive text 116 bytes byte[] descriptionBuffer = new byte[116]; buf.get(descriptionBuffer); description = zeroEndByteArrayToString(descriptionBuffer); if (!description.matches("MATLAB 5.0 MAT-file.*")) { throw new MatlabIOException("This is not a valid MATLAB 5.0 MAT-file."); } //subsyst data offset 8 bytes buf.position(buf.position() + 8); byte[] bversion = new byte[2]; //version 2 bytes buf.get(bversion); //endian indicator 2 bytes buf.get(endianIndicator); //program reading the MAT-file must perform byte swapping to interpret the data //in the MAT-file correctly if ((char) endianIndicator[0] == 'I' && (char) endianIndicator[1] == 'M') { byteOrder = ByteOrder.LITTLE_ENDIAN; version = bversion[1] & 0xff | bversion[0] << 8; } else { byteOrder = ByteOrder.BIG_ENDIAN; version = bversion[0] & 0xff | bversion[1] << 8; } buf.order(byteOrder); matFileHeader = new MatFileHeader(description, version, endianIndicator); } /** * TAG operator. Facilitates reading operations. * * Note: reading from buffer modifies it's position * * @author Wojciech Gradkowski ([email protected]) */ private static class ISMatTag extends MatTag { private final MatFileInputStream mfis; private final int padding; private final boolean compressed; public ISMatTag(ByteBuffer buf) throws IOException { //must call parent constructor super(0, 0); int tmp = buf.getInt(); //data not packed in the tag if (tmp >> 16 == 0) { type = tmp; size = buf.getInt(); compressed = false; } else //data _packed_ in the tag (compressed) { size = tmp >> 16; // 2 more significant bytes type = tmp & 0xffff; // 2 less significant bytes; compressed = true; } padding = getPadding(size, compressed); mfis = new MatFileInputStream(buf, type); } public void readToByteBuffer(ByteBuffer buff, ByteStorageSupport storage) throws IOException { int elements = size / sizeOf(); mfis.readToByteBuffer(buff, elements, storage); mfis.skip(padding); } public byte[] readToByteArray() throws IOException { //allocate memory for array elements int elements = size / sizeOf(); byte[] ab = new byte[elements]; for (int i = 0; i < elements; i++) { ab[i] = mfis.readByte(); } //skip padding mfis.skip(padding); return ab; } public double[] readToDoubleArray() throws IOException { //allocate memory for array elements int elements = size / sizeOf(); double[] ad = new double[elements]; for (int i = 0; i < elements; i++) { ad[i] = mfis.readDouble(); } //skip padding mfis.skip(padding); return ad; } public int[] readToIntArray() throws IOException { //allocate memory for array elements int elements = size / sizeOf(); int[] ai = new int[elements]; for (int i = 0; i < elements; i++) { ai[i] = mfis.readInt(); } //skip padding mfis.skip(padding); return ai; } public String readToString() throws IOException { // byte[] bytes = readToByteArray(); return new String(bytes, "UTF-8"); } } }