diff --git a/MatFileHandler.Tests/MatFileReaderHdfTests.cs b/MatFileHandler.Tests/MatFileReaderHdfTests.cs
index cdf2085..a5e7995 100644
--- a/MatFileHandler.Tests/MatFileReaderHdfTests.cs
+++ b/MatFileHandler.Tests/MatFileReaderHdfTests.cs
@@ -50,6 +50,66 @@ namespace MatFileHandler.Tests
Assert.That(arrayUnicodeWide.String, Is.EqualTo("🍆"));
}
+ ///
+ /// Test reading a two-dimensional double array.
+ ///
+ [Test]
+ public void TestMatrix()
+ {
+ var matFile = ReadHdfTestFile("matrix");
+ var matrix = matFile["matrix"].Value as IArrayOf;
+ 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));
+ }
+
+ ///
+ /// Test reading lower and upper limits of integer data types.
+ ///
+ [Test]
+ public void TestLimits()
+ {
+ var matFile = ReadHdfTestFile("limits");
+ IArray array;
+ array = matFile["int8_"].Value;
+ CheckLimits(array as IArrayOf, CommonData.Int8Limits);
+ Assert.That(array.ConvertToDoubleArray(), Is.EqualTo(new[] { -128.0, 127.0 }));
+
+ array = matFile["uint8_"].Value;
+ CheckLimits(array as IArrayOf, CommonData.UInt8Limits);
+
+ array = matFile["int16_"].Value;
+ CheckLimits(array as IArrayOf, CommonData.Int16Limits);
+
+ array = matFile["uint16_"].Value;
+ CheckLimits(array as IArrayOf, CommonData.UInt16Limits);
+
+ array = matFile["int32_"].Value;
+ CheckLimits(array as IArrayOf, CommonData.Int32Limits);
+
+ array = matFile["uint32_"].Value;
+ CheckLimits(array as IArrayOf, CommonData.UInt32Limits);
+
+ array = matFile["int64_"].Value;
+ CheckLimits(array as IArrayOf, CommonData.Int64Limits);
+
+ array = matFile["uint64_"].Value;
+ CheckLimits(array as IArrayOf, CommonData.UInt64Limits);
+ }
+
+ private static void CheckLimits(IArrayOf 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 AbstractTestDataFactory GetTests(string factoryName) =>
new MatTestDataFactory(Path.Combine(TestDirectory, factoryName));
diff --git a/MatFileHandler.Tests/test-data/hdf/limits.mat b/MatFileHandler.Tests/test-data/hdf/limits.mat
new file mode 100644
index 0000000..fa519c0
Binary files /dev/null and b/MatFileHandler.Tests/test-data/hdf/limits.mat differ
diff --git a/MatFileHandler.Tests/test-data/hdf/matrix.mat b/MatFileHandler.Tests/test-data/hdf/matrix.mat
new file mode 100644
index 0000000..555a5ca
Binary files /dev/null and b/MatFileHandler.Tests/test-data/hdf/matrix.mat differ
diff --git a/MatFileHandler/HdfFileReader.cs b/MatFileHandler/HdfFileReader.cs
index 512535b..fa14d82 100644
--- a/MatFileHandler/HdfFileReader.cs
+++ b/MatFileHandler/HdfFileReader.cs
@@ -8,26 +8,151 @@ using HDF.PInvoke;
namespace MatFileHandler
{
- public class HdfCharArray : ICharArray
+ internal class HdfArray : IArray
{
- public HdfCharArray(int[] dimensions, string data)
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Dimensions of the array.
+ protected HdfArray(
+ int[] dimensions)
{
Dimensions = dimensions;
+ }
+
+ ///
+ public int[] Dimensions { get; }
+
+ ///
+ public int Count => Dimensions.NumberOfElements();
+
+ ///
+ /// Returns a new empty array.
+ ///
+ /// Empty array.
+ public static HdfArray Empty()
+ {
+ return new HdfArray(Array.Empty());
+ }
+
+ public virtual double[] ConvertToDoubleArray()
+ {
+ return null;
+ }
+
+ public virtual Complex[] ConvertToComplexArray()
+ {
+ return null;
+ }
+
+ ///
+ public bool IsEmpty => Dimensions.Length == 0;
+ }
+
+ ///
+ /// A numerical array.
+ ///
+ /// Element type.
+ internal class HdfNumericalArrayOf : HdfArray, IArrayOf
+ where T : struct
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Dimensions of the array.
+ /// Array name.
+ /// Array contents.
+ public HdfNumericalArrayOf(int[] dimensions, T[] data)
+ : base(dimensions)
+ {
+ Data = data;
+ }
+
+ ///
+ public T[] Data { get; }
+
+ ///
+ public T this[params int[] list]
+ {
+ get => Data[Dimensions.DimFlatten(list)];
+ set => Data[Dimensions.DimFlatten(list)] = value;
+ }
+
+ ///
+ /// Tries to convert the array to an array of Double values.
+ ///
+ /// Array of values of the array, converted to Double, or null if the conversion is not possible.
+ public override double[] ConvertToDoubleArray()
+ {
+ return Data as double[] ?? Data.Select(x => Convert.ToDouble(x)).ToArray();
+ }
+
+ ///
+ /// Tries to convert the array to an array of Complex values.
+ ///
+ /// Array of values of the array, converted to Complex, or null if the conversion is not possible.
+ public override Complex[] ConvertToComplexArray()
+ {
+ if (Data is Complex[])
+ {
+ return Data as Complex[];
+ }
+ if (Data is ComplexOf[])
+ {
+ return ConvertToComplex(Data as ComplexOf[]);
+ }
+ if (Data is ComplexOf[])
+ {
+ return ConvertToComplex(Data as ComplexOf[]);
+ }
+ if (Data is ComplexOf[])
+ {
+ return ConvertToComplex(Data as ComplexOf[]);
+ }
+ if (Data is ComplexOf[])
+ {
+ return ConvertToComplex(Data as ComplexOf[]);
+ }
+ if (Data is ComplexOf[])
+ {
+ return ConvertToComplex(Data as ComplexOf[]);
+ }
+ if (Data is ComplexOf[])
+ {
+ return ConvertToComplex(Data as ComplexOf[]);
+ }
+ if (Data is ComplexOf[])
+ {
+ return ConvertToComplex(Data as ComplexOf[]);
+ }
+ if (Data is ComplexOf[])
+ {
+ return ConvertToComplex(Data as ComplexOf[]);
+ }
+ return ConvertToDoubleArray().Select(x => new Complex(x, 0.0)).ToArray();
+ }
+
+ private static Complex[] ConvertToComplex(IEnumerable> array)
+ where TS : struct
+ {
+ return array.Select(x => new Complex(Convert.ToDouble(x.Real), Convert.ToDouble(x.Imaginary))).ToArray();
+ }
+ }
+
+ internal class HdfCharArray : HdfArray, ICharArray
+ {
+ public HdfCharArray(int[] dimensions, string data)
+ : base(dimensions)
+ {
StringData = data;
}
- public bool IsEmpty => Dimensions.Length == 0;
-
- public int[] Dimensions { get; }
-
- public int Count => Dimensions.NumberOfElements();
-
- public double[] ConvertToDoubleArray()
+ public override double[] ConvertToDoubleArray()
{
return Data.Select(Convert.ToDouble).ToArray();
}
- public Complex[] ConvertToComplexArray()
+ public override Complex[] ConvertToComplexArray()
{
return ConvertToDoubleArray().Select(x => new Complex(x, 0.0)).ToArray();
}
@@ -129,26 +254,158 @@ namespace MatFileHandler
return dims.Select(x => (int)x).ToArray();
}
+ private static ArrayType ArrayTypeFromMatlabClassName(string matlabClassName)
+ {
+ switch (matlabClassName)
+ {
+ case "char":
+ return ArrayType.MxChar;
+ case "int8":
+ return ArrayType.MxInt8;
+ case "uint8":
+ return ArrayType.MxUInt8;
+ case "int16":
+ return ArrayType.MxInt16;
+ case "uint16":
+ return ArrayType.MxUInt16;
+ case "int32":
+ return ArrayType.MxInt32;
+ case "uint32":
+ return ArrayType.MxUInt32;
+ case "int64":
+ return ArrayType.MxInt64;
+ case "uint64":
+ return ArrayType.MxUInt64;
+ case "double":
+ return ArrayType.MxDouble;
+ }
+ throw new NotImplementedException();
+ }
+
private static IArray ReadDataset(long datasetId)
{
var dims = GetDimensionsOfDataset(datasetId);
var matlabClass = GetMatlabClassOfDataset(datasetId);
+ var arrayType = ArrayTypeFromMatlabClassName(matlabClass);
- if (matlabClass == "char")
+ switch (arrayType)
{
- return ReadCharArray(datasetId, dims);
+ case ArrayType.MxChar:
+ return ReadCharArray(datasetId, dims);
+ case ArrayType.MxInt8:
+ return ReadNumericalArray(datasetId, dims, arrayType);
+ case ArrayType.MxUInt8:
+ return ReadNumericalArray(datasetId, dims, arrayType);
+ case ArrayType.MxInt16:
+ return ReadNumericalArray(datasetId, dims, arrayType);
+ case ArrayType.MxUInt16:
+ return ReadNumericalArray(datasetId, dims, arrayType);
+ case ArrayType.MxInt32:
+ return ReadNumericalArray(datasetId, dims, arrayType);
+ case ArrayType.MxUInt32:
+ return ReadNumericalArray(datasetId, dims, arrayType);
+ case ArrayType.MxInt64:
+ return ReadNumericalArray(datasetId, dims, arrayType);
+ case ArrayType.MxUInt64:
+ return ReadNumericalArray(datasetId, dims, arrayType);
+ case ArrayType.MxSingle:
+ return ReadNumericalArray(datasetId, dims, arrayType);
+ case ArrayType.MxDouble:
+ return ReadNumericalArray(datasetId, dims, arrayType);
+ }
+ throw new NotImplementedException($"Unknown array type: {arrayType}.");
+ }
+
+ private static int SizeOfArrayElement(ArrayType arrayType)
+ {
+ switch (arrayType)
+ {
+ case ArrayType.MxInt8:
+ case ArrayType.MxUInt8:
+ return 1;
+ case ArrayType.MxInt16:
+ case ArrayType.MxUInt16:
+ return 2;
+ case ArrayType.MxInt32:
+ case ArrayType.MxUInt32:
+ case ArrayType.MxSingle:
+ return 4;
+ case ArrayType.MxInt64:
+ case ArrayType.MxUInt64:
+ case ArrayType.MxDouble:
+ return 8;
+ }
+
+ throw new NotImplementedException();
+ }
+
+ private static long H5tTypeFromArrayType(ArrayType arrayType)
+ {
+ switch (arrayType)
+ {
+ case ArrayType.MxInt8:
+ return H5T.NATIVE_INT8;
+ case ArrayType.MxUInt8:
+ return H5T.NATIVE_UINT8;
+ case ArrayType.MxInt16:
+ return H5T.NATIVE_INT16;
+ case ArrayType.MxUInt16:
+ return H5T.NATIVE_UINT16;
+ case ArrayType.MxInt32:
+ return H5T.NATIVE_INT32;
+ case ArrayType.MxUInt32:
+ return H5T.NATIVE_UINT32;
+ case ArrayType.MxInt64:
+ return H5T.NATIVE_INT64;
+ case ArrayType.MxUInt64:
+ return H5T.NATIVE_UINT64;
+ case ArrayType.MxSingle:
+ return H5T.NATIVE_FLOAT;
+ case ArrayType.MxDouble:
+ return H5T.NATIVE_DOUBLE;
}
throw new NotImplementedException();
}
+ private static T[] ConvertDataToProperType(byte[] bytes, ArrayType 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 byte[] ReadDataset(long datasetId, long elementType, int dataSize)
+ {
+ var dataBuffer = Marshal.AllocHGlobal(dataSize);
+ H5D.read(datasetId, elementType, H5S.ALL, H5S.ALL, H5P.DEFAULT, dataBuffer);
+ var data = new byte[dataSize];
+ Marshal.Copy(dataBuffer, data, 0, dataSize);
+ return data;
+ }
+
+ private static IArray ReadNumericalArray(long datasetId, int[] dims, ArrayType arrayType)
+ where T : struct
+ {
+ var numberOfElements = dims.NumberOfElements();
+ var dataSize = numberOfElements * SizeOfArrayElement(arrayType);
+ var storageSize = (int)H5D.get_storage_size(datasetId);
+ if (dataSize != storageSize)
+ {
+ throw new Exception("Data size mismatch.");
+ }
+ var data = ReadDataset(datasetId, H5tTypeFromArrayType(arrayType), dataSize);
+ var convertedData = ConvertDataToProperType(data, arrayType);
+ return new HdfNumericalArrayOf(dims, convertedData);
+ }
+
private static IArray ReadCharArray(long datasetId, int[] dims)
{
var storageSize = (int)H5D.get_storage_size(datasetId);
- var data = new byte[storageSize];
- var dataBuffer = Marshal.AllocHGlobal(storageSize);
- H5D.read(datasetId, H5T.NATIVE_UINT16, H5S.ALL, H5S.ALL, H5P.DEFAULT, dataBuffer);
- Marshal.Copy(dataBuffer, data, 0, storageSize);
+ var data = ReadDataset(datasetId, H5T.NATIVE_UINT16, storageSize);
var str = Encoding.Unicode.GetString(data);
return new HdfCharArray(dims, str);
}
diff --git a/MatFileHandler/MatFileHdfReader.cs b/MatFileHandler/MatFileHdfReader.cs
index 285b75b..d2d6036 100644
--- a/MatFileHandler/MatFileHdfReader.cs
+++ b/MatFileHandler/MatFileHdfReader.cs
@@ -1,9 +1,7 @@
-using System;
-using System.Collections.Generic;
+using HDF.PInvoke;
+using System;
using System.IO;
using System.Runtime.InteropServices;
-using System.Text;
-using HDF.PInvoke;
namespace MatFileHandler
{