Compare commits

...

19 Commits
master ... hdf

Author SHA1 Message Date
80e4356442 Update copyright text 2019-03-23 09:21:23 +01:00
aaefd0eb86 Unify MatFileReader tests 2019-03-23 09:18:03 +01:00
6a286ed53d Use latest C# version 2019-03-21 22:04:11 +01:00
e6d236139c Add XML documentation 2019-03-21 21:30:30 +01:00
bdd04a80b7 Unify HDF and old-style datatypes 2019-03-20 22:15:22 +01:00
433dca8c69 Get rid of unnecessary ArrayFlags 2019-03-20 22:01:47 +01:00
ef73a380bb Hdf refactoring 2019-03-17 20:39:11 +01:00
199ab46f0c Support global variables 2019-03-16 14:42:08 +01:00
cfe51d57ae Support sparse arrays 2019-03-16 14:30:43 +01:00
f727c6d430 Rename HDF-related types 2019-03-15 19:39:36 +01:00
82f43a7e5d Careful disposal 2019-03-12 21:15:12 +01:00
99558d96c4 Support logical arrays 2019-03-12 18:54:24 +01:00
67905605d6 Support structure and cell arrays 2019-03-11 22:15:06 +01:00
b0ae15abc9 Support complex numerical arrays 2019-03-10 12:35:28 +01:00
beae9e4429 Provide runtime identifiers for test project 2019-03-09 20:01:56 +01:00
b66e380414 Support numerical arrays 2019-03-09 18:53:44 +01:00
93be86d526 Support char arrays 2019-03-09 17:00:13 +01:00
0e14434bae Move test data 2019-03-07 19:04:16 +01:00
957234d30d Multi-target test project 2019-03-07 18:46:10 +01:00
107 changed files with 2567 additions and 969 deletions

View File

@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright 2017 Alexander Luzgarev
Copyright 2017-2019 Alexander Luzgarev
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

View File

@ -1,4 +1,4 @@
// Copyright 2017-2018 Alexander Luzgarev
// Copyright 2017-2019 Alexander Luzgarev
using System.Collections.Generic;
using System.IO;

View File

@ -1,4 +1,4 @@
// Copyright 2017-2018 Alexander Luzgarev
// Copyright 2017-2019 Alexander Luzgarev
using System.Collections.Generic;
using System.Numerics;

View File

