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..9000225 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;
@@ -292,7 +292,56 @@ 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 }));
+ }
+
+ [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) =>
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.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 0000000..1b074ec
Binary files /dev/null and b/MatFileHandler.Tests/test-data/good/object2.mat differ
diff --git a/MatFileHandler.Tests/test-data/good/table.mat b/MatFileHandler.Tests/test-data/good/table.mat
new file mode 100644
index 0000000..20018a4
Binary files /dev/null and b/MatFileHandler.Tests/test-data/good/table.mat differ
diff --git a/MatFileHandler/ArrayFlags.cs b/MatFileHandler/ArrayFlags.cs
index 3b5ef5b..4167e9d 100755
--- a/MatFileHandler/ArrayFlags.cs
+++ b/MatFileHandler/ArrayFlags.cs
@@ -1,4 +1,4 @@
-// Copyright 2017 Alexander Luzgarev
+// Copyright 2017-2018 Alexander Luzgarev
using System;
@@ -85,9 +85,9 @@ namespace MatFileHandler
MxUInt64 = 15,
///
- /// Undocumented object (?) array type.
+ /// Undocumented opaque object type.
///
- MxNewObject = 17,
+ MxOpaque = 17,
}
///
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..709462d 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;
@@ -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/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..06b68ce 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;
@@ -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/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/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/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..5bcb135 100755
--- a/MatFileHandler/MatFileReader.cs
+++ b/MatFileHandler/MatFileReader.cs
@@ -1,5 +1,6 @@
-// Copyright 2017 Alexander Luzgarev
+// 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/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/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;
+ }
+ }
+}
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; }
+ }
+}
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": {