diff --git a/src/Data/Matlab/Matlab.csproj b/src/Data/Matlab/Matlab.csproj index e612944b..edbce808 100644 --- a/src/Data/Matlab/Matlab.csproj +++ b/src/Data/Matlab/Matlab.csproj @@ -51,6 +51,7 @@ + diff --git a/src/Data/Matlab/MatlabFile.cs b/src/Data/Matlab/MatlabFile.cs index 202c42fe..bce0f85c 100644 --- a/src/Data/Matlab/MatlabFile.cs +++ b/src/Data/Matlab/MatlabFile.cs @@ -42,7 +42,7 @@ namespace MathNet.Numerics.Data.Matlab /// /// Matrices in a MATLAB file stored as 1-D arrays /// - readonly IDictionary _matrices = new SortedList(); + readonly IDictionary _matrices = new SortedList(); /// /// Gets or sets the header text. @@ -61,7 +61,7 @@ namespace MathNet.Numerics.Data.Matlab get { return _matrices.Keys; } } - internal void Add(string name, byte[] data) + internal void Add(string name, MatlabMatrix data) { if (FirstMatrixName == null) { @@ -73,7 +73,7 @@ namespace MathNet.Numerics.Data.Matlab internal Matrix ReadMatrix(string name) where TDataType : struct, IEquatable, IFormattable { - return Parser.ReadMatrix(_matrices[name]); + return _matrices[name].Read(); } } } diff --git a/src/Data/Matlab/MatlabMatrix.cs b/src/Data/Matlab/MatlabMatrix.cs new file mode 100644 index 00000000..d10d9b12 --- /dev/null +++ b/src/Data/Matlab/MatlabMatrix.cs @@ -0,0 +1,59 @@ +// +// Math.NET Numerics, part of the Math.NET Project +// http://numerics.mathdotnet.com +// http://github.com/mathnet/mathnet-numerics +// http://mathnetnumerics.codeplex.com +// +// Copyright (c) 2009-2014 Math.NET +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using MathNet.Numerics.LinearAlgebra; + +namespace MathNet.Numerics.Data.Matlab +{ + public class MatlabMatrix + { + byte[] _data; + + public string Name { get; private set; } + public int Size { get; private set; } + public int Dimensions { get; private set; } + + internal MatlabMatrix(string name, int size, int dimensions, byte[] data) + { + _data = data; + + Name = name; + Size = size; + Dimensions = dimensions; + } + + internal Matrix Read() + where TDataType : struct, IEquatable, IFormattable + { + return Parser.ReadMatrixBlock(_data); + } + } +} diff --git a/src/Data/Matlab/MatlabReader.cs b/src/Data/Matlab/MatlabReader.cs index f78327da..bca107d3 100644 --- a/src/Data/Matlab/MatlabReader.cs +++ b/src/Data/Matlab/MatlabReader.cs @@ -47,19 +47,20 @@ namespace MathNet.Numerics.Data.Matlab { var names = string.IsNullOrEmpty(matrixName) ? new string[] { } : new[] { matrixName }; var parser = new Parser(stream, names); - var file = parser.Parse(); + var file = parser.ParseAll(); if (string.IsNullOrEmpty(matrixName)) { - return file.ReadMatrix(file.FirstMatrixName); + return file.First().Read(); } - if (!file.MatrixNames.Contains(matrixName)) + var matrix = file.Find(m => m.Name == matrixName); + if (matrix == null) { throw new KeyNotFoundException("Matrix with the provided name was not found."); } - return file.ReadMatrix(matrixName); + return matrix.Read(); } /// The data type of the Matrix. It can be either: double, float, Complex, or Complex32. @@ -76,9 +77,10 @@ namespace MathNet.Numerics.Data.Matlab public static Dictionary> ReadMatrices(Stream stream, params string[] matrixNames) where TDataType : struct, IEquatable, IFormattable { + var names = new HashSet(matrixNames); var parser = new Parser(stream, matrixNames); - var file = parser.Parse(); - return file.MatrixNames.ToDictionary(name => name, file.ReadMatrix); + var file = parser.ParseAll(); + return file.Where(m => names.Count == 0 || names.Contains(m.Name)).ToDictionary(m => m.Name, m => m.Read()); } /// The data type of the Matrix. It can be either: double, float, Complex, or Complex32. diff --git a/src/Data/Matlab/Parser.cs b/src/Data/Matlab/Parser.cs index d55ca6af..7414481a 100644 --- a/src/Data/Matlab/Parser.cs +++ b/src/Data/Matlab/Parser.cs @@ -133,20 +133,15 @@ namespace MathNet.Numerics.Data.Matlab } /// - /// Parses the file. + /// Extracts all matrix blocks in a format we support. /// - /// The parsed MATLAB file as a object. - internal MatlabFile Parse() + internal List ParseAll() { - var file = new MatlabFile(); + var matrices = new List(); using (var reader = new BinaryReader(_stream)) { - file.HeaderText = Encoding.ASCII.GetString(reader.ReadBytes(116)); - - // skipping subsystem offsets reader.BaseStream.Position = 126; - if (reader.ReadByte() != LittleEndianIndicator) { throw new NotSupportedException(Resources.BigEndianNotSupported); @@ -182,16 +177,32 @@ namespace MathNet.Numerics.Data.Matlab if (type == DataType.Matrix) { - AddMatrix(data, file); - } - else - { - throw new NotSupportedException(string.Format(Resources.NotSupportedType, type)); + using (var matrixStream = new MemoryStream(data)) + using (var matrixReader = new BinaryReader(matrixStream)) + { + matrixReader.BaseStream.Seek(20, SeekOrigin.Current); + var matrixDim = matrixReader.ReadInt32()/8; + if (matrixDim > 2) + { + continue; + } + + matrixReader.BaseStream.Seek(10, SeekOrigin.Current); + int matrixSize = matrixReader.ReadInt16(); + if (matrixSize == 0) + { + matrixSize = matrixReader.ReadInt32(); + } + + var matrixName = Encoding.ASCII.GetString(matrixReader.ReadBytes(matrixSize)); + + matrices.Add(new MatlabMatrix(matrixName, matrixSize, matrixDim, data)); + } } } } - return file; + return matrices; } /// @@ -240,45 +251,7 @@ namespace MathNet.Numerics.Data.Matlab return data; } - /// - /// Adds a matrix from the actual file into our presentation of a MATLAB file. - /// - /// The data of the matrix. - /// The instance. - void AddMatrix(byte[] data, MatlabFile file) - { - using (var ms = new MemoryStream(data)) - using (var reader = new BinaryReader(ms)) - { - // skip unneeded bytes - reader.BaseStream.Seek(20, SeekOrigin.Current); - - var numDimensions = reader.ReadInt32()/8; - if (numDimensions > 2) - { - throw new NotSupportedException(Resources.MoreThan2D); - } - - // skip unneeded bytes - reader.BaseStream.Seek(10, SeekOrigin.Current); - - int size = reader.ReadInt16(); - if (size == 0) - { - size = reader.ReadInt32(); - } - - var name = Encoding.ASCII.GetString(reader.ReadBytes(size)); - - // only grab wanted objects - if (_names.Count == 0 || _names.Contains(name)) - { - file.Add(name, data); - } - } - } - - internal static Matrix ReadMatrix(byte[] data) + internal static Matrix ReadMatrixBlock(byte[] data) { using (var stream = new MemoryStream(data)) using (var reader = new BinaryReader(stream)) @@ -302,7 +275,7 @@ namespace MathNet.Numerics.Data.Matlab var rows = reader.ReadInt32(); var columns = reader.ReadInt32(); - // skip unneeded bytes + // skip name and unneeded bytes reader.BaseStream.Seek(2, SeekOrigin.Current); int size = reader.ReadInt16(); var smallBlock = true; @@ -311,8 +284,7 @@ namespace MathNet.Numerics.Data.Matlab size = reader.ReadInt32(); smallBlock = false; } - - var name = Encoding.ASCII.GetString(reader.ReadBytes(size)); + reader.BaseStream.Seek(size, SeekOrigin.Current); AlignData(reader.BaseStream, size, smallBlock); var type = (DataType)reader.ReadInt16();