@ -1,4 +1,4 @@
// Copyright 2017-2018 Alexander Luzgarev
// Copyright 2017-2019 Alexander Luzgarev
namespace MatFileHandler.Tests
{

View File

@ -1,4 +1,4 @@
// Copyright 2017-2018 Alexander Luzgarev
// Copyright 2017-2019 Alexander Luzgarev
using NUnit.Framework;

View File

@ -1,4 +1,4 @@
// Copyright 2017-2018 Alexander Luzgarev
// Copyright 2017-2019 Alexander Luzgarev
using System.IO;

View File

@ -1,4 +1,4 @@
// Copyright 2017-2018 Alexander Luzgarev
// Copyright 2017-2019 Alexander Luzgarev
namespace MatFileHandler.Tests
{

View File

@ -1,7 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
<TargetFrameworks>netcoreapp2.0;net461</TargetFrameworks>
<RuntimeIdentifiers>win-x64;win-x86</RuntimeIdentifiers>
<IsPackable>false</IsPackable>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<CodeAnalysisRuleSet>..\MatFileHandler.ruleset</CodeAnalysisRuleSet>

744
MatFileHandler.Tests/MatFileReaderTests.cs Executable file → Normal file
View File

@ -1,4 +1,4 @@
// Copyright 2017-2018 Alexander Luzgarev
// Copyright 2017-2019 Alexander Luzgarev
using System;
using System.IO;
@ -9,7 +9,7 @@ using NUnit.Framework;
namespace MatFileHandler.Tests
{
/// <summary>
/// Tests of file reading API.
/// Tests of file reading API (Level 5 cases).
/// </summary>
[TestFixture]
public class MatFileReaderTests
@ -17,59 +17,30 @@ namespace MatFileHandler.Tests
private const string TestDirectory = "test-data";
/// <summary>
/// Test reading all files in a given test set.
/// Test reading an ASCII-encoded string.
/// </summary>
/// <param name="testSet">Name of the set.</param>
[TestCase("good")]
public void TestReader(string testSet)
/// <param name="testCaseName">Test case name.</param>
[TestCase("level5/ascii")]
[TestCase("hdf/ascii")]
public void TestAscii(string testCaseName)
{
foreach (var matFile in GetTests(testSet).GetAllTestData())
{
Assert.That(matFile.Variables, Is.Not.Empty);
}
}
/// <summary>
/// Test reading lower and upper limits of integer data types.
/// </summary>
[Test]
public void TestLimits()
{
var matFile = GetTests("good")["limits"];
IArray array;
array = matFile["int8_"].Value;
CheckLimits(array as IArrayOf<sbyte>, CommonData.Int8Limits);
Assert.That(array.ConvertToDoubleArray(), Is.EqualTo(new[] { -128.0, 127.0 }));
array = matFile["uint8_"].Value;
CheckLimits(array as IArrayOf<byte>, CommonData.UInt8Limits);
array = matFile["int16_"].Value;
CheckLimits(array as IArrayOf<short>, CommonData.Int16Limits);
array = matFile["uint16_"].Value;
CheckLimits(array as IArrayOf<ushort>, CommonData.UInt16Limits);
array = matFile["int32_"].Value;
CheckLimits(array as IArrayOf<int>, CommonData.Int32Limits);
array = matFile["uint32_"].Value;
CheckLimits(array as IArrayOf<uint>, CommonData.UInt32Limits);
array = matFile["int64_"].Value;
CheckLimits(array as IArrayOf<long>, CommonData.Int64Limits);
array = matFile["uint64_"].Value;
CheckLimits(array as IArrayOf<ulong>, CommonData.UInt64Limits);
var matFile = GetTestCase(testCaseName);
var arrayAscii = matFile["s"].Value as ICharArray;
Assert.That(arrayAscii, Is.Not.Null);
Assert.That(arrayAscii.Dimensions, Is.EqualTo(new[] { 1, 3 }));
Assert.That(arrayAscii.String, Is.EqualTo("abc"));
Assert.That(arrayAscii[2], Is.EqualTo('c'));
}
/// <summary>
/// Test writing lower and upper limits of integer-based complex data types.
/// </summary>
[Test]
public void TestComplexLimits()
/// <param name="testCaseName">Test case name.</param>
[TestCase("level5/limits_complex")]
[TestCase("hdf/limits_complex")]
public void TestComplexLimits(string testCaseName)
{
var matFile = GetTests("good")["limits_complex"];
var matFile = GetTestCase(testCaseName);
IArray array;
array = matFile["int8_complex"].Value;
CheckComplexLimits(array as IArrayOf<ComplexOf<sbyte>>, CommonData.Int8Limits);
@ -100,78 +71,345 @@ namespace MatFileHandler.Tests
}
/// <summary>
/// Test reading an ASCII-encoded string.
/// Test reading a two-dimensional complex array.
/// </summary>
[Test]
public void TestAscii()
/// <param name="testCaseName">Test case name.</param>
[TestCase("level5/matrix_complex")]
[TestCase("hdf/matrix_complex")]
public void TestComplexMatrix(string testCaseName)
{
var matFile = GetTests("good")["ascii"];
var arrayAscii = matFile["s"].Value as ICharArray;
Assert.That(arrayAscii, Is.Not.Null);
Assert.That(arrayAscii.Dimensions, Is.EqualTo(new[] { 1, 3 }));
Assert.That(arrayAscii.String, Is.EqualTo("abc"));
Assert.That(arrayAscii[2], Is.EqualTo('c'));
}
/// <summary>
/// Test reading a Unicode string.
/// </summary>
[Test]
public void TestUnicode()
var matFile = GetTestCase(testCaseName);
var matrix = matFile["matrix"].Value as IArrayOf<Complex>;
Assert.That(matrix.Dimensions, Is.EqualTo(new[] { 3, 2 }));
Assert.That(matrix.ConvertToComplexArray(), Is.EqualTo(new[]
{
var matFile = GetTests("good")["unicode"];
var arrayUnicode = matFile["s"].Value as ICharArray;
Assert.That(arrayUnicode, Is.Not.Null);
Assert.That(arrayUnicode.Dimensions, Is.EqualTo(new[] { 1, 2 }));
Assert.That(arrayUnicode.String, Is.EqualTo("必フ"));
Assert.That(arrayUnicode[0], Is.EqualTo('必'));
Assert.That(arrayUnicode[1], Is.EqualTo('フ'));
}
/// <summary>
/// Test reading a wide Unicode string.
/// </summary>
[Test]
public void TestUnicodeWide()
{
var matFile = GetTests("good")["unicode-wide"];
var arrayUnicodeWide = matFile["s"].Value as ICharArray;
Assert.That(arrayUnicodeWide, Is.Not.Null);
Assert.That(arrayUnicodeWide.Dimensions, Is.EqualTo(new[] { 1, 2 }));
Assert.That(arrayUnicodeWide.String, Is.EqualTo("🍆"));
}
/// <summary>
/// Test converting a structure array to a Double array.
/// </summary>
/// <returns>Should return null.</returns>
[Test(ExpectedResult = null)]
public double[] TestConvertToDoubleArray()
{
var matFile = GetTests("good")["struct"];
var array = matFile.Variables[0].Value;
return array.ConvertToDoubleArray();
new Complex(1.0, 4.0),
new Complex(3.0, 1.0),
new Complex(5.0, 0.25),
new Complex(2.0, 2.0),
new Complex(4.0, 0.5),
new Complex(6.0, 0.125),
}));
Assert.That(matrix[0, 0], Is.EqualTo(new Complex(1.0, 4.0)));
Assert.That(matrix[0, 1], Is.EqualTo(new Complex(2.0, 2.0)));
Assert.That(matrix[1, 0], Is.EqualTo(new Complex(3.0, 1.0)));
Assert.That(matrix[1, 1], Is.EqualTo(new Complex(4.0, 0.5)));
Assert.That(matrix[2, 0], Is.EqualTo(new Complex(5.0, 0.25)));
Assert.That(matrix[2, 1], Is.EqualTo(new Complex(6.0, 0.125)));
}
/// <summary>
/// Test converting a structure array to a Complex array.
/// </summary>
/// <returns>Should return null.</returns>
[Test(ExpectedResult = null)]
public Complex[] TestConvertToComplexArray()
/// <param name="testCaseName">Test case name.</param>
[TestCase("level5/struct")]
[TestCase("hdf/struct")]
public void TestConvertToComplexArray(string testCaseName)
{
var matFile = GetTests("good")["struct"];
var matFile = GetTestCase(testCaseName);
var array = matFile.Variables[0].Value;
return array.ConvertToComplexArray();
Assert.IsNull(array.ConvertToComplexArray());
}
/// <summary>
/// Test converting a structure array to a Double array.
/// </summary>
/// <param name="testCaseName">Test case name.</param>
[TestCase("level5/struct")]
[TestCase("hdf/struct")]
public void TestConvertToDoubleArray(string testCaseName)
{
var matFile = GetTestCase(testCaseName);
var array = matFile.Variables[0].Value;
Assert.IsNull(array.ConvertToDoubleArray());
}
/// <summary>
/// Test datetime objects.
/// </summary>
[Test]
public void TestDatetime()
{
var matFile = ReadLevel5TestFile("datetime");
var d = matFile["d"].Value as IMatObject;
var datetime = new DatetimeAdapter(d);
Assert.That(datetime.Dimensions, Is.EquivalentTo(new[] { 1, 2 }));
Assert.That(datetime[0], Is.EqualTo(new DateTimeOffset(2000, 1, 1, 0, 0, 0, TimeSpan.Zero)));
Assert.That(datetime[1], Is.EqualTo(new DateTimeOffset(1987, 1, 2, 3, 4, 5, TimeSpan.Zero)));
}
/// <summary>
/// Test unrepresentable datetime.
/// </summary>
[Test]
public void TestDatetime_Unrepresentable()
{
var matFile = ReadLevel5TestFile("datetime-unrepresentable");
var obj = matFile["d"].Value as IMatObject;
var datetime = new DatetimeAdapter(obj);
var d0 = datetime[0];
Assert.That(d0, Is.Null);
}
/// <summary>
/// Another test for datetime objects.
/// </summary>
[Test]
public void TestDatetime2()
{
var matFile = ReadLevel5TestFile("datetime2");
var d = matFile["d"].Value as IMatObject;
var datetime = new DatetimeAdapter(d);
Assert.That(datetime.Dimensions, Is.EquivalentTo(new[] { 1, 1 }));
var diff = new DateTimeOffset(2, 1, 1, 1, 1, 1, 235, TimeSpan.Zero);
Assert.That(datetime[0] - diff < TimeSpan.FromMilliseconds(1));
Assert.That(diff - datetime[0] < TimeSpan.FromMilliseconds(1));
}
/// <summary>
/// Test duration objects.
/// </summary>
[Test]
public void TestDuration()
{
var matFile = ReadLevel5TestFile("duration");
var d = matFile["d"].Value as IMatObject;
var duration = new DurationAdapter(d);
Assert.That(duration.Dimensions, Is.EquivalentTo(new[] { 1, 3 }));
Assert.That(duration[0], Is.EqualTo(TimeSpan.FromTicks(12345678L)));
Assert.That(duration[1], Is.EqualTo(new TimeSpan(0, 2, 4)));
Assert.That(duration[2], Is.EqualTo(new TimeSpan(1, 3, 5)));
}
/// <summary>
/// Test reading a global variable.
/// </summary>
/// <param name="testCaseName">Test case name.</param>
[TestCase("level5/global")]
[TestCase("hdf/global")]
public void TestGlobal(string testCaseName)
{
var matFile = GetTestCase(testCaseName);
var variable = matFile.Variables.First();
Assert.That(variable.IsGlobal, Is.True);
}
/// <summary>
/// Test reading lower and upper limits of integer data types.
/// </summary>
/// <param name="testCaseName">Test case name.</param>
[TestCase("level5/limits")]
[TestCase("hdf/limits")]
public void TestLimits(string testCaseName)
{
var matFile = GetTestCase(testCaseName);
IArray array;
array = matFile["int8_"].Value;
CheckLimits(array as IArrayOf<sbyte>, CommonData.Int8Limits);
Assert.That(array.ConvertToDoubleArray(), Is.EqualTo(new[] { -128.0, 127.0 }));
array = matFile["uint8_"].Value;
CheckLimits(array as IArrayOf<byte>, CommonData.UInt8Limits);
array = matFile["int16_"].Value;
CheckLimits(array as IArrayOf<short>, CommonData.Int16Limits);
array = matFile["uint16_"].Value;
CheckLimits(array as IArrayOf<ushort>, CommonData.UInt16Limits);
array = matFile["int32_"].Value;
CheckLimits(array as IArrayOf<int>, CommonData.Int32Limits);
array = matFile["uint32_"].Value;
CheckLimits(array as IArrayOf<uint>, CommonData.UInt32Limits);
array = matFile["int64_"].Value;
CheckLimits(array as IArrayOf<long>, CommonData.Int64Limits);
array = matFile["uint64_"].Value;
CheckLimits(array as IArrayOf<ulong>, CommonData.UInt64Limits);
}
/// <summary>
/// Test reading a logical array.
/// </summary>
/// <param name="testCaseName">Test case name.</param>
[TestCase("level5/logical")]
[TestCase("hdf/logical")]
public void TestLogical(string testCaseName)
{
var matFile = GetTestCase(testCaseName);
var array = matFile["logical_"].Value;
var logicalArray = array as IArrayOf<bool>;
Assert.That(logicalArray, Is.Not.Null);
Assert.That(logicalArray[0, 0], Is.True);
Assert.That(logicalArray[0, 1], Is.True);
Assert.That(logicalArray[0, 2], Is.False);
Assert.That(logicalArray[1, 0], Is.False);
Assert.That(logicalArray[1, 1], Is.True);
Assert.That(logicalArray[1, 2], Is.True);
}
/// <summary>
/// Test reading a two-dimensional double array.
/// </summary>
/// <param name="testCaseName">Test case name.</param>
[TestCase("level5/matrix")]
[TestCase("hdf/matrix")]
public void TestMatrix(string testCaseName)
{
var matFile = GetTestCase(testCaseName);
var matrix = matFile["matrix"].Value as IArrayOf<double>;
Assert.That(matrix.Dimensions, Is.EqualTo(new[] { 3, 2 }));
Assert.That(matrix.ConvertToDoubleArray(), Is.EqualTo(new[] { 1.0, 3.0, 5.0, 2.0, 4.0, 6.0 }));
Assert.That(matrix[0, 0], Is.EqualTo(1.0));
Assert.That(matrix[0, 1], Is.EqualTo(2.0));
Assert.That(matrix[1, 0], Is.EqualTo(3.0));
Assert.That(matrix[1, 1], Is.EqualTo(4.0));
Assert.That(matrix[2, 0], Is.EqualTo(5.0));
Assert.That(matrix[2, 1], Is.EqualTo(6.0));
}
/// <summary>
/// Test nested objects.
/// </summary>
[Test]
public void TestNestedObjects()
{
var matFile = ReadLevel5TestFile("subsubPoint");
var p = matFile["p"].Value as IMatObject;
Assert.That(p.ClassName, Is.EqualTo("Point"));
Assert.That(p["x"].ConvertToDoubleArray(), Is.EquivalentTo(new[] { 1.0 }));
var pp = p["y"] as IMatObject;
Assert.That(pp.ClassName == "Point");
Assert.That(pp["x"].ConvertToDoubleArray(), Is.EquivalentTo(new[] { 10.0 }));
var ppp = pp["y"] as IMatObject;
Assert.That(ppp["x"].ConvertToDoubleArray(), Is.EquivalentTo(new[] { 100.0 }));
Assert.That(ppp["y"].ConvertToDoubleArray(), Is.EquivalentTo(new[] { 200.0 }));
}
/// <summary>
/// Test reading an object.
/// </summary>
[Test]
public void TestObject()
{
var matFile = ReadLevel5TestFile("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 }));
}
/// <summary>
/// Test reading another object.
/// </summary>
[Test]
public void TestObject2()
{
var matFile = ReadLevel5TestFile("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 }));
}
/// <summary>
/// Test reading all files in a given test set.
/// </summary>
/// <param name="testSet">Name of the set.</param>
[TestCase("level5")]
[TestCase("hdf")]
public void TestReader(string testSet)
{
foreach (var matFile in GetTests(testSet).GetAllTestData())
{
Assert.That(matFile.Variables, Is.Not.Empty);
}
}
/// <summary>
/// Test reading a sparse array.
/// </summary>
/// <param name="testCaseName">Test case name.</param>
[TestCase("level5/sparse")]
[TestCase("hdf/sparse")]
public void TestSparse(string testCaseName)
{
var matFile = GetTestCase(testCaseName);
var sparseArray = matFile["sparse_"].Value as ISparseArrayOf<double>;
Assert.That(sparseArray, Is.Not.Null);
Assert.That(sparseArray.Dimensions, Is.EqualTo(new[] { 4, 5 }));
Assert.That(sparseArray.Data[(1, 1)], Is.EqualTo(1.0));
Assert.That(sparseArray[1, 1], Is.EqualTo(1.0));
Assert.That(sparseArray[1, 2], Is.EqualTo(2.0));
Assert.That(sparseArray[2, 1], Is.EqualTo(3.0));
Assert.That(sparseArray[2, 3], Is.EqualTo(4.0));
Assert.That(sparseArray[0, 4], Is.EqualTo(0.0));
Assert.That(sparseArray[3, 0], Is.EqualTo(0.0));
Assert.That(sparseArray[3, 4], Is.EqualTo(0.0));
}
/// <summary>
/// Test reading a sparse logical array.
/// </summary>
/// <param name="testCaseName">Test case name.</param>
[TestCase("level5/sparse_logical")]
[TestCase("hdf/sparse_logical")]
public void TestSparseLogical(string testCaseName)
{
var matFile = GetTestCase(testCaseName);
var array = matFile["sparse_logical"].Value;
var sparseArray = array as ISparseArrayOf<bool>;
Assert.That(sparseArray, Is.Not.Null);
Assert.That(sparseArray.Data[(0, 0)], Is.True);
Assert.That(sparseArray[0, 0], Is.True);
Assert.That(sparseArray[0, 1], Is.True);
Assert.That(sparseArray[0, 2], Is.False);
Assert.That(sparseArray[1, 0], Is.False);
Assert.That(sparseArray[1, 1], Is.True);
Assert.That(sparseArray[1, 2], Is.True);
}
/// <summary>
/// Test string objects.
/// </summary>
[Test]
public void TestString()
{
var matFile = ReadLevel5TestFile("string");
var s = matFile["s"].Value as IMatObject;
var str = new StringAdapter(s);
Assert.That(str.Dimensions, Is.EquivalentTo(new[] { 4, 1 }));
Assert.That(str[0], Is.EqualTo("abc"));
Assert.That(str[1], Is.EqualTo("defgh"));
Assert.That(str[2], Is.EqualTo("абвгд"));
Assert.That(str[3], Is.EqualTo("æøå"));
}
/// <summary>
/// Test reading a structure array.
/// </summary>
[Test]
public void TestStruct()
/// <param name="testCaseName">Test case name.</param>
[TestCase("level5/struct")]
[TestCase("hdf/struct")]
public void TestStruct(string testCaseName)
{
var matFile = GetTests("good")["struct"];
var matFile = GetTestCase(testCaseName);
var structure = matFile["struct_"].Value as IStructureArray;
Assert.That(structure, Is.Not.Null);
Assert.That(structure.FieldNames, Is.EquivalentTo(new[] { "x", "y" }));
@ -203,158 +441,13 @@ namespace MatFileHandler.Tests
Assert.That(structure["y", 1, 2].IsEmpty, Is.True);
}
/// <summary>
/// Test reading a sparse array.
/// </summary>
[Test]
public void TestSparse()
{
var matFile = GetTests("good")["sparse"];
var sparseArray = matFile["sparse_"].Value as ISparseArrayOf<double>;
Assert.That(sparseArray, Is.Not.Null);
Assert.That(sparseArray.Dimensions, Is.EqualTo(new[] { 4, 5 }));
Assert.That(sparseArray.Data[(1, 1)], Is.EqualTo(1.0));
Assert.That(sparseArray[1, 1], Is.EqualTo(1.0));
Assert.That(sparseArray[1, 2], Is.EqualTo(2.0));
Assert.That(sparseArray[2, 1], Is.EqualTo(3.0));
Assert.That(sparseArray[2, 3], Is.EqualTo(4.0));
Assert.That(sparseArray[0, 4], Is.EqualTo(0.0));
Assert.That(sparseArray[3, 0], Is.EqualTo(0.0));
Assert.That(sparseArray[3, 4], Is.EqualTo(0.0));
}
/// <summary>
/// Test reading a logical array.
/// </summary>
[Test]
public void TestLogical()
{
var matFile = GetTests("good")["logical"];
var array = matFile["logical_"].Value;
var logicalArray = array as IArrayOf<bool>;
Assert.That(logicalArray, Is.Not.Null);
Assert.That(logicalArray[0, 0], Is.True);
Assert.That(logicalArray[0, 1], Is.True);
Assert.That(logicalArray[0, 2], Is.False);
Assert.That(logicalArray[1, 0], Is.False);
Assert.That(logicalArray[1, 1], Is.True);
Assert.That(logicalArray[1, 2], Is.True);
}
/// <summary>
/// Test reading a sparse logical array.
/// </summary>
[Test]
public void TestSparseLogical()
{
var matFile = GetTests("good")["sparse_logical"];
var array = matFile["sparse_logical"].Value;
var sparseArray = array as ISparseArrayOf<bool>;
Assert.That(sparseArray, Is.Not.Null);
Assert.That(sparseArray.Data[(0, 0)], Is.True);
Assert.That(sparseArray[0, 0], Is.True);
Assert.That(sparseArray[0, 1], Is.True);
Assert.That(sparseArray[0, 2], Is.False);
Assert.That(sparseArray[1, 0], Is.False);
Assert.That(sparseArray[1, 1], Is.True);
Assert.That(sparseArray[1, 2], Is.True);
}
/// <summary>
/// Test reading a global variable.
/// </summary>
[Test]
public void TestGlobal()
{
var matFile = GetTests("good")["global"];
var variable = matFile.Variables.First();
Assert.That(variable.IsGlobal, Is.True);
}
/// <summary>
/// Test reading a sparse complex array.
/// </summary>
[Test]
public void TextSparseComplex()
{
var matFile = GetTests("good")["sparse_complex"];
var array = matFile["sparse_complex"].Value;
var sparseArray = array as ISparseArrayOf<Complex>;
Assert.That(sparseArray, Is.Not.Null);
Assert.That(sparseArray[0, 0], Is.EqualTo(-1.5 + (2.5 * Complex.ImaginaryOne)));
Assert.That(sparseArray[1, 0], Is.EqualTo(2 - (3 * Complex.ImaginaryOne)));
Assert.That(sparseArray[0, 1], Is.EqualTo(Complex.Zero));
Assert.That(sparseArray[1, 1], Is.EqualTo(0.5 + (1.0 * Complex.ImaginaryOne)));
}
/// <summary>
/// Test reading an object.
/// </summary>
[Test]
public void TestObject()
{
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 }));
}
/// <summary>
/// Test reading another object.
/// </summary>
[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 }));
}
/// <summary>
/// Test reading a table.
/// </summary>
[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 }));
}
/// <summary>
/// Test subobjects within objects.
/// </summary>
[Test]
public void TestSubobjects()
{
var matFile = GetTests("good")["pointWithSubpoints"];
var matFile = ReadLevel5TestFile("pointWithSubpoints");
var p = matFile["p"].Value as IMatObject;
Assert.That(p.ClassName, Is.EqualTo("Point"));
var x = p["x"] as IMatObject;
@ -372,105 +465,74 @@ namespace MatFileHandler.Tests
}
/// <summary>
/// Test nested objects.
/// Test reading a table.
/// </summary>
[Test]
public void TestNestedObjects()
public void TestTable()
{
var matFile = GetTests("good")["subsubPoint"];
var p = matFile["p"].Value as IMatObject;
Assert.That(p.ClassName, Is.EqualTo("Point"));
Assert.That(p["x"].ConvertToDoubleArray(), Is.EquivalentTo(new[] { 1.0 }));
var pp = p["y"] as IMatObject;
Assert.That(pp.ClassName == "Point");
Assert.That(pp["x"].ConvertToDoubleArray(), Is.EquivalentTo(new[] { 10.0 }));
var ppp = pp["y"] as IMatObject;
Assert.That(ppp["x"].ConvertToDoubleArray(), Is.EquivalentTo(new[] { 100.0 }));
Assert.That(ppp["y"].ConvertToDoubleArray(), Is.EquivalentTo(new[] { 200.0 }));
var matFile = ReadLevel5TestFile("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 }));
}
/// <summary>
/// Test datetime objects.
/// Test reading a Unicode string.
/// </summary>
[Test]
public void TestDatetime()
/// <param name="testCaseName">Test case name.</param>
[TestCase("level5/unicode")]
[TestCase("hdf/unicode")]
public void TestUnicode(string testCaseName)
{
var matFile = GetTests("good")["datetime"];
var d = matFile["d"].Value as IMatObject;
var datetime = new DatetimeAdapter(d);
Assert.That(datetime.Dimensions, Is.EquivalentTo(new[] { 1, 2 }));
Assert.That(datetime[0], Is.EqualTo(new DateTimeOffset(2000, 1, 1, 0, 0, 0, TimeSpan.Zero)));
Assert.That(datetime[1], Is.EqualTo(new DateTimeOffset(1987, 1, 2, 3, 4, 5, TimeSpan.Zero)));
var matFile = GetTestCase(testCaseName);
var arrayUnicode = matFile["s"].Value as ICharArray;
Assert.That(arrayUnicode, Is.Not.Null);
Assert.That(arrayUnicode.Dimensions, Is.EqualTo(new[] { 1, 2 }));
Assert.That(arrayUnicode.String, Is.EqualTo("必フ"));
Assert.That(arrayUnicode[0], Is.EqualTo('必'));
Assert.That(arrayUnicode[1], Is.EqualTo('フ'));
}
/// <summary>
/// Another test for datetime objects.
/// Test reading a wide Unicode string.
/// </summary>
[Test]
public void TestDatetime2()
/// <param name="testCaseName">Test case name.</param>
[TestCase("level5/unicode-wide")]
[TestCase("hdf/unicode-wide")]
public void TestUnicodeWide(string testCaseName)
{
var matFile = GetTests("good")["datetime2"];
var d = matFile["d"].Value as IMatObject;
var datetime = new DatetimeAdapter(d);
Assert.That(datetime.Dimensions, Is.EquivalentTo(new[] { 1, 1 }));
var diff = new DateTimeOffset(2, 1, 1, 1, 1, 1, 235, TimeSpan.Zero);
Assert.That(datetime[0] - diff < TimeSpan.FromMilliseconds(1));
Assert.That(diff - datetime[0] < TimeSpan.FromMilliseconds(1));
var matFile = GetTestCase(testCaseName);
var arrayUnicodeWide = matFile["s"].Value as ICharArray;
Assert.That(arrayUnicodeWide, Is.Not.Null);
Assert.That(arrayUnicodeWide.Dimensions, Is.EqualTo(new[] { 1, 2 }));
Assert.That(arrayUnicodeWide.String, Is.EqualTo("🍆"));
}
/// <summary>
/// Test string objects.
/// Test reading a sparse complex array.
/// </summary>
[Test]
public void TestString()
/// <param name="testCaseName">Test case name.</param>
[TestCase("level5/sparse_complex")]
[TestCase("hdf/sparse_complex")]
public void TextSparseComplex(string testCaseName)
{
var matFile = GetTests("good")["string"];
var s = matFile["s"].Value as IMatObject;
var str = new StringAdapter(s);
Assert.That(str.Dimensions, Is.EquivalentTo(new[] { 4, 1 }));
Assert.That(str[0], Is.EqualTo("abc"));
Assert.That(str[1], Is.EqualTo("defgh"));
Assert.That(str[2], Is.EqualTo("абвгд"));
Assert.That(str[3], Is.EqualTo("æøå"));
}
/// <summary>
/// Test duration objects.
/// </summary>
[Test]
public void TestDuration()
{
var matFile = GetTests("good")["duration"];
var d = matFile["d"].Value as IMatObject;
var duration = new DurationAdapter(d);
Assert.That(duration.Dimensions, Is.EquivalentTo(new[] { 1, 3 }));
Assert.That(duration[0], Is.EqualTo(TimeSpan.FromTicks(12345678L)));
Assert.That(duration[1], Is.EqualTo(new TimeSpan(0, 2, 4)));
Assert.That(duration[2], Is.EqualTo(new TimeSpan(1, 3, 5)));
}
/// <summary>
/// Test unrepresentable datetime.
/// </summary>
[Test]
public void TestDatetime_Unrepresentable()
{
var matFile = GetTests("good")["datetime-unrepresentable"];
var obj = matFile["d"].Value as IMatObject;
var datetime = new DatetimeAdapter(obj);
var d0 = datetime[0];
Assert.That(d0, Is.Null);
}
private static AbstractTestDataFactory<IMatFile> GetTests(string factoryName) =>
new MatTestDataFactory(Path.Combine(TestDirectory, factoryName));
private static void CheckLimits<T>(IArrayOf<T> array, T[] limits)
where T : struct
{
Assert.That(array, Is.Not.Null);
Assert.That(array.Dimensions, Is.EqualTo(new[] { 1, 2 }));
Assert.That(array.Data, Is.EqualTo(limits));
var matFile = GetTestCase(testCaseName);
var array = matFile["sparse_complex"].Value;
var sparseArray = array as ISparseArrayOf<Complex>;
Assert.That(sparseArray, Is.Not.Null);
Assert.That(sparseArray[0, 0], Is.EqualTo(-1.5 + (2.5 * Complex.ImaginaryOne)));
Assert.That(sparseArray[1, 0], Is.EqualTo(2 - (3 * Complex.ImaginaryOne)));
Assert.That(sparseArray[0, 1], Is.EqualTo(Complex.Zero));
Assert.That(sparseArray[1, 1], Is.EqualTo(0.5 + (1.0 * Complex.ImaginaryOne)));
}
private static void CheckComplexLimits<T>(IArrayOf<ComplexOf<T>> array, T[] limits)
@ -481,5 +543,27 @@ namespace MatFileHandler.Tests
Assert.That(array[0], Is.EqualTo(new ComplexOf<T>(limits[0], limits[1])));
Assert.That(array[1], Is.EqualTo(new ComplexOf<T>(limits[1], limits[0])));
}
private static void CheckLimits<T>(IArrayOf<T> array, T[] limits)
where T : struct
{
Assert.That(array, Is.Not.Null);
Assert.That(array.Dimensions, Is.EqualTo(new[] { 1, 2 }));
Assert.That(array.Data, Is.EqualTo(limits));
}
private static IMatFile GetTestCase(string testCaseName)
{
var parts = testCaseName.Split('/');
return GetTests(parts[0])[parts[1]];
}
private static AbstractTestDataFactory<IMatFile> GetTests(string factoryName) =>
new MatTestDataFactory(Path.Combine(TestDirectory, factoryName));
private IMatFile ReadLevel5TestFile(string testName)
{
return GetTests("level5")[testName];
}
}
}

