From 126d8a748359bd49cba094d349bb6098444f383a Mon Sep 17 00:00:00 2001 From: Alexander Luzgarev Date: Sun, 14 Oct 2018 13:57:57 +0200 Subject: [PATCH 1/3] Update copyright text --- MatFileHandler.Tests/AbstractTestDataFactory.cs | 2 +- MatFileHandler.Tests/ArrayHandlingTests.cs | 2 +- MatFileHandler.Tests/CommonData.cs | 2 +- MatFileHandler.Tests/ComplexOfTests.cs | 2 +- MatFileHandler.Tests/ExtensionTestFilenameConvention.cs | 2 +- MatFileHandler.Tests/ITestFilenameConvention.cs | 2 +- MatFileHandler.Tests/MatFileHandler.Tests.csproj | 5 +++++ MatFileHandler.Tests/MatFileReaderTests.cs | 2 +- MatFileHandler.Tests/MatFileWriterTests.cs | 2 +- MatFileHandler.Tests/MatTestDataFactory.cs | 2 +- MatFileHandler/ArrayFlags.cs | 2 +- MatFileHandler/ComplexOf.cs | 2 +- MatFileHandler/CompressionUsage.cs | 2 +- MatFileHandler/DataBuilder.cs | 2 +- MatFileHandler/DataElement.cs | 2 +- MatFileHandler/DataElementConverter.cs | 2 +- MatFileHandler/DataElementReader.cs | 2 +- MatFileHandler/DataExtraction.cs | 2 +- MatFileHandler/DataTypeExtensions.cs | 2 +- MatFileHandler/DimensionCalculator.cs | 2 +- MatFileHandler/HandlerException.cs | 2 +- MatFileHandler/Header.cs | 2 +- MatFileHandler/IArray.cs | 2 +- MatFileHandler/IArrayOf.cs | 2 +- MatFileHandler/ICellArray.cs | 2 +- MatFileHandler/ICharArray.cs | 2 +- MatFileHandler/IMatFile.cs | 2 +- MatFileHandler/ISparseArrayOf.cs | 2 +- MatFileHandler/IStructureArray.cs | 2 +- MatFileHandler/IVariable.cs | 2 +- MatFileHandler/MatArray.cs | 2 +- MatFileHandler/MatCellArray.cs | 2 +- MatFileHandler/MatCharArrayOf.cs | 2 +- MatFileHandler/MatFile.cs | 2 +- MatFileHandler/MatFileReader.cs | 2 +- MatFileHandler/MatFileWriter.cs | 2 +- MatFileHandler/MatFileWriterOptions.cs | 2 +- MatFileHandler/MatNumericalArrayOf.cs | 2 +- MatFileHandler/MatSparseArrayOf.cs | 2 +- MatFileHandler/MatStructureArray.cs | 2 +- MatFileHandler/MatVariable.cs | 2 +- MatFileHandler/MiNum.cs | 2 +- MatFileHandler/Tag.cs | 2 +- stylecop.json | 2 +- 44 files changed, 48 insertions(+), 43 deletions(-) diff --git a/MatFileHandler.Tests/AbstractTestDataFactory.cs b/MatFileHandler.Tests/AbstractTestDataFactory.cs index ad7afdf..082c81d 100755 --- a/MatFileHandler.Tests/AbstractTestDataFactory.cs +++ b/MatFileHandler.Tests/AbstractTestDataFactory.cs @@ -1,4 +1,4 @@ -// Copyright 2017 Alexander Luzgarev +// Copyright 2017-2018 Alexander Luzgarev using System.Collections.Generic; using System.IO; diff --git a/MatFileHandler.Tests/ArrayHandlingTests.cs b/MatFileHandler.Tests/ArrayHandlingTests.cs index 97fa5c9..0264286 100755 --- a/MatFileHandler.Tests/ArrayHandlingTests.cs +++ b/MatFileHandler.Tests/ArrayHandlingTests.cs @@ -1,4 +1,4 @@ -// Copyright 2017 Alexander Luzgarev +// Copyright 2017-2018 Alexander Luzgarev using System.Collections.Generic; using System.Numerics; diff --git a/MatFileHandler.Tests/CommonData.cs b/MatFileHandler.Tests/CommonData.cs index 5e2e32b..5bf722d 100755 --- a/MatFileHandler.Tests/CommonData.cs +++ b/MatFileHandler.Tests/CommonData.cs @@ -1,4 +1,4 @@ -// Copyright 2017 Alexander Luzgarev +// Copyright 2017-2018 Alexander Luzgarev namespace MatFileHandler.Tests { diff --git a/MatFileHandler.Tests/ComplexOfTests.cs b/MatFileHandler.Tests/ComplexOfTests.cs index 17f59fb..d25305d 100755 --- a/MatFileHandler.Tests/ComplexOfTests.cs +++ b/MatFileHandler.Tests/ComplexOfTests.cs @@ -1,4 +1,4 @@ -// Copyright 2017 Alexander Luzgarev +// Copyright 2017-2018 Alexander Luzgarev using NUnit.Framework; diff --git a/MatFileHandler.Tests/ExtensionTestFilenameConvention.cs b/MatFileHandler.Tests/ExtensionTestFilenameConvention.cs index 6cd1a4e..08ebd80 100755 --- a/MatFileHandler.Tests/ExtensionTestFilenameConvention.cs +++ b/MatFileHandler.Tests/ExtensionTestFilenameConvention.cs @@ -1,4 +1,4 @@ -// Copyright 2017 Alexander Luzgarev +// Copyright 2017-2018 Alexander Luzgarev using System.IO; diff --git a/MatFileHandler.Tests/ITestFilenameConvention.cs b/MatFileHandler.Tests/ITestFilenameConvention.cs index 1668d01..5f31cbe 100755 --- a/MatFileHandler.Tests/ITestFilenameConvention.cs +++ b/MatFileHandler.Tests/ITestFilenameConvention.cs @@ -1,4 +1,4 @@ -// Copyright 2017 Alexander Luzgarev +// Copyright 2017-2018 Alexander Luzgarev namespace MatFileHandler.Tests { diff --git a/MatFileHandler.Tests/MatFileHandler.Tests.csproj b/MatFileHandler.Tests/MatFileHandler.Tests.csproj index 6b2cc62..1d96afa 100755 --- a/MatFileHandler.Tests/MatFileHandler.Tests.csproj +++ b/MatFileHandler.Tests/MatFileHandler.Tests.csproj @@ -24,4 +24,9 @@ + + + All + + \ No newline at end of file diff --git a/MatFileHandler.Tests/MatFileReaderTests.cs b/MatFileHandler.Tests/MatFileReaderTests.cs index 3245736..68bad4c 100755 --- a/MatFileHandler.Tests/MatFileReaderTests.cs +++ b/MatFileHandler.Tests/MatFileReaderTests.cs @@ -1,4 +1,4 @@ -// Copyright 2017 Alexander Luzgarev +// Copyright 2017-2018 Alexander Luzgarev using System.IO; using System.Linq; diff --git a/MatFileHandler.Tests/MatFileWriterTests.cs b/MatFileHandler.Tests/MatFileWriterTests.cs index 63905ed..335220a 100755 --- a/MatFileHandler.Tests/MatFileWriterTests.cs +++ b/MatFileHandler.Tests/MatFileWriterTests.cs @@ -1,4 +1,4 @@ -// Copyright 2017 Alexander Luzgarev +// Copyright 2017-2018 Alexander Luzgarev using System; using System.IO; diff --git a/MatFileHandler.Tests/MatTestDataFactory.cs b/MatFileHandler.Tests/MatTestDataFactory.cs index f343c79..ac6935d 100755 --- a/MatFileHandler.Tests/MatTestDataFactory.cs +++ b/MatFileHandler.Tests/MatTestDataFactory.cs @@ -1,4 +1,4 @@ -// Copyright 2017 Alexander Luzgarev +// Copyright 2017-2018 Alexander Luzgarev using System.IO; diff --git a/MatFileHandler/ArrayFlags.cs b/MatFileHandler/ArrayFlags.cs index 3b5ef5b..c5700f7 100755 --- a/MatFileHandler/ArrayFlags.cs +++ b/MatFileHandler/ArrayFlags.cs @@ -1,4 +1,4 @@ -// Copyright 2017 Alexander Luzgarev +// Copyright 2017-2018 Alexander Luzgarev using System; diff --git a/MatFileHandler/ComplexOf.cs b/MatFileHandler/ComplexOf.cs index b576fff..cbf7317 100755 --- a/MatFileHandler/ComplexOf.cs +++ b/MatFileHandler/ComplexOf.cs @@ -1,4 +1,4 @@ -// Copyright 2017 Alexander Luzgarev +// Copyright 2017-2018 Alexander Luzgarev using System; diff --git a/MatFileHandler/CompressionUsage.cs b/MatFileHandler/CompressionUsage.cs index 2d0fd93..719084a 100644 --- a/MatFileHandler/CompressionUsage.cs +++ b/MatFileHandler/CompressionUsage.cs @@ -1,4 +1,4 @@ -// Copyright 2017 Alexander Luzgarev +// Copyright 2017-2018 Alexander Luzgarev namespace MatFileHandler { diff --git a/MatFileHandler/DataBuilder.cs b/MatFileHandler/DataBuilder.cs index ad34d54..88042b1 100755 --- a/MatFileHandler/DataBuilder.cs +++ b/MatFileHandler/DataBuilder.cs @@ -1,4 +1,4 @@ -// Copyright 2017 Alexander Luzgarev +// Copyright 2017-2018 Alexander Luzgarev using System; using System.Collections.Generic; diff --git a/MatFileHandler/DataElement.cs b/MatFileHandler/DataElement.cs index ff8d08d..1da50e8 100755 --- a/MatFileHandler/DataElement.cs +++ b/MatFileHandler/DataElement.cs @@ -1,4 +1,4 @@ -// Copyright 2017 Alexander Luzgarev +// Copyright 2017-2018 Alexander Luzgarev namespace MatFileHandler { diff --git a/MatFileHandler/DataElementConverter.cs b/MatFileHandler/DataElementConverter.cs index 19652c5..1a1e252 100755 --- a/MatFileHandler/DataElementConverter.cs +++ b/MatFileHandler/DataElementConverter.cs @@ -1,4 +1,4 @@ -// Copyright 2017 Alexander Luzgarev +// Copyright 2017-2018 Alexander Luzgarev using System; using System.Collections.Generic; diff --git a/MatFileHandler/DataElementReader.cs b/MatFileHandler/DataElementReader.cs index e5f5a03..3c1021e 100755 --- a/MatFileHandler/DataElementReader.cs +++ b/MatFileHandler/DataElementReader.cs @@ -1,4 +1,4 @@ -// Copyright 2017 Alexander Luzgarev +// Copyright 2017-2018 Alexander Luzgarev using System; using System.Collections.Generic; diff --git a/MatFileHandler/DataExtraction.cs b/MatFileHandler/DataExtraction.cs index 8bfba9f..d5d9edd 100755 --- a/MatFileHandler/DataExtraction.cs +++ b/MatFileHandler/DataExtraction.cs @@ -1,4 +1,4 @@ -// Copyright 2017 Alexander Luzgarev +// Copyright 2017-2018 Alexander Luzgarev using System; using System.Collections.Generic; diff --git a/MatFileHandler/DataTypeExtensions.cs b/MatFileHandler/DataTypeExtensions.cs index 7c4bfa3..2b923b4 100755 --- a/MatFileHandler/DataTypeExtensions.cs +++ b/MatFileHandler/DataTypeExtensions.cs @@ -1,4 +1,4 @@ -// Copyright 2017 Alexander Luzgarev +// Copyright 2017-2018 Alexander Luzgarev using System; diff --git a/MatFileHandler/DimensionCalculator.cs b/MatFileHandler/DimensionCalculator.cs index c1de420..a13acff 100755 --- a/MatFileHandler/DimensionCalculator.cs +++ b/MatFileHandler/DimensionCalculator.cs @@ -1,4 +1,4 @@ -// Copyright 2017 Alexander Luzgarev +// Copyright 2017-2018 Alexander Luzgarev using System.Linq; diff --git a/MatFileHandler/HandlerException.cs b/MatFileHandler/HandlerException.cs index 43213a0..38ec92b 100755 --- a/MatFileHandler/HandlerException.cs +++ b/MatFileHandler/HandlerException.cs @@ -1,4 +1,4 @@ -// Copyright 2017 Alexander Luzgarev +// Copyright 2017-2018 Alexander Luzgarev using System; diff --git a/MatFileHandler/Header.cs b/MatFileHandler/Header.cs index f2ade66..56034d4 100755 --- a/MatFileHandler/Header.cs +++ b/MatFileHandler/Header.cs @@ -1,4 +1,4 @@ -// Copyright 2017 Alexander Luzgarev +// Copyright 2017-2018 Alexander Luzgarev using System; using System.Globalization; diff --git a/MatFileHandler/IArray.cs b/MatFileHandler/IArray.cs index 682c8fa..adda6fb 100755 --- a/MatFileHandler/IArray.cs +++ b/MatFileHandler/IArray.cs @@ -1,4 +1,4 @@ -// Copyright 2017 Alexander Luzgarev +// Copyright 2017-2018 Alexander Luzgarev using System.Numerics; diff --git a/MatFileHandler/IArrayOf.cs b/MatFileHandler/IArrayOf.cs index 2c808b5..49f4054 100755 --- a/MatFileHandler/IArrayOf.cs +++ b/MatFileHandler/IArrayOf.cs @@ -1,4 +1,4 @@ -// Copyright 2017 Alexander Luzgarev +// Copyright 2017-2018 Alexander Luzgarev namespace MatFileHandler { diff --git a/MatFileHandler/ICellArray.cs b/MatFileHandler/ICellArray.cs index 1decb55..c18b717 100755 --- a/MatFileHandler/ICellArray.cs +++ b/MatFileHandler/ICellArray.cs @@ -1,4 +1,4 @@ -// Copyright 2017 Alexander Luzgarev +// Copyright 2017-2018 Alexander Luzgarev namespace MatFileHandler { diff --git a/MatFileHandler/ICharArray.cs b/MatFileHandler/ICharArray.cs index 42a31cf..bcd1763 100755 --- a/MatFileHandler/ICharArray.cs +++ b/MatFileHandler/ICharArray.cs @@ -1,4 +1,4 @@ -// Copyright 2017 Alexander Luzgarev +// Copyright 2017-2018 Alexander Luzgarev namespace MatFileHandler { diff --git a/MatFileHandler/IMatFile.cs b/MatFileHandler/IMatFile.cs index 010c84d..da30d72 100755 --- a/MatFileHandler/IMatFile.cs +++ b/MatFileHandler/IMatFile.cs @@ -1,4 +1,4 @@ -// Copyright 2017 Alexander Luzgarev +// Copyright 2017-2018 Alexander Luzgarev namespace MatFileHandler { diff --git a/MatFileHandler/ISparseArrayOf.cs b/MatFileHandler/ISparseArrayOf.cs index 744ee15..74c77ae 100755 --- a/MatFileHandler/ISparseArrayOf.cs +++ b/MatFileHandler/ISparseArrayOf.cs @@ -1,4 +1,4 @@ -// Copyright 2017 Alexander Luzgarev +// Copyright 2017-2018 Alexander Luzgarev using System.Collections.Generic; diff --git a/MatFileHandler/IStructureArray.cs b/MatFileHandler/IStructureArray.cs index 12a052c..27f37c3 100755 --- a/MatFileHandler/IStructureArray.cs +++ b/MatFileHandler/IStructureArray.cs @@ -1,4 +1,4 @@ -// Copyright 2017 Alexander Luzgarev +// Copyright 2017-2018 Alexander Luzgarev using System.Collections.Generic; diff --git a/MatFileHandler/IVariable.cs b/MatFileHandler/IVariable.cs index b6b54c2..b3297da 100755 --- a/MatFileHandler/IVariable.cs +++ b/MatFileHandler/IVariable.cs @@ -1,4 +1,4 @@ -// Copyright 2017 Alexander Luzgarev +// Copyright 2017-2018 Alexander Luzgarev namespace MatFileHandler { diff --git a/MatFileHandler/MatArray.cs b/MatFileHandler/MatArray.cs index 8e4d293..b35fa50 100755 --- a/MatFileHandler/MatArray.cs +++ b/MatFileHandler/MatArray.cs @@ -1,4 +1,4 @@ -// Copyright 2017 Alexander Luzgarev +// Copyright 2017-2018 Alexander Luzgarev using System.Numerics; diff --git a/MatFileHandler/MatCellArray.cs b/MatFileHandler/MatCellArray.cs index d2f7347..8ac8973 100755 --- a/MatFileHandler/MatCellArray.cs +++ b/MatFileHandler/MatCellArray.cs @@ -1,4 +1,4 @@ -// Copyright 2017 Alexander Luzgarev +// Copyright 2017-2018 Alexander Luzgarev using System.Collections.Generic; using System.Linq; diff --git a/MatFileHandler/MatCharArrayOf.cs b/MatFileHandler/MatCharArrayOf.cs index 0b8305e..5a94ea0 100755 --- a/MatFileHandler/MatCharArrayOf.cs +++ b/MatFileHandler/MatCharArrayOf.cs @@ -1,4 +1,4 @@ -// Copyright 2017 Alexander Luzgarev +// Copyright 2017-2018 Alexander Luzgarev namespace MatFileHandler { diff --git a/MatFileHandler/MatFile.cs b/MatFileHandler/MatFile.cs index e230db2..ec17aab 100755 --- a/MatFileHandler/MatFile.cs +++ b/MatFileHandler/MatFile.cs @@ -1,4 +1,4 @@ -// Copyright 2017 Alexander Luzgarev +// Copyright 2017-2018 Alexander Luzgarev using System.Collections.Generic; using System.Linq; diff --git a/MatFileHandler/MatFileReader.cs b/MatFileHandler/MatFileReader.cs index f17e8d6..7fcc8a2 100755 --- a/MatFileHandler/MatFileReader.cs +++ b/MatFileHandler/MatFileReader.cs @@ -1,4 +1,4 @@ -// Copyright 2017 Alexander Luzgarev +// Copyright 2017-2018 Alexander Luzgarev using System.Collections.Generic; using System.IO; diff --git a/MatFileHandler/MatFileWriter.cs b/MatFileHandler/MatFileWriter.cs index 33fe176..414ab39 100755 --- a/MatFileHandler/MatFileWriter.cs +++ b/MatFileHandler/MatFileWriter.cs @@ -1,4 +1,4 @@ -// Copyright 2017 Alexander Luzgarev +// Copyright 2017-2018 Alexander Luzgarev using System; using System.Collections.Generic; diff --git a/MatFileHandler/MatFileWriterOptions.cs b/MatFileHandler/MatFileWriterOptions.cs index ee35d81..07b09f2 100644 --- a/MatFileHandler/MatFileWriterOptions.cs +++ b/MatFileHandler/MatFileWriterOptions.cs @@ -1,4 +1,4 @@ -// Copyright 2017 Alexander Luzgarev +// Copyright 2017-2018 Alexander Luzgarev namespace MatFileHandler { diff --git a/MatFileHandler/MatNumericalArrayOf.cs b/MatFileHandler/MatNumericalArrayOf.cs index 0945558..80bde49 100755 --- a/MatFileHandler/MatNumericalArrayOf.cs +++ b/MatFileHandler/MatNumericalArrayOf.cs @@ -1,4 +1,4 @@ -// Copyright 2017 Alexander Luzgarev +// Copyright 2017-2018 Alexander Luzgarev using System; using System.Collections.Generic; diff --git a/MatFileHandler/MatSparseArrayOf.cs b/MatFileHandler/MatSparseArrayOf.cs index 3b2dbc9..db9fd5d 100755 --- a/MatFileHandler/MatSparseArrayOf.cs +++ b/MatFileHandler/MatSparseArrayOf.cs @@ -1,4 +1,4 @@ -// Copyright 2017 Alexander Luzgarev +// Copyright 2017-2018 Alexander Luzgarev using System; using System.Collections.Generic; diff --git a/MatFileHandler/MatStructureArray.cs b/MatFileHandler/MatStructureArray.cs index 9dac8f5..9422d06 100755 --- a/MatFileHandler/MatStructureArray.cs +++ b/MatFileHandler/MatStructureArray.cs @@ -1,4 +1,4 @@ -// Copyright 2017 Alexander Luzgarev +// Copyright 2017-2018 Alexander Luzgarev using System; using System.Collections; diff --git a/MatFileHandler/MatVariable.cs b/MatFileHandler/MatVariable.cs index f5b8ca2..233da06 100755 --- a/MatFileHandler/MatVariable.cs +++ b/MatFileHandler/MatVariable.cs @@ -1,4 +1,4 @@ -// Copyright 2017 Alexander Luzgarev +// Copyright 2017-2018 Alexander Luzgarev namespace MatFileHandler { diff --git a/MatFileHandler/MiNum.cs b/MatFileHandler/MiNum.cs index 1013033..9e4a8f1 100755 --- a/MatFileHandler/MiNum.cs +++ b/MatFileHandler/MiNum.cs @@ -1,4 +1,4 @@ -// Copyright 2017 Alexander Luzgarev +// Copyright 2017-2018 Alexander Luzgarev namespace MatFileHandler { diff --git a/MatFileHandler/Tag.cs b/MatFileHandler/Tag.cs index 5caf83d..35f9a23 100755 --- a/MatFileHandler/Tag.cs +++ b/MatFileHandler/Tag.cs @@ -1,4 +1,4 @@ -// Copyright 2017 Alexander Luzgarev +// Copyright 2017-2018 Alexander Luzgarev namespace MatFileHandler { diff --git a/stylecop.json b/stylecop.json index 2f26842..4b05396 100755 --- a/stylecop.json +++ b/stylecop.json @@ -3,7 +3,7 @@ "settings": { "documentationRules": { "companyName": "None", - "copyrightText": "Copyright 2017 Alexander Luzgarev", + "copyrightText": "Copyright 2017-2018 Alexander Luzgarev", "xmlHeader": false }, "orderingRules": { From 10aa5581526a7f92aaa2aac76acfc4c1ebb82b83 Mon Sep 17 00:00:00 2001 From: Alexander Luzgarev Date: Sun, 14 Oct 2018 14:34:05 +0200 Subject: [PATCH 2/3] Preliminary support for Matlab objects --- MatFileHandler.Tests/MatFileReaderTests.cs | 33 +- .../test-data/{bad => good}/object.mat | Bin .../test-data/good/object2.mat | Bin 0 -> 528 bytes MatFileHandler/ArrayFlags.cs | 4 +- MatFileHandler/DataElementReader.cs | 318 +++++++++++------- MatFileHandler/Header.cs | 9 +- MatFileHandler/IMatObject.cs | 34 ++ MatFileHandler/MatFileReader.cs | 68 +++- MatFileHandler/Opaque.cs | 58 ++++ MatFileHandler/OpaqueLink.cs | 169 ++++++++++ MatFileHandler/RawVariable.cs | 36 ++ MatFileHandler/SubsystemData.cs | 144 ++++++++ MatFileHandler/SubsystemDataReader.cs | 168 +++++++++ 13 files changed, 902 insertions(+), 139 deletions(-) rename MatFileHandler.Tests/test-data/{bad => good}/object.mat (100%) mode change 100755 => 100644 create mode 100644 MatFileHandler.Tests/test-data/good/object2.mat create mode 100644 MatFileHandler/IMatObject.cs create mode 100644 MatFileHandler/Opaque.cs create mode 100644 MatFileHandler/OpaqueLink.cs create mode 100644 MatFileHandler/RawVariable.cs create mode 100644 MatFileHandler/SubsystemData.cs create mode 100644 MatFileHandler/SubsystemDataReader.cs diff --git a/MatFileHandler.Tests/MatFileReaderTests.cs b/MatFileHandler.Tests/MatFileReaderTests.cs index 68bad4c..b424227 100755 --- a/MatFileHandler.Tests/MatFileReaderTests.cs +++ b/MatFileHandler.Tests/MatFileReaderTests.cs @@ -292,7 +292,38 @@ namespace MatFileHandler.Tests [Test] public void TestObject() { - Assert.That(() => GetTests("bad")["object"], Throws.TypeOf()); + var matFile = GetTests("good")["object"]; + var obj = matFile["object_"].Value as IMatObject; + Assert.IsNotNull(obj); + Assert.That(obj.ClassName, Is.EqualTo("Point")); + Assert.That(obj.FieldNames, Is.EquivalentTo(new[] { "x", "y" })); + Assert.That(obj["x", 0].ConvertToDoubleArray(), Is.EqualTo(new[] { 3.0 })); + Assert.That(obj["y", 0].ConvertToDoubleArray(), Is.EqualTo(new[] { 5.0 })); + Assert.That(obj["x", 1].ConvertToDoubleArray(), Is.EqualTo(new[] { -2.0 })); + Assert.That(obj["y", 1].ConvertToDoubleArray(), Is.EqualTo(new[] { 6.0 })); + } + + /// + /// Test reading another object. + /// + [Test] + public void TestObject2() + { + var matFile = GetTests("good")["object2"]; + var obj = matFile["object2"].Value as IMatObject; + Assert.IsNotNull(obj); + Assert.That(obj.ClassName, Is.EqualTo("Point")); + Assert.That(obj.FieldNames, Is.EquivalentTo(new[] { "x", "y" })); + Assert.That(obj["x", 0, 0].ConvertToDoubleArray(), Is.EqualTo(new[] { 3.0 })); + Assert.That(obj["y", 0, 0].ConvertToDoubleArray(), Is.EqualTo(new[] { 5.0 })); + Assert.That(obj["x", 1, 0].ConvertToDoubleArray(), Is.EqualTo(new[] { 1.0 })); + Assert.That(obj["y", 1, 0].ConvertToDoubleArray(), Is.EqualTo(new[] { 0.0 })); + Assert.That(obj["x", 0, 1].ConvertToDoubleArray(), Is.EqualTo(new[] { -2.0 })); + Assert.That(obj["y", 0, 1].ConvertToDoubleArray(), Is.EqualTo(new[] { 6.0 })); + Assert.That(obj["x", 1, 1].ConvertToDoubleArray(), Is.EqualTo(new[] { 0.0 })); + Assert.That(obj["y", 1, 1].ConvertToDoubleArray(), Is.EqualTo(new[] { 1.0 })); + Assert.That(obj[0, 1]["x"].ConvertToDoubleArray(), Is.EqualTo(new[] { -2.0 })); + Assert.That(obj[2]["x"].ConvertToDoubleArray(), Is.EqualTo(new[] { -2.0 })); } private static AbstractTestDataFactory GetTests(string factoryName) => diff --git a/MatFileHandler.Tests/test-data/bad/object.mat b/MatFileHandler.Tests/test-data/good/object.mat old mode 100755 new mode 100644 similarity index 100% rename from MatFileHandler.Tests/test-data/bad/object.mat rename to MatFileHandler.Tests/test-data/good/object.mat diff --git a/MatFileHandler.Tests/test-data/good/object2.mat b/MatFileHandler.Tests/test-data/good/object2.mat new file mode 100644 index 0000000000000000000000000000000000000000..1b074ec915fa47140a20a570efa9a16c59259658 GIT binary patch literal 528 zcmeZu4DoSvQZUssQ1EpO(M`+DN!3vZ$Vn_o%P-2cQV4Jk_w+L}(NSSQvQ7|;LGO@5SF;FlvFtku05a3U~mM5DHepvlxZ$1*)XrW+lyq?A5Gh-pkq`J5jbzvIw zg|ozzLu8itu^!rUnnfjHhM<=*vmy7SDu!kej(m^>>WnZ83=)zP7?jxf6rQCtFaWg* z9!YGzz+6st@)BiOPkz2&k_ z!d3-4eSzJn&z7`_tBQ#%5j%R^`Rn}z#~1E5*dKmgu# - /// Undocumented object (?) array type. + /// Undocumented opaque object type. /// - MxNewObject = 17, + MxOpaque = 17, } /// diff --git a/MatFileHandler/DataElementReader.cs b/MatFileHandler/DataElementReader.cs index 3c1021e..709462d 100755 --- a/MatFileHandler/DataElementReader.cs +++ b/MatFileHandler/DataElementReader.cs @@ -12,14 +12,25 @@ namespace MatFileHandler /// /// Functions for reading data elements from a .mat file. /// - internal static class DataElementReader + internal class DataElementReader { + private readonly SubsystemData subsystemData; + + /// + /// Initializes a new instance of the class. + /// + /// Reference to file's SubsystemData. + public DataElementReader(SubsystemData subsystemData) + { + this.subsystemData = subsystemData ?? throw new ArgumentNullException(nameof(subsystemData)); + } + /// /// Read a data element. /// /// Input reader. /// Data element. - public static DataElement Read(BinaryReader reader) + public DataElement Read(BinaryReader reader) { var (dataReader, tag) = ReadTag(reader); DataElement result; @@ -66,6 +77,7 @@ namespace MatFileHandler default: throw new NotSupportedException("Unknown element."); } + if (tag.Type != DataType.MiCompressed) { var position = reader.BaseStream.Position; @@ -74,9 +86,109 @@ namespace MatFileHandler reader.ReadBytes(8 - (int)(position % 8)); } } + return result; } + private static (int[] dimensions, int[] links, int classIndex) ParseOpaqueData(MatNumericalArrayOf data) + { + var nDims = data.Data[1]; + var dimensions = new int[nDims]; + var position = 2; + for (var i = 0; i < nDims; i++) + { + dimensions[i] = (int)data.Data[position]; + position++; + } + + var count = dimensions.NumberOfElements(); + var links = new int[count]; + for (var i = 0; i < count; i++) + { + links[i] = (int)data.Data[position]; + position++; + } + + var classIndex = (int)data.Data[position]; + + return (dimensions, links, classIndex); + } + + private static ArrayFlags ReadArrayFlags(DataElement element) + { + var flagData = (element as MiNum)?.Data ?? + throw new HandlerException("Unexpected type in array flags."); + var class_ = (ArrayType)(flagData[0] & 0xff); + var variableFlags = (flagData[0] >> 8) & 0x0e; + return new ArrayFlags + { + Class = class_, + Variable = (Variable)variableFlags, + }; + } + + private static DataElement ReadData(DataElement element) + { + return element; + } + + private static int[] ReadDimensionsArray(MiNum element) + { + return element.Data; + } + + private static string[] ReadFieldNames(MiNum element, int fieldNameLength) + { + var numberOfFields = element.Data.Length / fieldNameLength; + var result = new string[numberOfFields]; + for (var i = 0; i < numberOfFields; i++) + { + var list = new List(); + var position = i * fieldNameLength; + while (element.Data[position] != 0) + { + list.Add((byte)element.Data[position]); + position++; + } + + result[i] = Encoding.ASCII.GetString(list.ToArray()); + } + + return result; + } + + private static string ReadName(MiNum element) + { + return Encoding.ASCII.GetString(element.Data.Select(x => (byte)x).ToArray()); + } + + private static DataElement ReadNum(Tag tag, BinaryReader reader) + where T : struct + { + var bytes = reader.ReadBytes(tag.Length); + if (tag.Type == DataType.MiUInt8) + { + return new MiNum(bytes); + } + + var result = new T[bytes.Length / tag.ElementSize]; + Buffer.BlockCopy(bytes, 0, result, 0, bytes.Length); + return new MiNum(result); + } + + private static SparseArrayFlags ReadSparseArrayFlags(DataElement element) + { + var arrayFlags = ReadArrayFlags(element); + var flagData = (element as MiNum)?.Data ?? + throw new HandlerException("Unexpected type in sparse array flags."); + var nzMax = flagData[1]; + return new SparseArrayFlags + { + ArrayFlags = arrayFlags, + NzMax = nzMax, + }; + } + private static (BinaryReader, Tag) ReadTag(BinaryReader reader) { var type = reader.ReadInt32(); @@ -95,87 +207,66 @@ namespace MatFileHandler } } - private static ArrayFlags ReadArrayFlags(DataElement element) + private DataElement ContinueReadingCellArray( + BinaryReader reader, + ArrayFlags flags, + int[] dimensions, + string name) { - var flagData = (element as MiNum)?.Data ?? - throw new HandlerException("Unexpected type in array flags."); - var class_ = (ArrayType)(flagData[0] & 0xff); - var variableFlags = (flagData[0] >> 8) & 0x0e; - return new ArrayFlags + var numberOfElements = dimensions.NumberOfElements(); + var elements = new List(); + for (var i = 0; i < numberOfElements; i++) { - Class = class_, - Variable = (Variable)variableFlags, - }; - } - - private static SparseArrayFlags ReadSparseArrayFlags(DataElement element) - { - var arrayFlags = ReadArrayFlags(element); - var flagData = (element as MiNum)?.Data ?? - throw new HandlerException("Unexpected type in sparse array flags."); - var nzMax = flagData[1]; - return new SparseArrayFlags - { - ArrayFlags = arrayFlags, - NzMax = nzMax, - }; - } - - private static int[] ReadDimensionsArray(MiNum element) - { - return element.Data; - } - - private static DataElement ReadData(DataElement element) - { - return element; - } - - private static string ReadName(MiNum element) - { - return Encoding.ASCII.GetString(element.Data.Select(x => (byte)x).ToArray()); - } - - private static DataElement ReadNum(Tag tag, BinaryReader reader) - where T : struct - { - var bytes = reader.ReadBytes(tag.Length); - if (tag.Type == DataType.MiUInt8) - { - return new MiNum(bytes); + var element = Read(reader) as IArray; + elements.Add(element); } - var result = new T[bytes.Length / tag.ElementSize]; - Buffer.BlockCopy(bytes, 0, result, 0, bytes.Length); - return new MiNum(result); + + return new MatCellArray(flags, dimensions, name, elements); } - private static string[] ReadFieldNames(MiNum element, int fieldNameLength) + private DataElement ContinueReadingOpaque(BinaryReader reader) { - var numberOfFields = element.Data.Length / fieldNameLength; - var result = new string[numberOfFields]; - for (var i = 0; i < numberOfFields; i++) + var nameElement = Read(reader) as MiNum ?? + throw new HandlerException("Unexpected type in object name."); + var name = ReadName(nameElement); + var anotherElement = Read(reader) as MiNum ?? + throw new HandlerException("Unexpected type in object type description."); + var typeDescription = ReadName(anotherElement); + var classNameElement = Read(reader) as MiNum ?? + throw new HandlerException("Unexpected type in class name."); + var className = ReadName(classNameElement); + var dataElement = Read(reader); + var data = ReadData(dataElement); + if (data is MatNumericalArrayOf linkElement) { - var list = new List(); - var position = i * fieldNameLength; - while (element.Data[position] != 0) - { - list.Add((byte)element.Data[position]); - position++; - } - result[i] = Encoding.ASCII.GetString(list.ToArray()); + var (dimensions, links, classIndex) = ParseOpaqueData(linkElement); + return new OpaqueLink( + name, + typeDescription, + className, + dimensions, + data, + links, + classIndex, + subsystemData); + } + else + { + return new Opaque(name, typeDescription, className, new int[] { }, data); } - return result; } - private static DataElement ContinueReadingSparseArray( + private DataElement ContinueReadingSparseArray( BinaryReader reader, DataElement firstElement, int[] dimensions, string name) { var sparseArrayFlags = ReadSparseArrayFlags(firstElement); - var rowIndex = Read(reader) as MiNum ?? throw new HandlerException("Unexpected type in row indices of a sparse array."); - var columnIndex = Read(reader) as MiNum ?? throw new HandlerException("Unexpected type in column indices of a sparse array."); + var rowIndex = Read(reader) as MiNum ?? + throw new HandlerException("Unexpected type in row indices of a sparse array."); + var columnIndex = Read(reader) as MiNum ?? + throw new HandlerException("Unexpected type in column indices of a sparse array."); var data = Read(reader); if (sparseArrayFlags.ArrayFlags.Variable.HasFlag(Variable.IsLogical)) { @@ -187,6 +278,7 @@ namespace MatFileHandler columnIndex.Data, data); } + if (sparseArrayFlags.ArrayFlags.Variable.HasFlag(Variable.IsComplex)) { var imaginaryData = Read(reader); @@ -199,6 +291,7 @@ namespace MatFileHandler data, imaginaryData); } + switch (data) { case MiNum _: @@ -214,23 +307,7 @@ namespace MatFileHandler } } - private static DataElement ContinueReadingCellArray( - BinaryReader reader, - ArrayFlags flags, - int[] dimensions, - string name) - { - var numberOfElements = dimensions.NumberOfElements(); - var elements = new List(); - for (var i = 0; i < numberOfElements; i++) - { - var element = Read(reader) as IArray; - elements.Add(element); - } - return new MatCellArray(flags, dimensions, name, elements); - } - - private static DataElement ContinueReadingStructure( + private DataElement ContinueReadingStructure( BinaryReader reader, ArrayFlags flags, int[] dimensions, @@ -244,6 +321,7 @@ namespace MatFileHandler { fields[fieldName] = new List(); } + var numberOfElements = dimensions.NumberOfElements(); for (var i = 0; i < numberOfElements; i++) { @@ -253,27 +331,53 @@ namespace MatFileHandler fields[fieldName].Add(field); } } + return new MatStructureArray(flags, dimensions, name, fields); } - private static DataElement ContinueReadingNewObject() + private DataElement Read(Stream stream) { - throw new HandlerException("Cannot read objects."); + using (var reader = new BinaryReader(stream)) + { + return Read(reader); + } } - private static DataElement ReadMatrix(Tag tag, BinaryReader reader) + private DataElement ReadCompressed(Tag tag, BinaryReader reader) + { + reader.ReadBytes(2); + var compressedData = new byte[tag.Length - 6]; + reader.BaseStream.Read(compressedData, 0, tag.Length - 6); + reader.ReadBytes(4); + var resultStream = new MemoryStream(); + using (var compressedStream = new MemoryStream(compressedData)) + { + using (var stream = new DeflateStream(compressedStream, CompressionMode.Decompress, leaveOpen: true)) + { + stream.CopyTo(resultStream); + } + } + + resultStream.Position = 0; + return Read(resultStream); + } + + private DataElement ReadMatrix(Tag tag, BinaryReader reader) { if (tag.Length == 0) { return MatArray.Empty(); } + var element1 = Read(reader); var flags = ReadArrayFlags(element1); - if (flags.Class == ArrayType.MxNewObject) + if (flags.Class == ArrayType.MxOpaque) { - return ContinueReadingNewObject(); + return ContinueReadingOpaque(reader); } - var element2 = Read(reader) as MiNum ?? throw new HandlerException("Unexpected type in array dimensions data."); + + var element2 = Read(reader) as MiNum ?? + throw new HandlerException("Unexpected type in array dimensions data."); var dimensions = ReadDimensionsArray(element2); var element3 = Read(reader) as MiNum ?? throw new HandlerException("Unexpected type in array name."); var name = ReadName(element3); @@ -281,10 +385,12 @@ namespace MatFileHandler { return ContinueReadingCellArray(reader, flags, dimensions, name); } + if (flags.Class == ArrayType.MxSparse) { return ContinueReadingSparseArray(reader, element1, dimensions, name); } + var element4 = Read(reader); var data = ReadData(element4); DataElement imaginaryData = null; @@ -293,12 +399,15 @@ namespace MatFileHandler var element5 = Read(reader); imaginaryData = ReadData(element5); } + if (flags.Class == ArrayType.MxStruct) { var fieldNameLengthElement = data as MiNum ?? - throw new HandlerException("Unexpected type in structure field name length."); + throw new HandlerException( + "Unexpected type in structure field name length."); return ContinueReadingStructure(reader, flags, dimensions, name, fieldNameLengthElement.Data[0]); } + switch (flags.Class) { case ArrayType.MxChar: @@ -339,6 +448,7 @@ namespace MatFileHandler data, imaginaryData); } + return DataElementConverter.ConvertToMatNumericalArrayOf( flags, dimensions, @@ -405,31 +515,5 @@ namespace MatFileHandler throw new HandlerException("Unknown data type."); } } - - private static DataElement ReadCompressed(Tag tag, BinaryReader reader) - { - reader.ReadBytes(2); - var compressedData = new byte[tag.Length - 6]; - reader.BaseStream.Read(compressedData, 0, tag.Length - 6); - reader.ReadBytes(4); - var resultStream = new MemoryStream(); - using (var compressedStream = new MemoryStream(compressedData)) - { - using (var stream = new DeflateStream(compressedStream, CompressionMode.Decompress, leaveOpen: true)) - { - stream.CopyTo(resultStream); - } - } - resultStream.Position = 0; - return Read(resultStream); - } - - private static DataElement Read(Stream stream) - { - using (var reader = new BinaryReader(stream)) - { - return Read(reader); - } - } } } \ No newline at end of file diff --git a/MatFileHandler/Header.cs b/MatFileHandler/Header.cs index 56034d4..06b68ce 100755 --- a/MatFileHandler/Header.cs +++ b/MatFileHandler/Header.cs @@ -13,7 +13,7 @@ namespace MatFileHandler /// internal class Header { - private Header(string text, byte[] subsystemDataOffset, int version) + private Header(string text, long subsystemDataOffset, int version) { Text = text; SubsystemDataOffset = subsystemDataOffset; @@ -28,7 +28,7 @@ namespace MatFileHandler /// /// Gets subsystem data offset. /// - public byte[] SubsystemDataOffset { get; } + public long SubsystemDataOffset { get; } /// /// Gets file version. @@ -55,7 +55,7 @@ namespace MatFileHandler platform = platform.Remove(length); } var text = $"MATLAB 5.0 MAT-file, Platform: {platform}, Created on: {dateTime}{padding}"; - return new Header(text, subsystemDataOffset, 256); + return new Header(text, 0, 256); } /// @@ -67,7 +67,8 @@ namespace MatFileHandler { var textBytes = reader.ReadBytes(116); var text = System.Text.Encoding.UTF8.GetString(textBytes); - var subsystemDataOffset = reader.ReadBytes(8); + var subsystemDataOffsetBytes = reader.ReadBytes(8); + var subsystemDataOffset = BitConverter.ToInt64(subsystemDataOffsetBytes, 0); var version = reader.ReadInt16(); var endian = reader.ReadInt16(); var isLittleEndian = endian == 19785; diff --git a/MatFileHandler/IMatObject.cs b/MatFileHandler/IMatObject.cs new file mode 100644 index 0000000..faab81e --- /dev/null +++ b/MatFileHandler/IMatObject.cs @@ -0,0 +1,34 @@ +// Copyright 2017-2018 Alexander Luzgarev + +using System.Collections.Generic; + +namespace MatFileHandler +{ + /// + /// An interface to access Matlab objects (more precisely, "object arrays"). + /// This is very similar to the interface: + /// an object holds fields that you can access, and the name of its class. + /// Additionally, you can treat is as an array of dictionaries mapping + /// field names to contents of fields. + /// + public interface IMatObject : IArrayOf> + { + /// + /// Gets the name of object's class. + /// + string ClassName { get; } + + /// + /// Gets the names of object's fields. + /// + IEnumerable FieldNames { get; } + + /// + /// Access a given field of a given object in the array. + /// + /// Field name. + /// Index of the object to access. + /// The value of the field in the selected object. + IArray this[string field, params int[] list] { get; set; } + } +} diff --git a/MatFileHandler/MatFileReader.cs b/MatFileHandler/MatFileReader.cs index 7fcc8a2..5bcb135 100755 --- a/MatFileHandler/MatFileReader.cs +++ b/MatFileHandler/MatFileReader.cs @@ -1,5 +1,6 @@ // Copyright 2017-2018 Alexander Luzgarev +using System; using System.Collections.Generic; using System.IO; @@ -33,35 +34,72 @@ namespace MatFileHandler } } - private static void ReadHeader(BinaryReader reader) + /// + /// Read raw variables from a .mat file. + /// + /// Binary reader. + /// Offset to the subsystem data to use (read from the file header). + /// Raw variables read. + internal static List ReadRawVariables(BinaryReader reader, long subsystemDataOffset) { - Header.Read(reader); - } - - private static IMatFile Read(BinaryReader reader) - { - ReadHeader(reader); - var variables = new List(); + var variables = new List(); + var subsystemData = new SubsystemData(); + var dataElementReader = new DataElementReader(subsystemData); while (true) { try { - var dataElement = DataElementReader.Read(reader) as MatArray; - if (dataElement == null) + var position = reader.BaseStream.Position; + var dataElement = dataElementReader.Read(reader); + if (position == subsystemDataOffset) { - continue; + var subsystemDataElement = dataElement as IArrayOf; + subsystemData.Set(ReadSubsystemData(subsystemDataElement.Data)); + } + else + { + variables.Add(new RawVariable(position, dataElement)); } - variables.Add(new MatVariable( - dataElement, - dataElement.Name, - dataElement.Flags.Variable.HasFlag(Variable.IsGlobal))); } catch (EndOfStreamException) { break; } } + + return variables; + } + + private static IMatFile Read(BinaryReader reader) + { + var header = ReadHeader(reader); + var rawVariables = ReadRawVariables(reader, header.SubsystemDataOffset); + var variables = new List(); + foreach (var variable in rawVariables) + { + var array = variable.DataElement as MatArray; + if (array is null) + { + continue; + } + + variables.Add(new MatVariable( + array, + array.Name, + array.Flags.Variable.HasFlag(Variable.IsGlobal))); + } + return new MatFile(variables); } + + private static Header ReadHeader(BinaryReader reader) + { + return Header.Read(reader); + } + + private static SubsystemData ReadSubsystemData(byte[] bytes) + { + return SubsystemDataReader.Read(bytes); + } } } \ No newline at end of file diff --git a/MatFileHandler/Opaque.cs b/MatFileHandler/Opaque.cs new file mode 100644 index 0000000..92859ad --- /dev/null +++ b/MatFileHandler/Opaque.cs @@ -0,0 +1,58 @@ +// Copyright 2017-2018 Alexander Luzgarev + +using System; +using System.Numerics; + +namespace MatFileHandler +{ + /// + /// Matlab "opaque object" structure. + /// If this object appears in the "main" section of the .mat file, + /// it just contains a small data structure pointing to the object's + /// storage in the "subsystem data" portion of the file. + /// In this case, an instance of class + /// will be created. + /// If this object appears in the "subsystem data" part, it contains + /// the data of all opaque objects in the file, and that is what we + /// put into property. + /// + internal class Opaque : MatArray, IArray + { + /// + /// Initializes a new instance of the class. + /// + /// Name of the object. + /// Type description. + /// Class name. + /// Dimensions of the object. + /// Raw object's data. + public Opaque(string name, string typeDescription, string className, int[] dimensions, DataElement rawData) + : base(new ArrayFlags(ArrayType.MxOpaque, 0), dimensions, name) + { + TypeDescription = typeDescription ?? throw new ArgumentNullException(nameof(typeDescription)); + ClassName = className ?? throw new ArgumentNullException(nameof(className)); + RawData = rawData ?? throw new ArgumentNullException(nameof(rawData)); + } + + /// + /// Gets class name of the opaque object. + /// + public string ClassName { get; } + + /// + /// Gets raw object's data: either links to subsystem data, or actual data. + /// + public DataElement RawData { get; } + + /// + /// Gets "type description" of the opaque object. + /// + public string TypeDescription { get; } + + /// + public override Complex[] ConvertToComplexArray() => null; + + /// + public override double[] ConvertToDoubleArray() => null; + } +} \ No newline at end of file diff --git a/MatFileHandler/OpaqueLink.cs b/MatFileHandler/OpaqueLink.cs new file mode 100644 index 0000000..54779b4 --- /dev/null +++ b/MatFileHandler/OpaqueLink.cs @@ -0,0 +1,169 @@ +// Copyright 2017-2018 Alexander Luzgarev + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace MatFileHandler +{ + /// + /// Implementation of Matlab's "opaque objects" via links to subsystem data. + /// + internal class OpaqueLink : Opaque, IMatObject + { + private readonly SubsystemData subsystemData; + + /// + /// Initializes a new instance of the class. + /// + /// Name of the object. + /// Description of object's class. + /// Name of the object's class. + /// Dimensions of the object. + /// Raw data containing links to object's storage. + /// Links to object's storage. + /// Index of object's class. + /// Reference to global subsystem data. + public OpaqueLink( + string name, + string typeDescription, + string className, + int[] dimensions, + DataElement data, + int[] links, + int classIndex, + SubsystemData subsystemData) + : base(name, typeDescription, className, dimensions, data) + { + Links = links ?? throw new ArgumentNullException(nameof(links)); + ClassIndex = classIndex; + this.subsystemData = subsystemData ?? throw new ArgumentNullException(nameof(subsystemData)); + } + + /// + /// Gets index of this object's class in subsystem data class list. + /// + public int ClassIndex { get; } + + /// + public IReadOnlyDictionary[] Data => null; + + /// + public IEnumerable FieldNames => FieldNamesArray; + + /// + /// Gets links to the fields stored in subsystem data. + /// + public int[] Links { get; } + + private string[] FieldNamesArray => subsystemData.ClassInformation[ClassIndex - 1].FieldNames.ToArray(); + + /// + public IArray this[string field, params int[] list] + { + get + { + if (TryGetValue(field, out var result, list)) + { + return result; + } + + throw new IndexOutOfRangeException(); + } + set => throw new NotImplementedException(); + } + + /// + public IReadOnlyDictionary this[params int[] list] + { + get => ExtractObject(Dimensions.DimFlatten(list)); + set => throw new NotImplementedException(); + } + + private IReadOnlyDictionary ExtractObject(int i) + { + return new OpaqueObjectArrayElement(this, i); + } + + private bool TryGetValue(string field, out IArray output, params int[] list) + { + var index = Dimensions.DimFlatten(list); + var maybeFieldIndex = subsystemData.ClassInformation[ClassIndex - 1].FindField(field); + if (!(maybeFieldIndex is int fieldIndex)) + { + output = default(IArray); + return false; + } + + if (index >= subsystemData.ObjectInformation.Length) + { + output = default(IArray); + return false; + } + + var dataIndex = subsystemData.ObjectInformation[index].FieldLinks[fieldIndex + 1]; + output = subsystemData.Data[dataIndex]; + return true; + } + + /// + /// Provides access to a single object in object array. + /// + internal class OpaqueObjectArrayElement : IReadOnlyDictionary + { + private readonly int index; + private readonly OpaqueLink parent; + + /// + /// Initializes a new instance of the class. + /// + /// Parent object array. + /// Index of the object in the array. + public OpaqueObjectArrayElement(OpaqueLink parent, int index) + { + this.parent = parent ?? throw new ArgumentNullException(nameof(parent)); + this.index = index; + } + + /// + public int Count => parent.FieldNamesArray.Length; + + /// + public IEnumerable Keys => parent.FieldNames; + + /// + public IEnumerable Values => parent.FieldNames.Select(fieldName => parent[fieldName, index]); + + /// + public IArray this[string key] => parent[key, index]; + + /// + public bool ContainsKey(string key) + { + return parent.FieldNames.Contains(key); + } + + /// + public IEnumerator> GetEnumerator() + { + foreach (var field in parent.FieldNamesArray) + { + yield return new KeyValuePair(field, parent[field, index]); + } + } + + /// + public bool TryGetValue(string key, out IArray value) + { + return parent.TryGetValue(key, out value, index); + } + + /// + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + } +} \ No newline at end of file diff --git a/MatFileHandler/RawVariable.cs b/MatFileHandler/RawVariable.cs new file mode 100644 index 0000000..c384b21 --- /dev/null +++ b/MatFileHandler/RawVariable.cs @@ -0,0 +1,36 @@ +// Copyright 2017-2018 Alexander Luzgarev + +using System; + +namespace MatFileHandler +{ + /// + /// Raw variable read from the file. + /// This gives a way to deal with "subsystem data" which looks like + /// a variable and can only be detected by comparing its offset with + /// the value stored in the file's header. + /// + internal class RawVariable + { + /// + /// Initializes a new instance of the class. + /// + /// Offset of the variable in the source file. + /// Data element parsed from the file. + internal RawVariable(long offset, DataElement dataElement) + { + Offset = offset; + DataElement = dataElement ?? throw new ArgumentNullException(nameof(dataElement)); + } + + /// + /// Gets data element with the variable's contents. + /// + public DataElement DataElement { get; } + + /// + /// Gets offset of the variable in the .mat file. + /// + public long Offset { get; } + } +} \ No newline at end of file diff --git a/MatFileHandler/SubsystemData.cs b/MatFileHandler/SubsystemData.cs new file mode 100644 index 0000000..acd0338 --- /dev/null +++ b/MatFileHandler/SubsystemData.cs @@ -0,0 +1,144 @@ +// Copyright 2017-2018 Alexander Luzgarev + +using System; +using System.Collections.Generic; + +namespace MatFileHandler +{ + /// + /// "Subsystem data" of the .mat file. + /// Subsystem data stores the actual contents + /// of all the "opaque objects" in the file. + /// + internal class SubsystemData + { + /// + /// Initializes a new instance of the class. + /// Default constructor: initializes everything to null. + /// + public SubsystemData() + { + ClassInformation = null; + ObjectInformation = null; + Data = null; + } + + /// + /// Initializes a new instance of the class. + /// The actual constructor. + /// + /// Information about the classes. + /// Information about the objects. + /// Field values. + public SubsystemData(ClassInfo[] classInformation, ObjectInfo[] objectInformation, Dictionary data) + { + this.ClassInformation = + classInformation ?? throw new ArgumentNullException(nameof(classInformation)); + this.ObjectInformation = + objectInformation ?? throw new ArgumentNullException(nameof(objectInformation)); + this.Data = data ?? throw new ArgumentNullException(nameof(data)); + } + + /// + /// Gets or sets information about all the classes occurring in the file. + /// + public ClassInfo[] ClassInformation { get; set; } + + /// + /// Gets or sets the actual data: mapping of "object field" indices to their values. + /// + public IReadOnlyDictionary Data { get; set; } + + /// + /// Gets or sets information about all the objects occurring in the file. + /// + public ObjectInfo[] ObjectInformation { get; set; } + + /// + /// Initialize this object from another object. + /// This ugly hack allows us to read the opaque objects and store references to + /// the subsystem data in them before parsing the actual subsystem data (which + /// comes later in the file). + /// + /// Another subsystem data. + public void Set(SubsystemData data) + { + this.ClassInformation = data.ClassInformation; + this.ObjectInformation = data.ObjectInformation; + this.Data = data.Data; + } + + /// + /// Stores information about a class. + /// + internal class ClassInfo + { + private readonly string[] fieldNames; + + private readonly Dictionary fieldToIndex; + + /// + /// Initializes a new instance of the class. + /// + /// Class name. + /// Names of the fields. + public ClassInfo(string name, string[] fieldNames) + { + Name = name ?? throw new ArgumentNullException(nameof(name)); + this.fieldNames = fieldNames ?? throw new ArgumentNullException(nameof(fieldNames)); + fieldToIndex = new Dictionary(); + for (var i = 0; i < fieldNames.Length; i++) + { + fieldToIndex[fieldNames[i]] = i; + } + } + + /// + /// Gets names of the fields in the class. + /// + public IReadOnlyCollection FieldNames => fieldNames; + + /// + /// Gets name of the class. + /// + public string Name { get; } + + /// + /// Find a field index given its name. + /// + /// Field name. + /// Field index. + public int? FindField(string fieldName) + { + if (fieldToIndex.TryGetValue(fieldName, out var index)) + { + return index; + } + + return null; + } + } + + /// + /// Stores information about an object. + /// + internal class ObjectInfo + { + private readonly Dictionary fieldLinks; + + /// + /// Initializes a new instance of the class. + /// + /// A dictionary mapping the field indices to "field values" indices. + public ObjectInfo(Dictionary fieldLinks) + { + this.fieldLinks = fieldLinks ?? throw new ArgumentNullException(nameof(fieldLinks)); + } + + /// + /// Gets mapping between the field indices and "field values" indices. + /// + public IReadOnlyDictionary FieldLinks => fieldLinks; + } + } +} \ No newline at end of file diff --git a/MatFileHandler/SubsystemDataReader.cs b/MatFileHandler/SubsystemDataReader.cs new file mode 100644 index 0000000..d6b2d35 --- /dev/null +++ b/MatFileHandler/SubsystemDataReader.cs @@ -0,0 +1,168 @@ +// Copyright 2017-2018 Alexander Luzgarev + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace MatFileHandler +{ + /// + /// Reader for "subsystem data" in .mat files. + /// + internal class SubsystemDataReader + { + /// + /// Read subsystem data from a given byte array. + /// + /// Byte array with the data. + /// Subsystem data read. + public static SubsystemData Read(byte[] bytes) + { + List rawVariables = null; + using (var stream = new MemoryStream(bytes)) + { + using (var reader = new BinaryReader(stream)) + { + reader.ReadBytes(8); + rawVariables = MatFileReader.ReadRawVariables(reader, -1); + } + } + + // Parse subsystem data. + var mainVariable = rawVariables[0].DataElement as IStructureArray; + var mcosData = mainVariable["MCOS", 0] as Opaque; + var opaqueData = mcosData.RawData as ICellArray; + var info = (opaqueData[0] as IArrayOf).Data; + var (offsets, position) = ReadOffsets(info, 0); + var fieldNames = ReadFieldNames(info, position, offsets[1]); + var numberOfClasses = ((offsets[3] - offsets[2]) / 16) - 1; + SubsystemData.ClassInfo[] classInformation = null; + using (var stream = new MemoryStream(info, offsets[2], offsets[3] - offsets[2])) + { + using (var reader = new BinaryReader(stream)) + { + classInformation = ReadClassInformation(reader, fieldNames, numberOfClasses); + } + } + var numberOfObjects = ((offsets[5] - offsets[4]) / 24) - 1; + SubsystemData.ObjectInfo[] objectInformation = null; + using (var stream = new MemoryStream(info, offsets[5], offsets[6] - offsets[5])) + { + using (var reader = new BinaryReader(stream)) + { + objectInformation = ReadObjectInformation(reader, numberOfObjects); + } + } + + var allFields = objectInformation.SelectMany(obj => obj.FieldLinks.Values); + var data = new Dictionary(); + foreach (var i in allFields) + { + data[i] = opaqueData[i + 2]; + } + return new SubsystemData(classInformation, objectInformation, data); + } + + private static SubsystemData.ObjectInfo ReadObjectInformation(BinaryReader reader) + { + var length = reader.ReadInt32(); + var fieldLinks = new Dictionary(); + for (var i = 0; i < length; i++) + { + var x = reader.ReadInt32(); + var y = reader.ReadInt32(); + var index = x * y; + var link = reader.ReadInt32(); + fieldLinks[index] = link; + } + return new SubsystemData.ObjectInfo(fieldLinks); + } + + private static SubsystemData.ObjectInfo[] ReadObjectInformation(BinaryReader reader, int numberOfObjects) + { + var result = new SubsystemData.ObjectInfo[numberOfObjects]; + reader.ReadBytes(8); + for (var objectIndex = 0; objectIndex < numberOfObjects; objectIndex++) + { + result[objectIndex] = ReadObjectInformation(reader); + var position = reader.BaseStream.Position; + if (position % 8 != 0) + { + reader.ReadBytes(8 - (int)(position % 8)); + } + } + return result; + } + + private static SubsystemData.ClassInfo[] ReadClassInformation( + BinaryReader reader, + string[] fieldNames, + int numberOfClasses) + { + var result = new SubsystemData.ClassInfo[numberOfClasses]; + var indices = new int[numberOfClasses + 1]; + for (var i = 0; i <= numberOfClasses; i++) + { + reader.ReadInt32(); + indices[i] = reader.ReadInt32(); + reader.ReadInt32(); + reader.ReadInt32(); + } + + for (var i = 0; i < numberOfClasses; i++) + { + var numberOfFields = indices[i + 1] - indices[i] - 1; + var names = new string[numberOfFields]; + Array.Copy(fieldNames, indices[i], names, 0, numberOfFields); + var className = fieldNames[indices[i + 1] - 1]; + result[i] = new SubsystemData.ClassInfo(className, names); + } + + return result; + } + + private static (int[] offsets, int newPosition) ReadOffsets(byte[] bytes, int startPosition) + { + var position = startPosition; + var offsets = new List(); + while (true) + { + var next = BitConverter.ToInt32(bytes, position); + position += 4; + if (next == 0) + { + if (position % 8 != 0) + { + position += 4; + } + + break; + } + offsets.Add(next); + } + + return (offsets.ToArray(), position); + } + + private static string[] ReadFieldNames(byte[] bytes, int startPosition, int numberOfFields) + { + var result = new string[numberOfFields]; + var position = startPosition; + for (var i = 0; i < numberOfFields; i++) + { + var list = new List(); + while (bytes[position] != 0) + { + list.Add(bytes[position]); + position++; + } + result[i] = Encoding.ASCII.GetString(list.ToArray()); + position++; + } + + return result; + } + } +} From 1018655f8cecf3584e1d19516d87faa18d00c65d Mon Sep 17 00:00:00 2001 From: Alexander Luzgarev Date: Sun, 14 Oct 2018 14:52:52 +0200 Subject: [PATCH 3/3] Add support for tables --- MatFileHandler.Tests/MatFileReaderTests.cs | 18 ++++++ MatFileHandler.Tests/test-data/good/table.mat | Bin 0 -> 837 bytes MatFileHandler/TableAdapter.cs | 60 ++++++++++++++++++ 3 files changed, 78 insertions(+) create mode 100644 MatFileHandler.Tests/test-data/good/table.mat create mode 100644 MatFileHandler/TableAdapter.cs diff --git a/MatFileHandler.Tests/MatFileReaderTests.cs b/MatFileHandler.Tests/MatFileReaderTests.cs index b424227..9000225 100755 --- a/MatFileHandler.Tests/MatFileReaderTests.cs +++ b/MatFileHandler.Tests/MatFileReaderTests.cs @@ -326,6 +326,24 @@ namespace MatFileHandler.Tests Assert.That(obj[2]["x"].ConvertToDoubleArray(), Is.EqualTo(new[] { -2.0 })); } + [Test] + public void TestTable() + { + var matFile = GetTests("good")["table"]; + var obj = matFile["table_"].Value as IMatObject; + var table = new TableAdapter(obj); + Assert.That(table.NumberOfRows, Is.EqualTo(3)); + Assert.That(table.NumberOfVariables, Is.EqualTo(2)); + Assert.That(table.Description, Is.EqualTo("Some table")); + Assert.That(table.VariableNames, Is.EqualTo(new[] { "variable1", "variable2" })); + var variable1 = table["variable1"] as ICellArray; + Assert.That((variable1[0] as ICharArray).String, Is.EqualTo("First row")); + Assert.That((variable1[1] as ICharArray).String, Is.EqualTo("Second row")); + Assert.That((variable1[2] as ICharArray).String, Is.EqualTo("Third row")); + var variable2 = table["variable2"]; + Assert.That(variable2.ConvertToDoubleArray(), Is.EqualTo(new[] { 1.0, 3.0, 5.0, 2.0, 4.0, 6.0 })); + } + private static AbstractTestDataFactory GetTests(string factoryName) => new MatTestDataFactory(Path.Combine(TestDirectory, factoryName)); diff --git a/MatFileHandler.Tests/test-data/good/table.mat b/MatFileHandler.Tests/test-data/good/table.mat new file mode 100644 index 0000000000000000000000000000000000000000..20018a47422d70ebd12c6bfd0cc94faa6f4fdb45 GIT binary patch literal 837 zcmeZu4DoSvQZUssQ1EpO(M`+DN!3vZ$Vn_o%P-2cQV4Jk_w+L}(NSSQv0U|3C11kdy1tSAP3k4#>I1*aArmCntU2?4XhYruabN>!16o$>PnDC4_iC^XKo`An=0_>!(=(w%0=+I|O zyqD@UsX3A1!UXmYAOmxlKn89NJD4YIC@|O8$s?$viACtROtsFO9i~BxIQSS{GG45W z-6R%qsw(_j(2-u{`4>L1%2y{U3ec^5X@r)_6wklSbIqqJ4yHNf6deI-d(|$>x-gqEM?XY<8@yp6K z-*xOFULF(b{uX=pSk1wECk%XN+E{MLsU~-z~r9arUmx1)t_m0xj(%Ss+F5^{qWJnUmLjgoSyiIeZpgD)<3yT z?<1?vEIsvWmxT0(5*xWK%X8*+XsF*Zn}7ZN&YnFrew_Z7AFr%_-6q5S;K`>8FRmSU z@jI;fs_kldK7(_UyDa|g%sqU|?#{*iTff|I|5RnY@asL(`|_e6yS_bUFk8Re?z+tR zPm84=Rr*xaT{GXEE+6sR{FC9z3Z-QaCA*IqbxvO}qdQS(dE&9sB{Llv#EWkq7q|Is z68PSvDC~({QkGujtcBOT+-%aAHlCk;efC7UU9^m?U7Z$(&2?`|KjqFRJhAYj zWBOL5n`QAedmT8-?i`=q{-U1Y>DN-H*VVz5{92Eu-HY}-^Xo}|v(5d4&BcMH@xSa1 z3txZmx}SG@9&>orcJV(y{_V0{`1;u&v%UHM%;#PE-`BqU|8pg|FaLuD&wu~^SoE&^ zhGUm^JenW(IR5Lo?l&*@=gs|NBk;fVLHE7o$64Yxo7K*@yQ`T0%g*&D`;J5N`vF6E BgO>mR literal 0 HcmV?d00001 diff --git a/MatFileHandler/TableAdapter.cs b/MatFileHandler/TableAdapter.cs new file mode 100644 index 0000000..0901f66 --- /dev/null +++ b/MatFileHandler/TableAdapter.cs @@ -0,0 +1,60 @@ +using System; +using System.Linq; + +namespace MatFileHandler +{ + public class TableAdapter + { + private readonly IMatObject matObject; + + public TableAdapter(IArray array) + { + matObject = array as IMatObject; + if (matObject?.ClassName != "table") + { + throw new ArgumentException("The object provided is not a table."); + } + var cellArray = matObject["varnames"] as ICellArray; + VariableNames = Enumerable + .Range(0, cellArray.Count) + .Select(i => (cellArray[i] as ICharArray).String) + .ToArray(); + NumberOfVariables = VariableNames.Length; + var props = matObject["props"] as IStructureArray; + Description = (props["Description"] as ICharArray).String; + NumberOfRows = (int) matObject["nrows"].ConvertToDoubleArray()[0]; + var rowNamesArrays = matObject["rownames"] as ICellArray; + RowNames = Enumerable + .Range(0, rowNamesArrays.Count) + .Select(i => (cellArray[i] as ICharArray).String) + .ToArray(); + } + + public int NumberOfRows { get; } + + public string[] RowNames { get; } + + public int NumberOfVariables { get; } + + public string[] VariableNames { get; } + + public IArray this[string variableName] + { + get + { + var maybeIndex = Enumerable.Range(0, VariableNames.Length) + .Where(i => VariableNames[i] == variableName) + .Select(i => (int?)i) + .FirstOrDefault(); + if (!(maybeIndex is int index)) + { + throw new IndexOutOfRangeException($"Variable '{variableName}' not found."); + } + var data = matObject["data"] as ICellArray; + return data[index]; + } + } + + public string Description { get; } + } +}