View File

@ -1,4 +1,4 @@
// Copyright 2017-2018 Alexander Luzgarev
// Copyright 2017-2019 Alexander Luzgarev
using System;
using System.IO;
@ -27,7 +27,7 @@ namespace MatFileHandler.Tests
array[1] = 17.0;
var variable = builder.NewVariable("test", array);
var actual = builder.NewFile(new[] { variable });
MatCompareWithTestData("good", "double-array", actual);
MatCompareWithLevel5TestData("double-array", actual);
}
/// <summary>
@ -65,7 +65,7 @@ namespace MatFileHandler.Tests
var int64 = builder.NewVariable("int64_", builder.NewArray(CommonData.Int64Limits, 1, 2));
var uint64 = builder.NewVariable("uint64_", builder.NewArray(CommonData.UInt64Limits, 1, 2));
var actual = builder.NewFile(new[] { int16, int32, int64, int8, uint16, uint32, uint64, uint8 });
MatCompareWithTestData("good", "limits", actual);
MatCompareWithLevel5TestData("limits", actual);
}
/// <summary>
@ -104,7 +104,7 @@ namespace MatFileHandler.Tests
int16Complex, int32Complex, int64Complex, int8Complex,
uint16Complex, uint32Complex, uint64Complex, uint8Complex,
});
MatCompareWithTestData("good", "limits_complex", actual);
MatCompareWithLevel5TestData("limits_complex", actual);
}
/// <summary>
@ -116,7 +116,7 @@ namespace MatFileHandler.Tests
var builder = new DataBuilder();
var s = builder.NewVariable("s", builder.NewCharArray("🍆"));
var actual = builder.NewFile(new[] { s });
MatCompareWithTestData("good", "unicode-wide", actual);
MatCompareWithLevel5TestData("unicode-wide", actual);
}
/// <summary>
@ -133,7 +133,7 @@ namespace MatFileHandler.Tests
sparseArray[2, 3] = 4;
var sparse = builder.NewVariable("sparse_", sparseArray);
var actual = builder.NewFile(new[] { sparse });
MatCompareWithTestData("good", "sparse", actual);
MatCompareWithLevel5TestData("sparse", actual);
}
/// <summary>
@ -161,7 +161,7 @@ namespace MatFileHandler.Tests
structure["y", 1, 2] = builder.NewEmpty();
var struct_ = builder.NewVariable("struct_", structure);
var actual = builder.NewFile(new[] { struct_ });
MatCompareWithTestData("good", "struct", actual);
MatCompareWithLevel5TestData("struct", actual);
}
/// <summary>
@ -174,7 +174,7 @@ namespace MatFileHandler.Tests
var logical = builder.NewArray(new[] { true, false, true, true, false, true }, 2, 3);
var logicalVariable = builder.NewVariable("logical_", logical);
var actual = builder.NewFile(new[] { logicalVariable });
MatCompareWithTestData("good", "logical", actual);
MatCompareWithLevel5TestData("logical", actual);
}
/// <summary>
@ -191,7 +191,7 @@ namespace MatFileHandler.Tests
array[1, 2] = true;
var sparseLogical = builder.NewVariable("sparse_logical", array);
var actual = builder.NewFile(new[] { sparseLogical });
MatCompareWithTestData("good", "sparse_logical", actual);
MatCompareWithLevel5TestData("sparse_logical", actual);
}
/// <summary>
@ -207,7 +207,7 @@ namespace MatFileHandler.Tests
array[1, 1] = 0.5 + Complex.ImaginaryOne;
var sparseComplex = builder.NewVariable("sparse_complex", array);
var actual = builder.NewFile(new[] { sparseComplex });
MatCompareWithTestData("good", "sparse_complex", actual);
MatCompareWithLevel5TestData("sparse_complex", actual);
}
/// <summary>
@ -220,7 +220,7 @@ namespace MatFileHandler.Tests
var array = builder.NewArray(new double[] { 1, 3, 5 }, 1, 3);
var global = builder.NewVariable("global_", array, true);
var actual = builder.NewFile(new[] { global });
MatCompareWithTestData("good", "global", actual);
MatCompareWithLevel5TestData("global", actual);
}
private static AbstractTestDataFactory<IMatFile> GetMatTestData(string factoryName) =>
@ -398,6 +398,11 @@ namespace MatFileHandler.Tests
}
}
private void MatCompareWithLevel5TestData(string testName, IMatFile actual)
{
MatCompareWithTestData("level5", testName, actual);
}
private void MatCompareWithTestData(string factoryName, string testName, IMatFile actual)
{
var expected = GetMatTestData(factoryName)[testName];

View File

@ -1,4 +1,4 @@
// Copyright 2017-2018 Alexander Luzgarev
// Copyright 2017-2019 Alexander Luzgarev
using System.IO;

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,4 +1,4 @@
// Copyright 2017-2018 Alexander Luzgarev
// Copyright 2017-2019 Alexander Luzgarev
using System;
@ -138,20 +138,4 @@ namespace MatFileHandler
Variable = variable;
}
}
/// <summary>
/// Sparse array properties.
/// </summary>
internal struct SparseArrayFlags
{
/// <summary>
/// Usual array properties.
/// </summary>
public ArrayFlags ArrayFlags;
/// <summary>
/// Maximal number of non-zero elements.
/// </summary>
public uint NzMax;
}
}

View File

@ -1,4 +1,4 @@
// Copyright 2017-2018 Alexander Luzgarev
// Copyright 2017-2019 Alexander Luzgarev
using System;

View File

@ -1,4 +1,4 @@
// Copyright 2017-2018 Alexander Luzgarev
// Copyright 2017-2019 Alexander Luzgarev
namespace MatFileHandler
{

View File

@ -1,4 +1,4 @@
// Copyright 2017-2018 Alexander Luzgarev
// Copyright 2017-2019 Alexander Luzgarev
using System;
using System.Collections.Generic;
@ -28,9 +28,7 @@ namespace MatFileHandler
where T : struct
{
return new MatNumericalArrayOf<T>(
GetStandardFlags<T>(),
dimensions,
string.Empty,
new T[dimensions.NumberOfElements()]);
}
@ -54,7 +52,7 @@ namespace MatFileHandler
{
throw new ArgumentException("Data size does not match the specified dimensions", "data");
}
return new MatNumericalArrayOf<T>(GetStandardFlags<T>(), dimensions, string.Empty, data);
return new MatNumericalArrayOf<T>(dimensions, data);
}
/// <summary>
@ -66,7 +64,7 @@ namespace MatFileHandler
{
var flags = ConstructArrayFlags(ArrayType.MxCell);
var elements = Enumerable.Repeat(MatArray.Empty() as IArray, dimensions.NumberOfElements()).ToList();
return new MatCellArray(flags, dimensions, string.Empty, elements);
return new MatCellArray(dimensions, elements);
}
/// <summary>
@ -84,7 +82,7 @@ namespace MatFileHandler
{
dictionary[field] = elements.ToList();
}
return new MatStructureArray(flags, dimensions, string.Empty, dictionary);
return new MatStructureArray(dimensions, dictionary);
}
/// <summary>
@ -107,7 +105,7 @@ namespace MatFileHandler
{
var flags = ConstructArrayFlags(ArrayType.MxChar);
var ushortArray = contents.ToCharArray().Select(c => (ushort)c).ToArray();
return new MatCharArrayOf<ushort>(flags, dimensions, string.Empty, ushortArray, contents);
return new MatCharArrayOf<ushort>(dimensions, ushortArray, contents);
}
/// <summary>
@ -129,9 +127,7 @@ namespace MatFileHandler
where T : struct
{
return new MatSparseArrayOf<T>(
GetStandardSparseArrayFlags<T>(),
dimensions,
string.Empty,
new Dictionary<(int, int), T>());
}
@ -255,15 +251,5 @@ namespace MatFileHandler
}
return ConstructArrayFlags(ArrayType.MxObject);
}
private SparseArrayFlags GetStandardSparseArrayFlags<T>()
{
var arrayFlags = GetStandardFlags<T>();
return new SparseArrayFlags
{
ArrayFlags = arrayFlags,
NzMax = 0,
};
}
}
}

View File

@ -1,4 +1,4 @@
// Copyright 2017-2018 Alexander Luzgarev
// Copyright 2017-2019 Alexander Luzgarev
namespace MatFileHandler
{

View File

@ -1,4 +1,4 @@
// Copyright 2017-2018 Alexander Luzgarev
// Copyright 2017-2019 Alexander Luzgarev
using System;
using System.Collections.Generic;
@ -16,18 +16,14 @@ namespace MatFileHandler
/// <summary>
/// Construct a complex sparse array.
/// </summary>
/// <param name="flags">Array flags.</param>
/// <param name="dimensions">Array dimensions.</param>
/// <param name="name">Array name.</param>
/// <param name="rowIndex">Row indices.</param>
/// <param name="columnIndex">Denotes index ranges for each column.</param>
/// <param name="data">Real parts of the values.</param>
/// <param name="imaginaryData">Imaginary parts of the values.</param>
/// <returns>A constructed array.</returns>
public static MatArray ConvertToMatSparseArrayOfComplex(
SparseArrayFlags flags,
int[] dimensions,
string name,
int[] rowIndex,
int[] columnIndex,
DataElement data,
@ -40,11 +36,11 @@ namespace MatFileHandler
throw new HandlerException("Couldn't read sparse array.");
}
var dataDictionary =
ConvertMatlabSparseToDictionary(
DataExtraction.ConvertMatlabSparseToDictionary(
rowIndex,
columnIndex,
j => new Complex(realParts[j], imaginaryParts[j]));
return new MatSparseArrayOf<Complex>(flags, dimensions, name, dataDictionary);
return new MatSparseArrayOf<Complex>(dimensions, dataDictionary);
}
/// <summary>
@ -53,15 +49,13 @@ namespace MatFileHandler
/// <typeparam name="T">Element type (Double or Boolean).</typeparam>
/// <param name="flags">Array flags.</param>
/// <param name="dimensions">Array dimensions.</param>
/// <param name="name">Array name.</param>
/// <param name="rowIndex">Row indices.</param>
/// <param name="columnIndex">Denotes index ranges for each column.</param>
/// <param name="data">The values.</param>
/// <returns>A constructed array.</returns>
public static MatArray ConvertToMatSparseArrayOf<T>(
SparseArrayFlags flags,
ArrayFlags flags,
int[] dimensions,
string name,
int[] rowIndex,
int[] columnIndex,
DataElement data)
@ -76,14 +70,14 @@ namespace MatFileHandler
throw new ArgumentException("Null data found.", "data");
}
var elements =
ConvertDataToSparseProperType<T>(data, flags.ArrayFlags.Variable.HasFlag(Variable.IsLogical));
ConvertDataToSparseProperType<T>(data, flags.Variable.HasFlag(Variable.IsLogical));
if (elements == null)
{
throw new HandlerException("Couldn't read sparse array.");
}
var dataDictionary =
ConvertMatlabSparseToDictionary(rowIndex, columnIndex, j => elements[j]);
return new MatSparseArrayOf<T>(flags, dimensions, name, dataDictionary);
DataExtraction.ConvertMatlabSparseToDictionary(rowIndex, columnIndex, j => elements[j]);
return new MatSparseArrayOf<T>(dimensions, dataDictionary);
}
/// <summary>
@ -92,7 +86,6 @@ namespace MatFileHandler
/// <typeparam name="T">Element type.</typeparam>
/// <param name="flags">Array flags.</param>
/// <param name="dimensions">Array dimensions.</param>
/// <param name="name">Array name.</param>
/// <param name="realData">Real parts of the values.</param>
/// <param name="imaginaryData">Imaginary parts of the values.</param>
/// <returns>A constructed array.</returns>
@ -104,7 +97,6 @@ namespace MatFileHandler
public static MatArray ConvertToMatNumericalArrayOf<T>(
ArrayFlags flags,
int[] dimensions,
string name,
DataElement realData,
DataElement imaginaryData)
where T : struct
@ -112,7 +104,7 @@ namespace MatFileHandler
if (flags.Variable.HasFlag(Variable.IsLogical))
{
var data = DataExtraction.GetDataAsUInt8(realData).ToArrayLazily().Select(x => x != 0).ToArray();
return new MatNumericalArrayOf<bool>(flags, dimensions, name, data);
return new MatNumericalArrayOf<bool>(dimensions, data);
}
switch (flags.Class)
{
@ -120,9 +112,9 @@ namespace MatFileHandler
switch (realData)
{
case MiNum<byte> dataByte:
return ConvertToMatCharArray(flags, dimensions, name, dataByte);
return ConvertToMatCharArray(dimensions, dataByte);
case MiNum<ushort> dataUshort:
return ConvertToMatCharArray(flags, dimensions, name, dataUshort);
return ConvertToMatCharArray(dimensions, dataUshort);
default:
throw new NotSupportedException("Only utf8, utf16 or ushort char arrays are supported.");
}
@ -146,25 +138,23 @@ namespace MatFileHandler
(dataArray as double[])
.Zip(dataArray2 as double[], (x, y) => new Complex(x, y))
.ToArray();
return new MatNumericalArrayOf<Complex>(flags, dimensions, name, complexArray);
return new MatNumericalArrayOf<Complex>(dimensions, complexArray);
}
var complexDataArray = dataArray.Zip(dataArray2, (x, y) => new ComplexOf<T>(x, y)).ToArray();
return new MatNumericalArrayOf<ComplexOf<T>>(flags, dimensions, name, complexDataArray);
return new MatNumericalArrayOf<ComplexOf<T>>(dimensions, complexDataArray);
}
return new MatNumericalArrayOf<T>(flags, dimensions, name, dataArray);
return new MatNumericalArrayOf<T>(dimensions, dataArray);
default:
throw new NotSupportedException();
}
}
private static MatCharArrayOf<byte> ConvertToMatCharArray(
ArrayFlags flags,
int[] dimensions,
string name,
MiNum<byte> dataElement)
{
var data = dataElement?.Data;
return new MatCharArrayOf<byte>(flags, dimensions, name, data, Encoding.UTF8.GetString(data));
return new MatCharArrayOf<byte>(dimensions, data, Encoding.UTF8.GetString(data));
}
private static T[] ConvertDataToProperType<T>(DataElement data, ArrayType arrayType)
@ -212,35 +202,14 @@ namespace MatFileHandler
}
private static MatCharArrayOf<ushort> ConvertToMatCharArray(
ArrayFlags flags,
int[] dimensions,
string name,
MiNum<ushort> dataElement)
{
var data = dataElement?.Data;
return new MatCharArrayOf<ushort>(
flags,
dimensions,
name,
data,
new string(data.Select(x => (char)x).ToArray()));
}
private static Dictionary<(int, int), T> ConvertMatlabSparseToDictionary<T>(
int[] rowIndex,
int[] columnIndex,
Func<int, T> get)
{
var result = new Dictionary<(int, int), T>();
for (var column = 0; column < columnIndex.Length - 1; column++)
{
for (var j = columnIndex[column]; j < columnIndex[column + 1]; j++)
{
var row = rowIndex[j];
result[(row, column)] = get(j);
}
}
return result;
}
}
}

View File

@ -1,4 +1,4 @@
// Copyright 2017-2018 Alexander Luzgarev
// Copyright 2017-2019 Alexander Luzgarev
using System;
using System.Collections.Generic;
@ -30,54 +30,10 @@ namespace MatFileHandler
/// </summary>
/// <param name="reader">Input reader.</param>
/// <returns>Data element.</returns>
public DataElement Read(BinaryReader reader)
public DataElementWithMetadata Read(BinaryReader reader)
{
var (dataReader, tag) = ReadTag(reader);
DataElement result;
switch (tag.Type)
{
case DataType.MiInt8:
result = ReadNum<sbyte>(tag, dataReader);
break;
case DataType.MiUInt8:
case DataType.MiUtf8:
result = ReadNum<byte>(tag, dataReader);
break;
case DataType.MiInt16:
result = ReadNum<short>(tag, dataReader);
break;
case DataType.MiUInt16:
case DataType.MiUtf16:
result = ReadNum<ushort>(tag, dataReader);
break;
case DataType.MiInt32:
result = ReadNum<int>(tag, dataReader);
break;
case DataType.MiUInt32:
result = ReadNum<uint>(tag, dataReader);
break;
case DataType.MiSingle:
result = ReadNum<float>(tag, dataReader);
break;
case DataType.MiDouble:
result = ReadNum<double>(tag, dataReader);
break;
case DataType.MiInt64:
result = ReadNum<long>(tag, dataReader);
break;
case DataType.MiUInt64:
result = ReadNum<ulong>(tag, dataReader);
break;
case DataType.MiMatrix:
result = ReadMatrix(tag, dataReader);
break;
case DataType.MiCompressed:
result = ReadCompressed(tag, dataReader);
break;
default:
throw new NotSupportedException("Unknown element.");
}
DataElementWithMetadata result = ReadElementWithFlags(tag, dataReader);
if (tag.Type != DataType.MiCompressed)
{
var position = reader.BaseStream.Position;
@ -181,17 +137,13 @@ namespace MatFileHandler
return new MiNum<T>(result);
}
private static SparseArrayFlags ReadSparseArrayFlags(DataElement element)
private static (ArrayFlags flags, uint nzMax) ReadSparseArrayFlags(DataElement element)
{
var arrayFlags = ReadArrayFlags(element);
var flagData = (element as MiNum<uint>)?.Data ??
throw new HandlerException("Unexpected type in sparse array flags.");
var nzMax = flagData[1];
return new SparseArrayFlags
{
ArrayFlags = arrayFlags,
NzMax = nzMax,
};
return (arrayFlags, nzMax);
}
private static (BinaryReader, Tag) ReadTag(BinaryReader reader)
@ -214,99 +166,114 @@ namespace MatFileHandler
private DataElement ContinueReadingCellArray(
BinaryReader reader,
ArrayFlags flags,
int[] dimensions,
string name)
int[] dimensions)
{
var numberOfElements = dimensions.NumberOfElements();
var elements = new List<IArray>();
for (var i = 0; i < numberOfElements; i++)
{
var element = Read(reader) as IArray;
var element = Read(reader).Element as IArray;
elements.Add(element);
}
return new MatCellArray(flags, dimensions, name, elements);
return new MatCellArray(dimensions, elements);
}
private DataElement ContinueReadingOpaque(BinaryReader reader)
private DataElementWithMetadata ContinueReadingOpaque(BinaryReader reader)
{
var nameElement = Read(reader) as MiNum<sbyte> ??
var nameElement = Read(reader).Element as MiNum<sbyte> ??
throw new HandlerException("Unexpected type in object name.");
var name = ReadName(nameElement);
var anotherElement = Read(reader) as MiNum<sbyte> ??
var anotherElement = Read(reader).Element as MiNum<sbyte> ??
throw new HandlerException("Unexpected type in object type description.");
var typeDescription = ReadName(anotherElement);
var classNameElement = Read(reader) as MiNum<sbyte> ??
var classNameElement = Read(reader).Element as MiNum<sbyte> ??
throw new HandlerException("Unexpected type in class name.");
var className = ReadName(classNameElement);
var dataElement = Read(reader);
var dataElement = Read(reader).Element;
var data = ReadData(dataElement);
if (data is MatNumericalArrayOf<uint> linkElement)
{
var (dimensions, indexToObjectId, classIndex) = ParseOpaqueData(linkElement.Data);
return new OpaqueLink(
name,
return new DataElementWithMetadata(
new OpaqueLink(
typeDescription,
className,
dimensions,
data,
indexToObjectId,
classIndex,
subsystemData);
subsystemData),
default,
name);
}
else
{
return new Opaque(name, typeDescription, className, new int[] { }, data);
return new DataElementWithMetadata(
new Opaque(
typeDescription,
className,
new int[] { },
data),
default,
name);
}
}
private DataElement ContinueReadingSparseArray(
private DataElementWithMetadata ContinueReadingSparseArray(
BinaryReader reader,
DataElement firstElement,
int[] dimensions,
string name)
{
var sparseArrayFlags = ReadSparseArrayFlags(firstElement);
var rowIndex = Read(reader) as MiNum<int> ??
var (arrayFlags, nzMax) = ReadSparseArrayFlags(firstElement);
var rowIndex = Read(reader).Element as MiNum<int> ??
throw new HandlerException("Unexpected type in row indices of a sparse array.");
var columnIndex = Read(reader) as MiNum<int> ??
var columnIndex = Read(reader).Element as MiNum<int> ??
throw new HandlerException("Unexpected type in column indices of a sparse array.");
var data = Read(reader);
if (sparseArrayFlags.ArrayFlags.Variable.HasFlag(Variable.IsLogical))
var data = Read(reader).Element;
if (arrayFlags.Variable.HasFlag(Variable.IsLogical))
{
return DataElementConverter.ConvertToMatSparseArrayOf<bool>(
sparseArrayFlags,
return new DataElementWithMetadata(
DataElementConverter.ConvertToMatSparseArrayOf<bool>(
arrayFlags,
dimensions,
name,
rowIndex.Data,
columnIndex.Data,
data);
data),
arrayFlags,
name,
nzMax);
}
if (sparseArrayFlags.ArrayFlags.Variable.HasFlag(Variable.IsComplex))
if (arrayFlags.Variable.HasFlag(Variable.IsComplex))
{
var imaginaryData = Read(reader);
return DataElementConverter.ConvertToMatSparseArrayOfComplex(
sparseArrayFlags,
var imaginaryData = Read(reader).Element;
return new DataElementWithMetadata(
DataElementConverter.ConvertToMatSparseArrayOfComplex(
dimensions,
name,
rowIndex.Data,
columnIndex.Data,
data,
imaginaryData);
imaginaryData),
arrayFlags,
name,
nzMax);
}
switch (data)
{
case MiNum<double> _:
return DataElementConverter.ConvertToMatSparseArrayOf<double>(
sparseArrayFlags,
return new DataElementWithMetadata(
DataElementConverter.ConvertToMatSparseArrayOf<double>(
arrayFlags,
dimensions,
name,
rowIndex.Data,
columnIndex.Data,
data);
data),
arrayFlags,
name,
nzMax);
default:
throw new NotSupportedException("Only double and logical sparse arrays are supported.");
}
@ -314,12 +281,10 @@ namespace MatFileHandler
private DataElement ContinueReadingStructure(
BinaryReader reader,
ArrayFlags flags,
int[] dimensions,
string name,
int fieldNameLength)
{
var element = Read(reader);
var element = Read(reader).Element;
var fieldNames = ReadFieldNames(element as MiNum<sbyte>, fieldNameLength);
var fields = new Dictionary<string, List<IArray>>();
foreach (var fieldName in fieldNames)
@ -332,15 +297,15 @@ namespace MatFileHandler
{
foreach (var fieldName in fieldNames)
{
var field = Read(reader) as IArray;
var field = Read(reader).Element as IArray;
fields[fieldName].Add(field);
}
}
return new MatStructureArray(flags, dimensions, name, fields);
return new MatStructureArray(dimensions, fields);
}
private DataElement Read(Stream stream)
private DataElementWithMetadata Read(Stream stream)
{
using (var reader = new BinaryReader(stream))
{
@ -348,7 +313,7 @@ namespace MatFileHandler
}
}
private DataElement ReadCompressed(Tag tag, BinaryReader reader)
private DataElementWithMetadata ReadCompressed(Tag tag, BinaryReader reader)
{
reader.ReadBytes(2);
var compressedData = new byte[tag.Length - 6];
@ -367,28 +332,76 @@ namespace MatFileHandler
return Read(resultStream);
}
private DataElement ReadMatrix(Tag tag, BinaryReader reader)
private DataElementWithMetadata ReadElementWithFlags(Tag tag, BinaryReader reader)
{
switch (tag.Type)
{
case DataType.MiMatrix:
return ReadMatrix(tag, reader);
case DataType.MiCompressed:
return ReadCompressed(tag, reader);
default:
var element = ReadElementWithoutFlags(tag, reader);
return new DataElementWithMetadata(element);
}
}
private DataElement ReadElementWithoutFlags(Tag tag, BinaryReader reader)
{
switch (tag.Type)
{
case DataType.MiInt8:
return ReadNum<sbyte>(tag, reader);
case DataType.MiUInt8:
case DataType.MiUtf8:
return ReadNum<byte>(tag, reader);
case DataType.MiInt16:
return ReadNum<short>(tag, reader);
case DataType.MiUInt16:
case DataType.MiUtf16:
return ReadNum<ushort>(tag, reader);
case DataType.MiInt32:
return ReadNum<int>(tag, reader);
case DataType.MiUInt32:
return ReadNum<uint>(tag, reader);
case DataType.MiSingle:
return ReadNum<float>(tag, reader);
case DataType.MiDouble:
return ReadNum<double>(tag, reader);
case DataType.MiInt64:
return ReadNum<long>(tag, reader);
case DataType.MiUInt64:
return ReadNum<ulong>(tag, reader);
default:
throw new NotSupportedException("Unknown element.");
}
}
private DataElementWithMetadata ReadMatrix(Tag tag, BinaryReader reader)
{
if (tag.Length == 0)
{
return MatArray.Empty();
return new DataElementWithMetadata(MatArray.Empty());
}
var element1 = Read(reader);
var element1 = Read(reader).Element;
var flags = ReadArrayFlags(element1);
if (flags.Class == ArrayType.MxOpaque)
{
return ContinueReadingOpaque(reader);
}
var element2 = Read(reader) as MiNum<int> ??
throw new HandlerException("Unexpected type in array dimensions data.");
var element2 =
Read(reader).Element as MiNum<int>
?? throw new HandlerException("Unexpected type in array dimensions data.");
var dimensions = ReadDimensionsArray(element2);
var element3 = Read(reader) as MiNum<sbyte> ?? throw new HandlerException("Unexpected type in array name.");
var element3 =
Read(reader).Element as MiNum<sbyte>
?? throw new HandlerException("Unexpected type in array name.");
var name = ReadName(element3);
if (flags.Class == ArrayType.MxCell)
{
return ContinueReadingCellArray(reader, flags, dimensions, name);
return new DataElementWithMetadata(ContinueReadingCellArray(reader, dimensions));
}
if (flags.Class == ArrayType.MxSparse)
@ -396,12 +409,12 @@ namespace MatFileHandler
return ContinueReadingSparseArray(reader, element1, dimensions, name);
}
var element4 = Read(reader);
var element4 = Read(reader).Element;
var data = ReadData(element4);
DataElement imaginaryData = null;
if (flags.Variable.HasFlag(Variable.IsComplex))
{
var element5 = Read(reader);
var element5 = Read(reader).Element;
imaginaryData = ReadData(element5);
}
@ -410,7 +423,10 @@ namespace MatFileHandler
var fieldNameLengthElement = data as MiNum<int> ??
throw new HandlerException(
"Unexpected type in structure field name length.");
return ContinueReadingStructure(reader, flags, dimensions, name, fieldNameLengthElement.Data[0]);
return new DataElementWithMetadata(
ContinueReadingStructure(reader, dimensions, fieldNameLengthElement.Data[0]),
flags,
name);
}
switch (flags.Class)
@ -419,103 +435,129 @@ namespace MatFileHandler
switch (data)
{
case MiNum<byte> _:
return DataElementConverter.ConvertToMatNumericalArrayOf<byte>(
return new DataElementWithMetadata(
DataElementConverter.ConvertToMatNumericalArrayOf<byte>(
flags,
dimensions,
name,
data,
imaginaryData);
imaginaryData),
flags,
name);
case MiNum<ushort> _:
return DataElementConverter.ConvertToMatNumericalArrayOf<ushort>(
return new DataElementWithMetadata(
DataElementConverter.ConvertToMatNumericalArrayOf<ushort>(
flags,
dimensions,
name,
data,
imaginaryData);
imaginaryData),
flags,
name);
default:
throw new NotSupportedException(
$"This type of char array ({data.GetType()}) is not supported.");
}
case ArrayType.MxInt8:
return DataElementConverter.ConvertToMatNumericalArrayOf<sbyte>(
return new DataElementWithMetadata(
DataElementConverter.ConvertToMatNumericalArrayOf<sbyte>(
flags,
dimensions,
name,
data,
imaginaryData);
imaginaryData),
flags,
name);
case ArrayType.MxUInt8:
if (flags.Variable.HasFlag(Variable.IsLogical))
{
return DataElementConverter.ConvertToMatNumericalArrayOf<bool>(
return new DataElementWithMetadata(
DataElementConverter.ConvertToMatNumericalArrayOf<bool>(
flags,
dimensions,
name,
data,
imaginaryData);
imaginaryData),
flags,
name);
}
return DataElementConverter.ConvertToMatNumericalArrayOf<byte>(
return new DataElementWithMetadata(
DataElementConverter.ConvertToMatNumericalArrayOf<byte>(
flags,
dimensions,
name,
data,
imaginaryData);
imaginaryData),
flags,
name);
case ArrayType.MxInt16:
return DataElementConverter.ConvertToMatNumericalArrayOf<short>(
return new DataElementWithMetadata(
DataElementConverter.ConvertToMatNumericalArrayOf<short>(
flags,
dimensions,
name,
data,
imaginaryData);
imaginaryData),
flags,
name);
case ArrayType.MxUInt16:
return DataElementConverter.ConvertToMatNumericalArrayOf<ushort>(
return new DataElementWithMetadata(
DataElementConverter.ConvertToMatNumericalArrayOf<ushort>(
flags,
dimensions,
name,
data,
imaginaryData);
imaginaryData),
flags,
name);
case ArrayType.MxInt32:
return DataElementConverter.ConvertToMatNumericalArrayOf<int>(
return new DataElementWithMetadata(
DataElementConverter.ConvertToMatNumericalArrayOf<int>(
flags,
dimensions,
name,
data,
imaginaryData);
imaginaryData),
flags,
name);
case ArrayType.MxUInt32:
return DataElementConverter.ConvertToMatNumericalArrayOf<uint>(
return new DataElementWithMetadata(
DataElementConverter.ConvertToMatNumericalArrayOf<uint>(
flags,
dimensions,
name,
data,
imaginaryData);
imaginaryData),
flags,
name);
case ArrayType.MxInt64:
return DataElementConverter.ConvertToMatNumericalArrayOf<long>(
return new DataElementWithMetadata(
DataElementConverter.ConvertToMatNumericalArrayOf<long>(
flags,
dimensions,
name,
data,
imaginaryData);
imaginaryData),
flags,
name);
case ArrayType.MxUInt64:
return DataElementConverter.ConvertToMatNumericalArrayOf<ulong>(
return new DataElementWithMetadata(
DataElementConverter.ConvertToMatNumericalArrayOf<ulong>(
flags,
dimensions,
name,
data,
imaginaryData);
imaginaryData),
flags,
name);
case ArrayType.MxSingle:
return DataElementConverter.ConvertToMatNumericalArrayOf<float>(
return new DataElementWithMetadata(
DataElementConverter.ConvertToMatNumericalArrayOf<float>(
flags,
dimensions,
name,
data,
imaginaryData);
imaginaryData),
flags,
name);
case ArrayType.MxDouble:
return DataElementConverter.ConvertToMatNumericalArrayOf<double>(
return new DataElementWithMetadata(
DataElementConverter.ConvertToMatNumericalArrayOf<double>(
flags,
dimensions,
name,
data,
imaginaryData);
imaginaryData),
flags,
name);
default:
throw new HandlerException("Unknown data type.");
}

View File

@ -0,0 +1,54 @@
// Copyright 2017-2019 Alexander Luzgarev
namespace MatFileHandler
{
/// <summary>
/// Data element together with array flags, variable name, and sparse array's nzMax value.
/// </summary>
internal class DataElementWithMetadata
{
/// <summary>
/// Initializes a new instance of the <see cref="DataElementWithMetadata"/> class.
/// </summary>
/// <param name="element">Data element.</param>
/// <param name="flags">Array flags.</param>
/// <param name="name">Variable name.</param>
/// <param name="nzMax">nzMax (for sparse arrays).</param>
public DataElementWithMetadata(DataElement element, ArrayFlags flags, string name, uint nzMax = 0)
{
Element = element;
Flags = flags;
Name = name;
NzMax = nzMax;
}
/// <summary>
/// Initializes a new instance of the <see cref="DataElementWithMetadata"/> class.
/// </summary>
/// <param name="element">Data element.</param>
public DataElementWithMetadata(DataElement element)
{
Element = element;
}
/// <summary>
/// Gets data element.
/// </summary>
public DataElement Element { get; }
/// <summary>
/// Gets array flags.
/// </summary>
public ArrayFlags Flags { get; }
/// <summary>
/// Gets variable name.
/// </summary>
public string Name { get; }
/// <summary>
/// Gets nzMax (for sparse arrays).
/// </summary>
public uint NzMax { get; }
}
}

View File

@ -1,4 +1,4 @@
// Copyright 2017-2018 Alexander Luzgarev
// Copyright 2017-2019 Alexander Luzgarev
using System;
using System.Collections.Generic;
@ -362,5 +362,30 @@ namespace MatFileHandler
throw new HandlerException(
$"Expected data element that would be convertible to uint64, found {element.GetType()}.");
}
/// <summary>
/// Convert sparse MATLAB data into dictionary.
/// </summary>
/// <typeparam name="T">Array element type.</typeparam>
/// <param name="rowIndex">Array of row indices.</param>
/// <param name="columnIndex">Array of column indices.</param>
/// <param name="get">Getter function.</param>
/// <returns>Dictionary mapping (row, column) pairs to value.</returns>
public static Dictionary<(int, int), T> ConvertMatlabSparseToDictionary<T>(
int[] rowIndex,
int[] columnIndex,
Func<int, T> get)
{
var result = new Dictionary<(int, int), T>();
for (var column = 0; column < columnIndex.Length - 1; column++)
{
for (var j = columnIndex[column]; j < columnIndex[column + 1]; j++)
{
var row = rowIndex[j];
result[(row, column)] = get(j);
}
}
return result;
}
}
}

View File

@ -1,4 +1,4 @@
// Copyright 2017-2018 Alexander Luzgarev
// Copyright 2017-2019 Alexander Luzgarev
using System;

View File

@ -1,4 +1,4 @@
// Copyright 2017-2018 Alexander Luzgarev
// Copyright 2017-2019 Alexander Luzgarev
using System;
using System.Linq;

View File

@ -1,4 +1,4 @@
// Copyright 2017-2018 Alexander Luzgarev
// Copyright 2017-2019 Alexander Luzgarev
using System.Linq;

View File

@ -1,4 +1,4 @@
// Copyright 2017-2018 Alexander Luzgarev
// Copyright 2017-2019 Alexander Luzgarev
using System;

View File

@ -1,4 +1,4 @@
// Copyright 2017-2018 Alexander Luzgarev
// Copyright 2017-2019 Alexander Luzgarev
using System;

View File

@ -0,0 +1,81 @@
// Copyright 2017-2019 Alexander Luzgarev
using System;
using System.Runtime.InteropServices;
using HDF.PInvoke;
namespace MatFileHandler.Hdf
{
/// <summary>
/// Wrapper for HDF attribute.
/// </summary>
internal struct Attribute : IDisposable
{
/// <summary>
/// Initializes a new instance of the <see cref="Attribute"/> struct.
/// </summary>
/// <param name="locationId">Containing location id.</param>
/// <param name="name">Attribute name.</param>
public Attribute(long locationId, string name)
{
Id = H5A.open_by_name(locationId, ".", name);
}
/// <summary>
/// Gets attribute id.
/// </summary>
public long Id { get; private set; }
/// <inheritdoc />
public void Dispose()
{
if (Id != -1)
{
H5A.close(Id);
Id = -1;
}
}
/// <summary>
/// Get HDF type of the attribute.
/// </summary>
/// <returns>HDF type.</returns>
public Type GetHdfType()
{
return new Type(H5A.get_type(Id));
}
/// <summary>
/// Get HDF space of the attribute.
/// </summary>
/// <returns>HDF space.</returns>
public Space GetSpace()
{
return new Space(H5A.get_space(Id));
}
/// <summary>
/// Read attribute value as boolean.
/// </summary>
/// <returns>Attribute value.</returns>
public bool ReadBool()
{
using (var h = new MemoryHandle(sizeof(int)))
{
H5A.read(Id, H5T.NATIVE_INT, h.Handle);
var result = Marshal.ReadInt32(h.Handle);
return result != 0;
}
}
/// <summary>
/// Read attribute value to the provided memory handle.
/// </summary>
/// <param name="handle">Target memory handle.</param>
/// <param name="type">HDF type to read from the attribute.</param>
public void ReadToHandle(MemoryHandle handle, Type type)
{
H5A.read(Id, type.Id, handle.Handle);
}
}
}

View File

@ -0,0 +1,71 @@
// Copyright 2017-2019 Alexander Luzgarev
using System;
using HDF.PInvoke;
namespace MatFileHandler.Hdf
{
/// <summary>
/// HDF class.
/// </summary>
internal struct Class : IEquatable<Class>
{
/// <summary>
/// Compound class.
/// </summary>
public static readonly Class Compound = new Class(H5T.class_t.COMPOUND);
/// <summary>
/// Reference class.
/// </summary>
public static readonly Class Reference = new Class(H5T.class_t.REFERENCE);
/// <summary>
/// String class.
/// </summary>
public static readonly Class String = new Class(H5T.class_t.STRING);
private readonly H5T.class_t classT;
/// <summary>
/// Initializes a new instance of the <see cref="Class"/> struct.
/// </summary>
/// <param name="classT">HDF class_t.</param>
public Class(H5T.class_t classT)
{
this.classT = classT;
}
public static bool operator ==(Class one, Class other)
{
return one.Equals(other);
}
public static bool operator !=(Class one, Class other)
{
return !one.Equals(other);
}
/// <summary>
/// Check if the class is equal to the other class.
/// </summary>
/// <param name="other">Other class.</param>
/// <returns>True iff the classes are equal.</returns>
public bool Equals(Class other)
{
return classT == other.classT;
}
/// <inheritdoc />
public override bool Equals(object obj)
{
return obj is Class other && Equals(other);
}
/// <inheritdoc />
public override int GetHashCode()
{
return (int)classT;
}
}
}

View File

@ -0,0 +1,104 @@
// Copyright 2017-2019 Alexander Luzgarev
using System;
using HDF.PInvoke;
namespace MatFileHandler.Hdf
{
/// <summary>
/// HDF dataset.
/// </summary>
internal struct Dataset : IDisposable
{
/// <summary>
/// Initializes a new instance of the <see cref="Dataset"/> struct.
/// </summary>
/// <param name="datasetId">Dataset id.</param>
public Dataset(long datasetId)
{
Id = datasetId;
}
/// <summary>
/// Initializes a new instance of the <see cref="Dataset"/> struct.
/// </summary>
/// <param name="groupId">Containing group id.</param>
/// <param name="name">Name of the dataset in the group.</param>
public Dataset(long groupId, string name)
{
Id = H5D.open(groupId, name);
}
/// <summary>
/// Gets dataset id.
/// </summary>
public long Id { get; private set; }
/// <summary>
/// Check if dataset attribute with the given name exists.
/// </summary>
/// <param name="name">Attribute name.</param>
/// <returns>True iff dataset has an attribute with this name.</returns>
public bool AttributeExists(string name)
{
return H5A.exists_by_name(Id, ".", name) != 0;
}
/// <inheritdoc />
public void Dispose()
{
if (Id != -1)
{
H5D.close(Id);
Id = -1;
}
}
/// <summary>
/// Open attribute with given name.
/// </summary>
/// <param name="name">Attribute name.</param>
/// <returns>Attribute.</returns>
public Attribute GetAttribute(string name)
{
return new Attribute(Id, name);
}
/// <summary>
/// Get HDF space of the dataset.
/// </summary>
/// <returns>HDF space.</returns>
public Space GetHdfSpace()
{
return new Space(H5D.get_space(Id));
}
/// <summary>
/// Get HDF type of the dataset.
/// </summary>
/// <returns>HDF type.</returns>
public Type GetHdfType()
{
return new Type(H5D.get_type(Id));
}
/// <summary>
/// Get storage size of the dataset.
/// </summary>
/// <returns>Storage size.</returns>
public int GetStorageSize()
{
return (int)H5D.get_storage_size(Id);
}
/// <summary>
/// Read the contents of the dataset into the memory handle.
/// </summary>
/// <param name="type">HDF type of the data to read.</param>
/// <param name="handle">Memory handle.</param>
public void ReadToHandle(Type type, MemoryHandle handle)
{
H5D.read(Id, type.Id, H5S.ALL, H5S.ALL, H5P.DEFAULT, handle.Handle);
}
}
}

View File

@ -0,0 +1,58 @@
// Copyright 2017-2019 Alexander Luzgarev
using System;
using HDF.PInvoke;
namespace MatFileHandler.Hdf
{
/// <summary>
/// Hdf group.
/// </summary>
internal struct Group : IDisposable
{
/// <summary>
/// Initializes a new instance of the <see cref="Group"/> struct.
/// </summary>
/// <param name="groupId">Containing group id.</param>
/// <param name="name">Name of the subgroup in the containing group.</param>
public Group(long groupId, string name)
{
Id = H5G.open(groupId, name);
}
/// <summary>
/// Gets group id.
/// </summary>
public long Id { get; private set; }
/// <summary>
/// Check if group attribute with the given name exists.
/// </summary>
/// <param name="name">Attribute name.</param>
/// <returns>True iff group has an attribute with this name.</returns>
public bool AttributeExists(string name)
{
return H5A.exists_by_name(Id, ".", name) != 0;
}
/// <inheritdoc />
public void Dispose()
{
if (Id != -1)
{
H5G.close(Id);
Id = -1;
}
}
/// <summary>
/// Get group attribute.
/// </summary>
/// <param name="name">Attribute name.</param>
/// <returns>Attribute.</returns>
public Attribute GetAttribute(string name)
{
return new Attribute(Id, name);
}
}
}

View File

@ -0,0 +1,80 @@
// Copyright 2017-2019 Alexander Luzgarev
namespace MatFileHandler.Hdf
{
/// <summary>
/// Matlab classes as they appear in HDF files.
/// </summary>
internal enum MatlabClass
{
/// <summary>
/// Empty array.
/// </summary>
MEmpty,
/// <summary>
/// Char array.
/// </summary>
MChar,
/// <summary>
/// Int8 array.
/// </summary>
MInt8,
/// <summary>
/// UInt8 array.
/// </summary>
MUInt8,
/// <summary>
/// Int16 array.
/// </summary>
MInt16,
/// <summary>
/// UInt16 array.
/// </summary>
MUInt16,
/// <summary>
/// Int32 array.
/// </summary>
MInt32,
/// <summary>
/// UInt32 array.
/// </summary>
MUInt32,
/// <summary>
/// Int64 array.
/// </summary>
MInt64,
/// <summary>
/// UInt64 array.
/// </summary>
MUInt64,
/// <summary>
/// Single-precision floating point array.
/// </summary>
MSingle,
/// <summary>
/// Double-precision floating point array.
/// </summary>
MDouble,
/// <summary>
/// Cell array.
/// </summary>
MCell,
/// <summary>
/// Logical array.
/// </summary>
MLogical,
}
}

View File

@ -0,0 +1,37 @@
// Copyright 2017-2019 Alexander Luzgarev
using System;
using System.Runtime.InteropServices;
namespace MatFileHandler.Hdf
{
/// <summary>
/// Wrapper around IntPtr to array in unmanaged memory.
/// </summary>
internal sealed class MemoryHandle : IDisposable
{
/// <summary>
/// Initializes a new instance of the <see cref="MemoryHandle"/> class.
/// </summary>
/// <param name="sizeInBytes">Size of the memory to be allocated.</param>
internal MemoryHandle(int sizeInBytes)
{
Handle = Marshal.AllocHGlobal(sizeInBytes);
}
/// <summary>
/// Gets wrapped IntPtr.
/// </summary>
internal IntPtr Handle { get; private set; }
/// <inheritdoc/>
public void Dispose()
{
if (Handle != IntPtr.Zero)
{
Marshal.FreeHGlobal(Handle);
Handle = IntPtr.Zero;
}
}
}
}

View File

@ -0,0 +1,85 @@
// Copyright 2017-2019 Alexander Luzgarev
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using HDF.PInvoke;
namespace MatFileHandler.Hdf
{
/// <summary>
/// Array of HDF references stored in an HDF dataset.
/// </summary>
internal struct ReferenceArray : IDisposable, IEnumerable<Dataset>
{
private readonly Dataset[] references;
/// <summary>
/// Initializes a new instance of the <see cref="ReferenceArray"/> struct.
/// </summary>
/// <param name="dataset">Containing dataset.</param>
/// <param name="size">Array size.</param>
public ReferenceArray(Dataset dataset, int size)
{
Dataset = dataset;
Size = size;
Buf = new MemoryHandle(Marshal.SizeOf(default(IntPtr)) * size);
Dataset.ReadToHandle(Type.Reference, Buf);
references = new Dataset[size];
for (var i = 0; i < size; i++)
{
references[i] =
new Dataset(H5R.dereference(
dataset.Id,
H5P.DEFAULT,
H5R.type_t.OBJECT,
Buf.Handle + (i * Marshal.SizeOf(default(IntPtr)))));
}
}
/// <summary>
/// Gets containing dataset.
/// </summary>
public Dataset Dataset { get; }
/// <summary>
/// Gets references.
/// </summary>
public IReadOnlyList<Dataset> References => references;
/// <summary>
/// Gets array size.
/// </summary>
public int Size { get; }
private MemoryHandle Buf { get; }
/// <inheritdoc />
public void Dispose()
{
Buf?.Dispose();
if (References is null)
{
return;
}
foreach (var reference in References)
{
reference.Dispose();
}
}
/// <inheritdoc />
public IEnumerator<Dataset> GetEnumerator()
{
return ((IEnumerable<Dataset>)References).GetEnumerator();
}
/// <inheritdoc />
IEnumerator IEnumerable.GetEnumerator()
{
return References.GetEnumerator();
}
}
}

View File

@ -0,0 +1,48 @@
// Copyright 2017-2019 Alexander Luzgarev
using System.Linq;
using HDF.PInvoke;
namespace MatFileHandler.Hdf
{
/// <summary>
/// HDF space.
/// </summary>
internal struct Space
{
/// <summary>
/// Initializes a new instance of the <see cref="Space"/> struct.
/// </summary>
/// <param name="id">Space id.</param>
public Space(long id)
{
Id = id;
}
/// <summary>
/// Gets space id.
/// </summary>
public long Id { get; }
/// <summary>
/// Get space rank.
/// </summary>
/// <returns>Space rank.</returns>
public int GetRank()
{
return H5S.get_simple_extent_ndims(Id);
}
/// <summary>
/// Get dimensions of the space.
/// </summary>
/// <returns>Space dimensions.</returns>
public int[] GetDimensions()
{
var dims = new ulong[GetRank()];
H5S.get_simple_extent_dims(Id, dims, null);
System.Array.Reverse(dims);
return dims.Select(x => (int)x).ToArray();
}
}
}

147
MatFileHandler/Hdf/Type.cs Normal file
View File

@ -0,0 +1,147 @@
// Copyright 2017-2019 Alexander Luzgarev
using System;
using HDF.PInvoke;
namespace MatFileHandler.Hdf
{
/// <summary>
/// HDF type.
/// </summary>
internal struct Type
{
/// <summary>
/// Initializes a new instance of the <see cref="Type"/> struct.
/// </summary>
/// <param name="id">Type id.</param>
public Type(long id)
{
Id = id;
}
/// <summary>
/// Gets HDF string type.
/// </summary>
public static Type CS1 => new Type(H5T.C_S1);
/// <summary>
/// Gets HDF double type.
/// </summary>
public static Type NativeDouble => new Type(H5T.NATIVE_DOUBLE);
/// <summary>
/// Gets HDF float (single) type.
/// </summary>
public static Type NativeFloat => new Type(H5T.NATIVE_FLOAT);
/// <summary>
/// Gets HDF int type.
/// </summary>
public static Type NativeInt => new Type(H5T.NATIVE_INT);
/// <summary>
/// Gets HDF int16 type.
/// </summary>
public static Type NativeInt16 => new Type(H5T.NATIVE_INT16);
/// <summary>
/// Gets HDF int32 type.
/// </summary>
public static Type NativeInt32 => new Type(H5T.NATIVE_INT32);
/// <summary>
/// Gets HDF int64 type.
/// </summary>
public static Type NativeInt64 => new Type(H5T.NATIVE_INT64);
/// <summary>
/// Gets HDF int8 type.
/// </summary>
public static Type NativeInt8 => new Type(H5T.NATIVE_INT8);
/// <summary>
/// Gets HDF uint type.
/// </summary>
public static Type NativeUInt => new Type(H5T.NATIVE_UINT);
/// <summary>
/// Gets HDF uint16 type
/// </summary>
public static Type NativeUInt16 => new Type(H5T.NATIVE_UINT16);
/// <summary>
/// Gets HDF uint32 type.
/// </summary>
public static Type NativeUInt32 => new Type(H5T.NATIVE_UINT32);
/// <summary>
/// Gets HDF uint64 type.
/// </summary>
public static Type NativeUInt64 => new Type(H5T.NATIVE_UINT64);
/// <summary>
/// Gets HDF uint8 type.
/// </summary>
public static Type NativeUInt8 => new Type(H5T.NATIVE_UINT8);
/// <summary>
/// Gets HDF reference type.
/// </summary>
public static Type Reference => new Type(H5T.STD_REF_OBJ);
/// <summary>
/// Gets type id.
/// </summary>
public long Id { get; }
/// <summary>
/// Create compound type of given size.
/// </summary>
/// <param name="size">Size of the type.</param>
/// <returns>The created type.</returns>
public static Type CreateCompound(int size)
{
return new Type(H5T.create(H5T.class_t.COMPOUND, (IntPtr)size));
}
/// <summary>
/// Get class of the type.
/// </summary>
/// <returns>Class of the type.</returns>
public Class GetClass()
{
return new Class(H5T.get_class(Id));
}
/// <summary>
/// Get size of the type.
/// </summary>
/// <returns>Size of the type.</returns>
public int GetSize()
{
return (int)H5T.get_size(Id);
}
/// <summary>
/// Insert a field into the type.
/// </summary>
/// <param name="name">Field name.</param>
/// <param name="fieldType">Field type.</param>
public void InsertField(string name, Type fieldType)
{
H5T.insert(Id, name, IntPtr.Zero, fieldType.Id);
}
/// <summary>
/// Create type copy with same class and given size.
/// </summary>
/// <param name="size">New size.</param>
/// <returns>New type.</returns>
public Type WithSize(int size)
{
var classId = H5T.copy(Id);
H5T.set_size(classId, (IntPtr)size);
return new Type(classId);
}
}
}

View File

@ -0,0 +1,667 @@
// Copyright 2017-2019 Alexander Luzgarev
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Text;
using HDF.PInvoke;
using MatFileHandler.Hdf;
namespace MatFileHandler
{
/// <summary>
/// Reader of HDF files containing MATLAB data.
/// </summary>
internal class HdfFileReader
{
private const string ClassAttributeName = "MATLAB_class";
private const string GlobalAttributeName = "MATLAB_global";
private const string SparseAttributeName = "MATLAB_sparse";
private readonly long fileId;
private List<IVariable> variables;
/// <summary>
/// Initializes a new instance of the <see cref="HdfFileReader"/> class.
/// </summary>
/// <param name="fileId">File id to read data from.</param>
internal HdfFileReader(long fileId)
{
this.fileId = fileId;
}
/// <summary>
/// Read MATLAB data from the HDF file.
/// </summary>
/// <returns>MATLAB data file contents.</returns>
internal IMatFile Read()
{
variables = new List<IVariable>();
var group_info = default(H5G.info_t);
H5G.get_info(fileId, ref group_info);
var numberOfVariables = group_info.nlinks;
ulong idx = 0;
while (idx < numberOfVariables)
{
H5L.iterate(
fileId,
H5.index_t.NAME,
H5.iter_order_t.NATIVE,
ref idx,
VariableIterator,
IntPtr.Zero);
}
return new MatFile(variables);
}
private static MatlabClass ArrayTypeFromMatlabClassName(string matlabClassName)
{
switch (matlabClassName)
{
case "canonical empty":
return MatlabClass.MEmpty;
case "logical":
return MatlabClass.MLogical;
case "char":
return MatlabClass.MChar;
case "int8":
return MatlabClass.MInt8;
case "uint8":
return MatlabClass.MUInt8;
case "int16":
return MatlabClass.MInt16;
case "uint16":
return MatlabClass.MUInt16;
case "int32":
return MatlabClass.MInt32;
case "uint32":
return MatlabClass.MUInt32;
case "int64":
return MatlabClass.MInt64;
case "uint64":
return MatlabClass.MUInt64;
case "single":
return MatlabClass.MSingle;
case "double":
return MatlabClass.MDouble;
case "cell":
return MatlabClass.MCell;
}
throw new NotImplementedException();
}
private static T[] ConvertDataToProperType<T>(byte[] bytes, MatlabClass arrayType)
where T : struct
{
var length = bytes.Length;
var arrayElementSize = SizeOfArrayElement(arrayType);
var data = new T[length / arrayElementSize];
Buffer.BlockCopy(bytes, 0, data, 0, length);
return data;
}
private static int[] GetDimensionsOfDataset(Dataset dataset)
{
return dataset.GetHdfSpace().GetDimensions();
}
private static string GetMatlabClassFromAttribute(Hdf.Attribute attribute)
{
var type = attribute.GetHdfType();
var cl = type.GetClass();
if (cl != Class.String)
{
throw new NotImplementedException();
}
var typeIdSize = type.GetSize();
var copiedType = Hdf.Type.CS1.WithSize(type.GetSize());
var matlabClassNameBytes = new byte[typeIdSize];
using (var buf = new MemoryHandle(typeIdSize))
{
attribute.ReadToHandle(buf, copiedType);
Marshal.Copy(buf.Handle, matlabClassNameBytes, 0, typeIdSize);
}
var length = typeIdSize;
for (var i = 0; i < typeIdSize; i++)
{
if (matlabClassNameBytes[i] == 0)
{
length = i;
break;
}
}
return Encoding.ASCII.GetString(matlabClassNameBytes, 0, length);
}
private static string GetMatlabClassOfDataset(Dataset dataset)
{
using (var attribute = dataset.GetAttribute(ClassAttributeName))
{
return GetMatlabClassFromAttribute(attribute);
}
}
private static string GetMatlabClassOfGroup(Group group)
{
using (var attribute = group.GetAttribute(ClassAttributeName))
{
return GetMatlabClassFromAttribute(attribute);
}
}
private static H5O.type_t GetObjectType(long groupId, string fieldName)
{
var objectInfo = default(H5O.info_t);
H5O.get_info_by_name(groupId, fieldName, ref objectInfo);
return objectInfo.type;
}
private static Hdf.Type H5tTypeFromHdfMatlabClass(MatlabClass arrayType)
{
switch (arrayType)
{
case MatlabClass.MInt8:
return Hdf.Type.NativeInt8;
case MatlabClass.MUInt8:
case MatlabClass.MLogical:
return Hdf.Type.NativeUInt8;
case MatlabClass.MInt16:
return Hdf.Type.NativeInt16;
case MatlabClass.MUInt16:
return Hdf.Type.NativeUInt16;
case MatlabClass.MInt32:
return Hdf.Type.NativeInt32;
case MatlabClass.MUInt32:
return Hdf.Type.NativeUInt32;
case MatlabClass.MInt64:
return Hdf.Type.NativeInt64;
case MatlabClass.MUInt64:
return Hdf.Type.NativeUInt64;
case MatlabClass.MSingle:
return Hdf.Type.NativeFloat;
case MatlabClass.MDouble:
return Hdf.Type.NativeDouble;
}
throw new NotImplementedException();
}
private static IArray ReadCellArray(Dataset dataset, int[] dims)
{
var numberOfElements = dims.NumberOfElements();
var elements = new IArray[numberOfElements];
using (var array = new ReferenceArray(dataset, numberOfElements))
{
var i = 0;
foreach (var reference in array)
{
elements[i++] = ReadDataset(reference);
}
}
return new MatCellArray(dims, elements);
}
private static IArray ReadCharArray(Dataset dataset, int[] dims)
{
var storageSize = dataset.GetStorageSize();
var data = ReadDataset(dataset, Hdf.Type.NativeUInt16, storageSize);
var uInt16Data = new ushort[data.Length / sizeof(ushort)];
Buffer.BlockCopy(data, 0, uInt16Data, 0, data.Length);
var str = Encoding.Unicode.GetString(data);
return new MatCharArrayOf<ushort>(dims, uInt16Data, str);
}
private static (T[] real, T[] imaginary) ReadComplexData<T>(
Dataset dataset,
int dataSize,
MatlabClass arrayType)
where T : struct
{
var h5Type = H5tTypeFromHdfMatlabClass(arrayType);
var h5Size = h5Type.GetSize();
var h5tComplexReal = Hdf.Type.CreateCompound(h5Size);
h5tComplexReal.InsertField("real", h5Type);
var realData = ReadDataset(dataset, h5tComplexReal, dataSize);
var h5tComplexImaginary = Hdf.Type.CreateCompound(h5Size);
h5tComplexImaginary.InsertField("imag", h5Type);
var imaginaryData = ReadDataset(dataset, h5tComplexImaginary, dataSize);
var convertedRealData = ConvertDataToProperType<T>(realData, arrayType);
var convertedImaginaryData = ConvertDataToProperType<T>(imaginaryData, arrayType);
return (convertedRealData, convertedImaginaryData);
}
private static IArray ReadDataset(Dataset dataset)
{
var dims = GetDimensionsOfDataset(dataset);
var matlabClass = GetMatlabClassOfDataset(dataset);
var arrayType = ArrayTypeFromMatlabClassName(matlabClass);
switch (arrayType)
{
case MatlabClass.MEmpty:
return MatArray.Empty();
case MatlabClass.MLogical:
return ReadNumericalArray<bool>(dataset, dims, arrayType);
case MatlabClass.MChar:
return ReadCharArray(dataset, dims);
case MatlabClass.MInt8:
return ReadNumericalArray<sbyte>(dataset, dims, arrayType);
case MatlabClass.MUInt8:
return ReadNumericalArray<byte>(dataset, dims, arrayType);
case MatlabClass.MInt16:
return ReadNumericalArray<short>(dataset, dims, arrayType);
case MatlabClass.MUInt16:
return ReadNumericalArray<ushort>(dataset, dims, arrayType);
case MatlabClass.MInt32:
return ReadNumericalArray<int>(dataset, dims, arrayType);
case MatlabClass.MUInt32:
return ReadNumericalArray<uint>(dataset, dims, arrayType);
case MatlabClass.MInt64:
return ReadNumericalArray<long>(dataset, dims, arrayType);
case MatlabClass.MUInt64:
return ReadNumericalArray<ulong>(dataset, dims, arrayType);
case MatlabClass.MSingle:
return ReadNumericalArray<float>(dataset, dims, arrayType);
case MatlabClass.MDouble:
return ReadNumericalArray<double>(dataset, dims, arrayType);
case MatlabClass.MCell:
return ReadCellArray(dataset, dims);
}
throw new NotImplementedException($"Unknown array type: {arrayType}.");
}
private static byte[] ReadDataset(Dataset dataset, Hdf.Type elementType, int dataSize)
{
var data = new byte[dataSize];
using (var dataBuffer = new MemoryHandle(dataSize))
{
dataset.ReadToHandle(elementType, dataBuffer);
Marshal.Copy(dataBuffer.Handle, data, 0, dataSize);
}
return data;
}
private static string[] ReadFieldNames(long groupId)
{
// Try to read fields from MATLAB_fields.
using (var attr = new Hdf.Attribute(groupId, "MATLAB_fields"))
{
if (attr.Id == 0)
{
throw new NotImplementedException();
}
var dimensions = attr.GetSpace().GetDimensions();
var numberOfFields = dimensions.NumberOfElements();
var fieldType = attr.GetHdfType();
var fieldNamePointersSizeInBytes = numberOfFields * Marshal.SizeOf(default(H5T.hvl_t));
var fieldNamePointers = new IntPtr[numberOfFields * 2];
using (var fieldNamesBuf = new MemoryHandle(fieldNamePointersSizeInBytes))
{
attr.ReadToHandle(fieldNamesBuf, fieldType);
Marshal.Copy(fieldNamesBuf.Handle, fieldNamePointers, 0, numberOfFields * 2);
}
var fieldNames = new string[numberOfFields];
for (var i = 0; i < numberOfFields; i++)
{
var stringLength = fieldNamePointers[i * 2];
var stringPointer = fieldNamePointers[(i * 2) + 1];
fieldNames[i] = Marshal.PtrToStringAnsi(stringPointer, (int)stringLength);
}
return fieldNames;
}
}
private static IArray ReadGroup(Group group)
{
var matlabClass = GetMatlabClassOfGroup(group);
if (matlabClass == "struct")
{
return ReadStruct(group.Id);
}
if (group.AttributeExists(SparseAttributeName))
{
var arrayType = ArrayTypeFromMatlabClassName(matlabClass);
switch (arrayType)
{
case MatlabClass.MEmpty:
return MatArray.Empty();
case MatlabClass.MLogical:
return ReadSparseArray<bool>(group.Id, arrayType);
case MatlabClass.MInt8:
return ReadSparseArray<sbyte>(group.Id, arrayType);
case MatlabClass.MUInt8:
return ReadSparseArray<byte>(group.Id, arrayType);
case MatlabClass.MInt16:
return ReadSparseArray<short>(group.Id, arrayType);
case MatlabClass.MUInt16:
return ReadSparseArray<ushort>(group.Id, arrayType);
case MatlabClass.MInt32:
return ReadSparseArray<int>(group.Id, arrayType);
case MatlabClass.MUInt32:
return ReadSparseArray<uint>(group.Id, arrayType);
case MatlabClass.MInt64:
return ReadSparseArray<long>(group.Id, arrayType);
case MatlabClass.MUInt64:
return ReadSparseArray<ulong>(group.Id, arrayType);
case MatlabClass.MSingle:
return ReadSparseArray<float>(group.Id, arrayType);
case MatlabClass.MDouble:
return ReadSparseArray<double>(group.Id, arrayType);
default:
throw new NotSupportedException();
}
}
throw new NotImplementedException();
}
private static IEnumerable<ComplexOf<T>> CombineComplexOfData<T>(
IEnumerable<T> realData,
IEnumerable<T> imaginaryData)
where T : struct
{
return realData.Zip(
imaginaryData,
(x, y) => new ComplexOf<T>(x, y));
}
private static IEnumerable<Complex> CombineComplexData(
IEnumerable<double> realData,
IEnumerable<double> imaginaryData)
{
return realData.Zip(
imaginaryData,
(x, y) => new Complex(x, y));
}
private static IArray ReadNumericalArray<T>(Dataset dataset, int[] dims, MatlabClass arrayType)
where T : struct
{
var numberOfElements = dims.NumberOfElements();
var dataSize = numberOfElements * SizeOfArrayElement(arrayType);
var dataSetType = dataset.GetHdfType();
var dataSetTypeClass = dataSetType.GetClass();
var isCompound = dataSetTypeClass == Class.Compound;
if (isCompound)
{
var (convertedRealData, convertedImaginaryData) = ReadComplexData<T>(dataset, dataSize, arrayType);
if (arrayType == MatlabClass.MDouble)
{
var complexData =
CombineComplexData(
convertedRealData as double[],
convertedImaginaryData as double[])
.ToArray();
return new MatNumericalArrayOf<Complex>(dims, complexData);
}
else
{
var complexData =
CombineComplexOfData(
convertedRealData,
convertedImaginaryData)
.ToArray();
return new MatNumericalArrayOf<ComplexOf<T>>(dims, complexData);
}
}
var data = ReadDataset(dataset, H5tTypeFromHdfMatlabClass(arrayType), dataSize);
var convertedData = ConvertDataToProperType<T>(data, arrayType);
return new MatNumericalArrayOf<T>(dims, convertedData);
}
private static IArray ReadSparseArray<T>(long groupId, MatlabClass arrayType)
where T : struct
{
using (var sparseAttribute = new Hdf.Attribute(groupId, SparseAttributeName))
{
using (var numberOfRowsHandle = new MemoryHandle(sizeof(uint)))
{
sparseAttribute.ReadToHandle(numberOfRowsHandle, Hdf.Type.NativeUInt);
var numberOfRows = Marshal.ReadInt32(numberOfRowsHandle.Handle);
int[] rowIndex;
int[] columnIndex;
using (var irData = new Dataset(groupId, "ir"))
{
var ds = GetDimensionsOfDataset(irData);
var numberOfIr = ds.NumberOfElements();
var irBytes = ReadDataset(irData, Hdf.Type.NativeInt, numberOfIr * sizeof(int));
rowIndex = new int[numberOfIr];
Buffer.BlockCopy(irBytes, 0, rowIndex, 0, irBytes.Length);
}
using (var jcData = new Dataset(groupId, "jc"))
{
var ds = GetDimensionsOfDataset(jcData);
var numberOfJc = ds.NumberOfElements();
var jcBytes = ReadDataset(jcData, Hdf.Type.NativeInt, numberOfJc * sizeof(int));
columnIndex = new int[numberOfJc];
Buffer.BlockCopy(jcBytes, 0, columnIndex, 0, jcBytes.Length);
}
using (var data = new Dataset(groupId, "data"))
{
var ds = GetDimensionsOfDataset(data);
var dims = new int[2];
dims[0] = numberOfRows;
dims[1] = columnIndex.Length - 1;
var dataSize = ds.NumberOfElements() * SizeOfArrayElement(arrayType);
var storageSize = data.GetStorageSize();
var dataSetType = data.GetHdfType();
var dataSetTypeClass = dataSetType.GetClass();
var isCompound = dataSetTypeClass == Class.Compound;
if (isCompound)
{
var (convertedRealData, convertedImaginaryData) =
ReadComplexData<T>(data, dataSize, arrayType);
if (arrayType == MatlabClass.MDouble)
{
var complexData =
CombineComplexData(
convertedRealData as double[],
convertedImaginaryData as double[])
.ToArray();
var complexDataDictionary =
DataExtraction.ConvertMatlabSparseToDictionary(
rowIndex,
columnIndex,
j => complexData[j]);
return new MatSparseArrayOf<Complex>(dims, complexDataDictionary);
}
else
{
var complexData =
CombineComplexOfData<T>(
convertedRealData,
convertedImaginaryData)
.ToArray();
var complexDataDictionary =
DataExtraction.ConvertMatlabSparseToDictionary(
rowIndex,
columnIndex,
j => complexData[j]);
return new MatSparseArrayOf<ComplexOf<T>>(dims, complexDataDictionary);
}
}
var d = ReadDataset(data, H5tTypeFromHdfMatlabClass(arrayType), dataSize);
var elements = ConvertDataToProperType<T>(d, arrayType);
var dataDictionary =
DataExtraction.ConvertMatlabSparseToDictionary(rowIndex, columnIndex, j => elements[j]);
return new MatSparseArrayOf<T>(dims, dataDictionary);
}
}
}
}
private static IArray ReadStruct(long groupId)
{
var fieldNames = ReadFieldNames(groupId);
var firstObjectType = GetObjectType(groupId, fieldNames[0]);
if (firstObjectType == H5O.type_t.DATASET)
{
using (var firstField = new Dataset(groupId, fieldNames[0]))
{
var firstFieldType = firstField.GetHdfType();
if (firstFieldType.GetClass() == Class.Reference)
{
if (firstField.AttributeExists(ClassAttributeName))
{
throw new NotImplementedException();
}
else
{
var dimensions = GetDimensionsOfDataset(firstField);
var numberOfElements = dimensions.NumberOfElements();
var dictionary = new Dictionary<string, List<IArray>>();
foreach (var fieldName in fieldNames)
{
var fieldType = GetObjectType(groupId, fieldName);
dictionary[fieldName] = new List<IArray>();
switch (fieldType)
{
case H5O.type_t.DATASET:
using (var field = new Dataset(groupId, fieldName))
{
using (var array = new ReferenceArray(field, numberOfElements))
{
foreach (var reference in array)
{
var value = ReadDataset(reference);
dictionary[fieldName].Add(value);
}
}
}
break;
default:
throw new NotImplementedException();
}
}
return new MatStructureArray(dimensions, dictionary);
}
}
else
{
throw new NotImplementedException();
}
}
}
else
{
throw new NotImplementedException();
}
throw new NotImplementedException();
}
private static int SizeOfArrayElement(MatlabClass arrayType)
{
switch (arrayType)
{
case MatlabClass.MInt8:
case MatlabClass.MUInt8:
case MatlabClass.MLogical:
return 1;
case MatlabClass.MInt16:
case MatlabClass.MUInt16:
return 2;
case MatlabClass.MInt32:
case MatlabClass.MUInt32:
case MatlabClass.MSingle:
return 4;
case MatlabClass.MInt64:
case MatlabClass.MUInt64:
case MatlabClass.MDouble:
return 8;
}
throw new NotImplementedException();
}
private bool ReadGlobalFlag(Group group)
{
if (!group.AttributeExists(GlobalAttributeName))
{
return false;
}
using (var globalAttribute = group.GetAttribute(GlobalAttributeName))
{
return globalAttribute.ReadBool();
}
}
private bool ReadGlobalFlag(Dataset dataset)
{
if (!dataset.AttributeExists(GlobalAttributeName))
{
return false;
}
using (var globalAttribute = dataset.GetAttribute(GlobalAttributeName))
{
return globalAttribute.ReadBool();
}
}
private int VariableIterator(long group, IntPtr name, ref H5L.info_t info, IntPtr op_data)
{
var variableName = Marshal.PtrToStringAnsi(name);
var object_info = default(H5O.info_t);
H5O.get_info_by_name(group, variableName, ref object_info);
switch (object_info.type)
{
case H5O.type_t.DATASET:
using (var dataset = new Dataset(group, variableName))
{
var isGlobal = ReadGlobalFlag(dataset);
var value = ReadDataset(dataset);
variables.Add(new MatVariable(value, variableName, isGlobal));
}
break;
case H5O.type_t.GROUP:
if (variableName == "#refs#")
{
return 0;
}
using (var subGroup = new Group(group, variableName))
{
var isGlobal = ReadGlobalFlag(subGroup);
var groupValue = ReadGroup(subGroup);
variables.Add(new MatVariable(groupValue, variableName, isGlobal));
}
break;
default:
throw new NotImplementedException();
}
return 0;
}
}
}

View File

@ -1,4 +1,4 @@
// Copyright 2017-2018 Alexander Luzgarev
// Copyright 2017-2019 Alexander Luzgarev
using System;
using System.Globalization;
@ -13,13 +13,19 @@ namespace MatFileHandler
/// </summary>
internal class Header
{
private Header(string text, long subsystemDataOffset, int version)
private Header(byte[] rawBytes, string text, long subsystemDataOffset, int version)
{
RawBytes = rawBytes;
Text = text;
SubsystemDataOffset = subsystemDataOffset;
Version = version;
}
/// <summary>
/// Gets raw byte contents of the header.
/// </summary>
public byte[] RawBytes { get; }
/// <summary>
/// Gets the header text.
/// </summary>
@ -55,7 +61,7 @@ namespace MatFileHandler
platform = platform.Remove(length);
}
var text = $"MATLAB 5.0 MAT-file, Platform: {platform}, Created on: {dateTime}{padding}";
return new Header(text, 0, 256);
return new Header(null, text, 0, 256);
}
/// <summary>
@ -65,18 +71,26 @@ namespace MatFileHandler
/// <returns>The header read.</returns>
public static Header Read(BinaryReader reader)
{
var textBytes = reader.ReadBytes(116);
var rawBytes = reader.ReadBytes(128);
using (var stream = new MemoryStream(rawBytes))
{
using (var newReader = new BinaryReader(stream))
{
var textBytes = newReader.ReadBytes(116);
var text = System.Text.Encoding.UTF8.GetString(textBytes);
var subsystemDataOffsetBytes = reader.ReadBytes(8);
var subsystemDataOffsetBytes = newReader.ReadBytes(8);
var subsystemDataOffset = BitConverter.ToInt64(subsystemDataOffsetBytes, 0);
var version = reader.ReadInt16();
var endian = reader.ReadInt16();
var version = newReader.ReadInt16();
var endian = newReader.ReadInt16();
var isLittleEndian = endian == 19785;
if (!isLittleEndian)
{
throw new NotSupportedException("Big-endian files are not supported.");
}
return new Header(text, subsystemDataOffset, version);
return new Header(rawBytes, text, subsystemDataOffset, version);
}
}
}
private static string GetOperatingSystem()

View File

@ -1,4 +1,4 @@
// Copyright 2017-2018 Alexander Luzgarev
// Copyright 2017-2019 Alexander Luzgarev
using System.Numerics;

View File

@ -1,4 +1,4 @@
// Copyright 2017-2018 Alexander Luzgarev
// Copyright 2017-2019 Alexander Luzgarev
namespace MatFileHandler
{

View File

@ -1,4 +1,4 @@
// Copyright 2017-2018 Alexander Luzgarev
// Copyright 2017-2019 Alexander Luzgarev
namespace MatFileHandler
{

View File

@ -1,4 +1,4 @@
// Copyright 2017-2018 Alexander Luzgarev
// Copyright 2017-2019 Alexander Luzgarev
namespace MatFileHandler
{

View File

@ -1,4 +1,4 @@
// Copyright 2017-2018 Alexander Luzgarev
// Copyright 2017-2019 Alexander Luzgarev
namespace MatFileHandler
{

View File

@ -1,4 +1,4 @@
// Copyright 2017-2018 Alexander Luzgarev
// Copyright 2017-2019 Alexander Luzgarev
using System.Collections.Generic;

View File

@ -1,4 +1,4 @@
// Copyright 2017-2018 Alexander Luzgarev
// Copyright 2017-2019 Alexander Luzgarev
using System.Collections.Generic;

View File

@ -1,4 +1,4 @@
// Copyright 2017-2018 Alexander Luzgarev
// Copyright 2017-2019 Alexander Luzgarev
using System.Collections.Generic;

View File

@ -1,4 +1,4 @@
// Copyright 2017-2018 Alexander Luzgarev
// Copyright 2017-2019 Alexander Luzgarev
namespace MatFileHandler
{

View File

@ -1,4 +1,4 @@
// Copyright 2017-2018 Alexander Luzgarev
// Copyright 2017-2019 Alexander Luzgarev
using System.Numerics;
@ -12,45 +12,28 @@ namespace MatFileHandler
/// <summary>
/// Initializes a new instance of the <see cref="MatArray"/> class.
/// </summary>
/// <param name="flags">Array properties.</param>
/// <param name="dimensions">Dimensions of the array.</param>
/// <param name="name">Array name.</param>
protected MatArray(
ArrayFlags flags,
int[] dimensions,
string name)
protected MatArray(int[] dimensions)
{
Flags = flags;
Dimensions = dimensions;
Name = name;
}
/// <inheritdoc />
public int[] Dimensions { get; }
/// <summary>
/// Gets the array name.
/// </summary>
public string Name { get; }
/// <inheritdoc />
public int Count => Dimensions.NumberOfElements();
/// <inheritdoc />
public bool IsEmpty => Dimensions.Length == 0;
/// <summary>
/// Gets properties of the array.
/// </summary>
internal ArrayFlags Flags { get; }
/// <summary>
/// Returns a new empty array.
/// </summary>
/// <returns>Empty array.</returns>
public static MatArray Empty()
{
return new MatArray(new ArrayFlags { Class = ArrayType.MxCell, Variable = 0 }, new int[] { }, string.Empty);
return new MatArray(new int[] { });
}
/// <inheritdoc />

View File

@ -1,4 +1,4 @@
// Copyright 2017-2018 Alexander Luzgarev
// Copyright 2017-2019 Alexander Luzgarev
using System.Collections.Generic;
using System.Linq;
@ -13,12 +13,10 @@ namespace MatFileHandler
/// <summary>
/// Initializes a new instance of the <see cref="MatCellArray"/> class.
/// </summary>
/// <param name="flags">Array properties.</param>
/// <param name="dimensions">Dimensions of the array.</param>
/// <param name="name">Array name.</param>
/// <param name="elements">Array elements.</param>
public MatCellArray(ArrayFlags flags, int[] dimensions, string name, IEnumerable<IArray> elements)
: base(flags, dimensions, name)
public MatCellArray(int[] dimensions, IEnumerable<IArray> elements)
: base(dimensions)
{
Data = elements.ToArray();
}

View File

@ -1,4 +1,4 @@
// Copyright 2017-2018 Alexander Luzgarev
// Copyright 2017-2019 Alexander Luzgarev
namespace MatFileHandler
{
@ -15,13 +15,11 @@ namespace MatFileHandler
/// <summary>
/// Initializes a new instance of the <see cref="MatCharArrayOf{T}"/> class.
/// </summary>
/// <param name="flags">Array parameters.</param>
/// <param name="dimensions">Dimensions of the array.</param>
/// <param name="name">Array name.</param>
/// <param name="rawData">Raw data (UTF-8 or UTF-16).</param>
/// <param name="stringData">Contents as a string.</param>
internal MatCharArrayOf(ArrayFlags flags, int[] dimensions, string name, T[] rawData, string stringData)
: base(flags, dimensions, name, rawData)
internal MatCharArrayOf(int[] dimensions, T[] rawData, string stringData)
: base(dimensions, rawData)
{
StringData = stringData;
}

View File

@ -1,4 +1,4 @@
// Copyright 2017-2018 Alexander Luzgarev
// Copyright 2017-2019 Alexander Luzgarev
using System.Collections.Generic;
using System.Linq;

View File

@ -15,6 +15,7 @@
<OutputPath>bin\$(Configuration)\</OutputPath>
<DocumentationFile>$(OutputPath)\$(TargetFramework)\$(AssemblyName).xml</DocumentationFile>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<CodeAnalysisRuleSet>..\MatFileHandler.ruleset</CodeAnalysisRuleSet>
@ -26,6 +27,7 @@
<AdditionalFiles Include="..\stylecop.json" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="HDF.PInvoke.NETStandard" Version="1.10.200" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.0-beta004">
<PrivateAssets>All</PrivateAssets>
</PackageReference>

View File

@ -0,0 +1,50 @@
// Copyright 2017-2019 Alexander Luzgarev
using System;
using System.IO;
using System.Runtime.InteropServices;
using HDF.PInvoke;
namespace MatFileHandler
{
/// <summary>
/// Reader for MATLAB HDF (-v7.3) files.
/// </summary>
internal static class MatFileHdfReader
{
/// <summary>
/// Continue reading MATLAB HDF file after reading the MATLAB header.
/// </summary>
/// <param name="header">MATLAB header that was read.</param>
/// <param name="stream">Stream to read the rest of the file from.</param>
/// <returns>MATLAB data file contents.</returns>
internal static IMatFile ContinueReadingHdfFile(Header header, Stream stream)
{
using (var memoryStream = new MemoryStream())
{
using (var headerStream = new MemoryStream(header.RawBytes))
{
headerStream.CopyTo(memoryStream);
}
stream.CopyTo(memoryStream);
var bytes = memoryStream.ToArray();
return ReadFromByteArray(bytes);
}
}
private static IMatFile ReadFromByteArray(byte[] bytes)
{
var fileAccessPropertyList = H5P.create(H5P.FILE_ACCESS);
H5P.set_fapl_core(fileAccessPropertyList, IntPtr.Add(IntPtr.Zero, 1024), 0);
var ptr = Marshal.AllocCoTaskMem(bytes.Length);
Marshal.Copy(bytes, 0, ptr, bytes.Length);
H5P.set_file_image(fileAccessPropertyList, ptr, IntPtr.Add(IntPtr.Zero, bytes.Length));
var fileId = H5F.open(Guid.NewGuid().ToString(), H5F.ACC_RDONLY, fileAccessPropertyList);
var hdfFileReader = new HdfFileReader(fileId);
var result = hdfFileReader.Read();
H5F.close(fileId);
H5F.clear_elink_file_cache(fileId);
return result;
}
}
}

View File

@ -0,0 +1,97 @@
// Copyright 2017-2019 Alexander Luzgarev
using System.Collections.Generic;
using System.IO;
namespace MatFileHandler
{
/// <summary>
/// Reader for MATLAB "Level 5" .mat files.
/// </summary>
internal static class MatFileLevel5Reader
{
/// <summary>
/// Read a sequence of raw variables from .mat file.
/// </summary>
/// <param name="reader">Reader.</param>
/// <param name="subsystemDataOffset">Offset of subsystem data in the file;
/// we need it because we may encounter it during reading, and
/// the subsystem data should be parsed in a special way.</param>
/// <param name="subsystemData">
/// Link to the current file's subsystem data structure; initially it has dummy value
/// which will be replaced after we parse the whole subsystem data.</param>
/// <returns>List of "raw" variables; the actual variables are constructed from them later.</returns>
internal static List<RawVariable> ReadRawVariables(BinaryReader reader, long subsystemDataOffset, SubsystemData subsystemData)
{
var variables = new List<RawVariable>();
var dataElementReader = new DataElementReader(subsystemData);
while (true)
{
try
{
var position = reader.BaseStream.Position;
var dataElement = dataElementReader.Read(reader);
if (position == subsystemDataOffset)
{
var subsystemDataElement = dataElement.Element as IArrayOf<byte>;
var newSubsystemData = ReadSubsystemData(subsystemDataElement.Data, subsystemData);
subsystemData.Set(newSubsystemData);
}
else
{
variables.Add(new RawVariable(position, dataElement.Element, dataElement.Flags, dataElement.Name));
}
}
catch (EndOfStreamException)
{
break;
}
}
return variables;
}
/// <summary>
/// Read raw variables from a .mat file.
/// </summary>
/// <param name="reader">Binary reader.</param>
/// <param name="subsystemDataOffset">Offset to the subsystem data to use (read from the file header).</param>
/// <returns>Raw variables read.</returns>
internal static List<RawVariable> ReadRawVariables(BinaryReader reader, long subsystemDataOffset)
{
var subsystemData = new SubsystemData();
return ReadRawVariables(reader, subsystemDataOffset, subsystemData);
}
/// <summary>
/// Continue reading old-style (Level 5) MATLAB file.
/// </summary>
/// <param name="header">Header that was already read.</param>
/// <param name="reader">Reader for reading the rest of the file.</param>
/// <returns>MATLAB data file contents.</returns>
internal static IMatFile ContinueReadingLevel5File(Header header, BinaryReader reader)
{
var rawVariables = ReadRawVariables(reader, header.SubsystemDataOffset);
var variables = new List<IVariable>();
foreach (var rawVariable in rawVariables)
{
if (!(rawVariable.DataElement is MatArray array))
{
continue;
}
variables.Add(new MatVariable(
array,
rawVariable.Name,
rawVariable.Flags.Variable.HasFlag(Variable.IsGlobal)));
}
return new MatFile(variables);
}
private static SubsystemData ReadSubsystemData(byte[] bytes, SubsystemData subsystemData)
{
return SubsystemDataReader.Read(bytes, subsystemData);
}
}
}

View File

@ -1,7 +1,6 @@
// Copyright 2017-2018 Alexander Luzgarev
// Copyright 2017-2019 Alexander Luzgarev
using System;
using System.Collections.Generic;
using System.IO;
namespace MatFileHandler
@ -34,89 +33,23 @@ namespace MatFileHandler
}
}
/// <summary>
/// Read a sequence of raw variables from .mat file.
/// </summary>
/// <param name="reader">Reader.</param>
/// <param name="subsystemDataOffset">Offset of subsystem data in the file;
/// we need it because we may encounter it during reading, and
/// the subsystem data should be parsed in a special way.</param>
/// <param name="subsystemData">
/// Link to the current file's subsystem data structure; initially it has dummy value
/// which will be replaced after we parse the whole subsystem data.</param>
/// <returns>List of "raw" variables; the actual variables are constructed from them later.</returns>
internal static List<RawVariable> ReadRawVariables(BinaryReader reader, long subsystemDataOffset, SubsystemData subsystemData)
{
var variables = new List<RawVariable>();
var dataElementReader = new DataElementReader(subsystemData);
while (true)
{
try
{
var position = reader.BaseStream.Position;
var dataElement = dataElementReader.Read(reader);
if (position == subsystemDataOffset)
{
var subsystemDataElement = dataElement as IArrayOf<byte>;
var newSubsystemData = ReadSubsystemData(subsystemDataElement.Data, subsystemData);
subsystemData.Set(newSubsystemData);
}
else
{
variables.Add(new RawVariable(position, dataElement));
}
}
catch (EndOfStreamException)
{
break;
}
}
return variables;
}
/// <summary>
/// Read raw variables from a .mat file.
/// </summary>
/// <param name="reader">Binary reader.</param>
/// <param name="subsystemDataOffset">Offset to the subsystem data to use (read from the file header).</param>
/// <returns>Raw variables read.</returns>
internal static List<RawVariable> ReadRawVariables(BinaryReader reader, long subsystemDataOffset)
{
var subsystemData = new SubsystemData();
return ReadRawVariables(reader, subsystemDataOffset, subsystemData);
}
private static IMatFile Read(BinaryReader reader)
{
var header = ReadHeader(reader);
var rawVariables = ReadRawVariables(reader, header.SubsystemDataOffset);
var variables = new List<IVariable>();
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, SubsystemData subsystemData)
private IMatFile Read(BinaryReader reader)
{
return SubsystemDataReader.Read(bytes, subsystemData);
var header = ReadHeader(reader);
switch (header.Version)
{
case 256:
return MatFileLevel5Reader.ContinueReadingLevel5File(header, reader);
case 512:
return MatFileHdfReader.ContinueReadingHdfFile(header, reader.BaseStream);
default:
throw new NotSupportedException($"Unknown file format.");
}
}
}
}

View File

@ -1,4 +1,4 @@
// Copyright 2017-2018 Alexander Luzgarev
// Copyright 2017-2019 Alexander Luzgarev
using System;
using System.Collections.Generic;
@ -226,14 +226,14 @@ namespace MatFileHandler
writer.Write(new byte[] { 0, 0, 0, 0, 0, 0 });
}
private void WriteSparseArrayFlags(BinaryWriter writer, SparseArrayFlags flags)
private void WriteSparseArrayFlags(BinaryWriter writer, ArrayFlags flags, uint nzMax)
{
var flag = (byte)flags.ArrayFlags.Variable;
var flag = (byte)flags.Variable;
WriteTag(writer, new Tag(DataType.MiUInt32, 8));
writer.Write((byte)flags.ArrayFlags.Class);
writer.Write((byte)flags.Class);
writer.Write(flag);
writer.Write(new byte[] { 0, 0 });
writer.Write(flags.NzMax);
writer.Write(nzMax);
}
private void WriteName(BinaryWriter writer, string name)
@ -373,19 +373,16 @@ namespace MatFileHandler
}
}
private SparseArrayFlags GetSparseArrayFlags<T>(ISparseArrayOf<T> array, bool isGlobal, uint nonZero)
private (ArrayFlags flags, uint nzMax) GetSparseArrayFlags<T>(ISparseArrayOf<T> array, bool isGlobal, uint nonZero)
where T : struct
{
var flags = GetArrayFlags(array, isGlobal);
return new SparseArrayFlags
{
ArrayFlags = new ArrayFlags
return (new ArrayFlags
{
Class = ArrayType.MxSparse,
Variable = flags.Variable,
},
NzMax = nonZero,
};
nonZero);
}
private ArrayFlags GetCharArrayFlags(bool isGlobal)
@ -513,7 +510,8 @@ namespace MatFileHandler
where T : struct, IEquatable<T>
{
(var rows, var columns, var data, var nonZero) = PrepareSparseArrayData(array);
WriteSparseArrayFlags(writer, GetSparseArrayFlags(array, isGlobal, nonZero));
var (flags, nzMax) = GetSparseArrayFlags(array, isGlobal, nonZero);
WriteSparseArrayFlags(writer, flags, nzMax);
WriteDimensions(writer, array.Dimensions);
WriteName(writer, name);
WriteSparseArrayValues(writer, rows, columns, data);

View File

@ -1,4 +1,4 @@
// Copyright 2017-2018 Alexander Luzgarev
// Copyright 2017-2019 Alexander Luzgarev
namespace MatFileHandler
{

View File

@ -1,4 +1,4 @@
// Copyright 2017-2018 Alexander Luzgarev
// Copyright 2017-2019 Alexander Luzgarev
using System;
using System.Collections.Generic;
@ -17,12 +17,10 @@ namespace MatFileHandler
/// <summary>
/// Initializes a new instance of the <see cref="MatNumericalArrayOf{T}"/> class.
/// </summary>
/// <param name="flags">Array parameters.</param>
/// <param name="dimensions">Dimensions of the array.</param>
/// <param name="name">Array name.</param>
/// <param name="data">Array contents.</param>
public MatNumericalArrayOf(ArrayFlags flags, int[] dimensions, string name, T[] data)
: base(flags, dimensions, name)
public MatNumericalArrayOf(int[] dimensions, T[] data)
: base(dimensions)
{
Data = data;
}

View File

@ -1,4 +1,4 @@
// Copyright 2017-2018 Alexander Luzgarev
// Copyright 2017-2019 Alexander Luzgarev
using System;
using System.Collections.Generic;
@ -18,16 +18,10 @@ namespace MatFileHandler
/// <summary>
/// Initializes a new instance of the <see cref="MatSparseArrayOf{T}"/> class.
/// </summary>
/// <param name="flags">Array properties.</param>
/// <param name="dimensions">Dimensions of the array.</param>
/// <param name="name">Array name.</param>
/// <param name="data">Array contents.</param>
public MatSparseArrayOf(
SparseArrayFlags flags,
int[] dimensions,
string name,
Dictionary<(int, int), T> data)
: base(flags.ArrayFlags, dimensions, name)
public MatSparseArrayOf(int[] dimensions, Dictionary<(int, int), T> data)
: base(dimensions)
{
DataDictionary = data;
}
@ -49,7 +43,7 @@ namespace MatFileHandler
get
{
var rowAndColumn = GetRowAndColumn(list);
return DataDictionary.ContainsKey(rowAndColumn) ? DataDictionary[rowAndColumn] : default(T);
return DataDictionary.ContainsKey(rowAndColumn) ? DataDictionary[rowAndColumn] : default;
}
set => DataDictionary[GetRowAndColumn(list)] = value;
}

View File

@ -1,4 +1,4 @@
// Copyright 2017-2018 Alexander Luzgarev
// Copyright 2017-2019 Alexander Luzgarev
using System;
using System.Collections;
@ -15,16 +15,10 @@ namespace MatFileHandler
/// <summary>
/// Initializes a new instance of the <see cref="MatStructureArray"/> class.
/// </summary>
/// <param name="flags">Array properties.</param>
/// <param name="dimensions">Dimensions of the array.</param>
/// <param name="name">Array name.</param>
/// <param name="fields">Array contents.</param>
public MatStructureArray(
ArrayFlags flags,
int[] dimensions,
string name,
Dictionary<string, List<IArray>> fields)
: base(flags, dimensions, name)
public MatStructureArray(int[] dimensions, Dictionary<string, List<IArray>> fields)
: base(dimensions)
{
Fields = fields;
}

View File

@ -1,4 +1,4 @@
// Copyright 2017-2018 Alexander Luzgarev
// Copyright 2017-2019 Alexander Luzgarev
namespace MatFileHandler
{

View File

@ -1,4 +1,4 @@
// Copyright 2017-2018 Alexander Luzgarev
// Copyright 2017-2019 Alexander Luzgarev
namespace MatFileHandler
{

View File

@ -1,4 +1,4 @@
// Copyright 2017-2018 Alexander Luzgarev
// Copyright 2017-2019 Alexander Luzgarev
using System;
using System.Numerics;
@ -21,13 +21,12 @@ namespace MatFileHandler
/// <summary>
/// Initializes a new instance of the <see cref="Opaque"/> class.
/// </summary>
/// <param name="name">Name of the object.</param>
/// <param name="typeDescription">Type description.</param>
/// <param name="className">Class name.</param>
/// <param name="dimensions">Dimensions of the object.</param>
/// <param name="rawData">Raw object's data.</param>
public Opaque(string name, string typeDescription, string className, int[] dimensions, DataElement rawData)
: base(new ArrayFlags(ArrayType.MxOpaque, 0), dimensions, name)
public Opaque(string typeDescription, string className, int[] dimensions, DataElement rawData)
: base(dimensions)
{
TypeDescription = typeDescription ?? throw new ArgumentNullException(nameof(typeDescription));
ClassName = className ?? throw new ArgumentNullException(nameof(className));

View File

@ -1,4 +1,4 @@
// Copyright 2017-2018 Alexander Luzgarev
// Copyright 2017-2019 Alexander Luzgarev
using System;
using System.Collections;
@ -17,7 +17,6 @@ namespace MatFileHandler
/// <summary>
/// Initializes a new instance of the <see cref="OpaqueLink"/> class.
/// </summary>
/// <param name="name">Name of the object.</param>
/// <param name="typeDescription">Description of object's class.</param>
/// <param name="className">Name of the object's class.</param>
/// <param name="dimensions">Dimensions of the object.</param>
@ -26,7 +25,6 @@ namespace MatFileHandler
/// <param name="classIndex">Index of object's class.</param>
/// <param name="subsystemData">Reference to global subsystem data.</param>
public OpaqueLink(
string name,
string typeDescription,
string className,
int[] dimensions,
@ -34,7 +32,7 @@ namespace MatFileHandler
int[] indexToObjectId,
int classIndex,
SubsystemData subsystemData)
: base(name, typeDescription, className, dimensions, data)
: base(typeDescription, className, dimensions, data)
{
IndexToObjectId = indexToObjectId ?? throw new ArgumentNullException(nameof(indexToObjectId));
ClassIndex = classIndex;

View File

@ -1,4 +1,4 @@
// Copyright 2017-2018 Alexander Luzgarev
// Copyright 2017-2019 Alexander Luzgarev
using System;
@ -17,10 +17,14 @@ namespace MatFileHandler
/// </summary>
/// <param name="offset">Offset of the variable in the source file.</param>
/// <param name="dataElement">Data element parsed from the file.</param>
internal RawVariable(long offset, DataElement dataElement)
/// <param name="flags">Array flags.</param>
/// <param name="name">Variable name.</param>
internal RawVariable(long offset, DataElement dataElement, ArrayFlags flags, string name)
{
Offset = offset;
DataElement = dataElement ?? throw new ArgumentNullException(nameof(dataElement));
Flags = flags;
Name = name;
}
/// <summary>
@ -28,6 +32,16 @@ namespace MatFileHandler
/// </summary>
public DataElement DataElement { get; }
/// <summary>
/// Gets array flags.
/// </summary>
public ArrayFlags Flags { get; }
/// <summary>
/// Gets variable name.
/// </summary>
public string Name { get; }
/// <summary>
/// Gets offset of the variable in the .mat file.
/// </summary>

Some files were not shown because too many files have changed in this diff Show More