Support numerical arrays

This commit is contained in:
Alexander Luzgarev 2019-03-09 18:53:44 +01:00
parent 93be86d526
commit b66e380414
5 changed files with 335 additions and 20 deletions

View File

@ -50,6 +50,66 @@ namespace MatFileHandler.Tests
Assert.That(arrayUnicodeWide.String, Is.EqualTo("🍆"));
}
/// <summary>
/// Test reading a two-dimensional double array.
/// </summary>
[Test]
public void TestMatrix()
{
var matFile = ReadHdfTestFile("matrix");
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 reading lower and upper limits of integer data types.
/// </summary>
[Test]
public void TestLimits()
{
var matFile = ReadHdfTestFile("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);
}
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 AbstractTestDataFactory<IMatFile> GetTests(string factoryName) =>
new MatTestDataFactory(Path.Combine(TestDirectory, factoryName));

Binary file not shown.

Binary file not shown.

View File

@ -8,26 +8,151 @@ using HDF.PInvoke;
namespace MatFileHandler
{
public class HdfCharArray : ICharArray
internal class HdfArray : IArray
{
public HdfCharArray(int[] dimensions, string data)
/// <summary>
/// Initializes a new instance of the <see cref="HdfArray"/> class.
/// </summary>
/// <param name="dimensions">Dimensions of the array.</param>
protected HdfArray(
int[] dimensions)
{
Dimensions = dimensions;
}
/// <inheritdoc />
public int[] Dimensions { get; }
/// <inheritdoc />
public int Count => Dimensions.NumberOfElements();
/// <summary>
/// Returns a new empty array.
/// </summary>
/// <returns>Empty array.</returns>
public static HdfArray Empty()
{
return new HdfArray(Array.Empty<int>());
}
public virtual double[] ConvertToDoubleArray()
{
return null;
}
public virtual Complex[] ConvertToComplexArray()
{
return null;
}
/// <inheritdoc />
public bool IsEmpty => Dimensions.Length == 0;
}
/// <summary>
/// A numerical array.
/// </summary>
/// <typeparam name="T">Element type.</typeparam>
internal class HdfNumericalArrayOf<T> : HdfArray, IArrayOf<T>
where T : struct
{
/// <summary>
/// Initializes a new instance of the <see cref="HdfNumericalArrayOf{T}"/> class.
/// </summary>
/// <param name="dimensions">Dimensions of the array.</param>
/// <param name="name">Array name.</param>
/// <param name="data">Array contents.</param>
public HdfNumericalArrayOf(int[] dimensions, T[] data)
: base(dimensions)
{
Data = data;
}
/// <inheritdoc />
public T[] Data { get; }
/// <inheritdoc />
public T this[params int[] list]
{
get => Data[Dimensions.DimFlatten(list)];
set => Data[Dimensions.DimFlatten(list)] = value;
}
/// <summary>
/// Tries to convert the array to an array of Double values.
/// </summary>
/// <returns>Array of values of the array, converted to Double, or null if the conversion is not possible.</returns>
public override double[] ConvertToDoubleArray()
{
return Data as double[] ?? Data.Select(x => Convert.ToDouble(x)).ToArray();
}
/// <summary>
/// Tries to convert the array to an array of Complex values.
/// </summary>
/// <returns>Array of values of the array, converted to Complex, or null if the conversion is not possible.</returns>
public override Complex[] ConvertToComplexArray()
{
if (Data is Complex[])
{
return Data as Complex[];
}
if (Data is ComplexOf<sbyte>[])
{
return ConvertToComplex(Data as ComplexOf<sbyte>[]);
}
if (Data is ComplexOf<byte>[])
{
return ConvertToComplex(Data as ComplexOf<byte>[]);
}
if (Data is ComplexOf<short>[])
{
return ConvertToComplex(Data as ComplexOf<short>[]);
}
if (Data is ComplexOf<ushort>[])
{
return ConvertToComplex(Data as ComplexOf<ushort>[]);
}
if (Data is ComplexOf<int>[])
{
return ConvertToComplex(Data as ComplexOf<int>[]);
}
if (Data is ComplexOf<uint>[])
{
return ConvertToComplex(Data as ComplexOf<uint>[]);
}
if (Data is ComplexOf<long>[])
{
return ConvertToComplex(Data as ComplexOf<long>[]);
}
if (Data is ComplexOf<ulong>[])
{
return ConvertToComplex(Data as ComplexOf<ulong>[]);
}
return ConvertToDoubleArray().Select(x => new Complex(x, 0.0)).ToArray();
}
private static Complex[] ConvertToComplex<TS>(IEnumerable<ComplexOf<TS>> 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)
{
case ArrayType.MxChar:
return ReadCharArray(datasetId, dims);
case ArrayType.MxInt8:
return ReadNumericalArray<sbyte>(datasetId, dims, arrayType);
case ArrayType.MxUInt8:
return ReadNumericalArray<byte>(datasetId, dims, arrayType);
case ArrayType.MxInt16:
return ReadNumericalArray<short>(datasetId, dims, arrayType);
case ArrayType.MxUInt16:
return ReadNumericalArray<ushort>(datasetId, dims, arrayType);
case ArrayType.MxInt32:
return ReadNumericalArray<int>(datasetId, dims, arrayType);
case ArrayType.MxUInt32:
return ReadNumericalArray<uint>(datasetId, dims, arrayType);
case ArrayType.MxInt64:
return ReadNumericalArray<long>(datasetId, dims, arrayType);
case ArrayType.MxUInt64:
return ReadNumericalArray<ulong>(datasetId, dims, arrayType);
case ArrayType.MxSingle:
return ReadNumericalArray<float>(datasetId, dims, arrayType);
case ArrayType.MxDouble:
return ReadNumericalArray<double>(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<T>(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<T>(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<T>(data, arrayType);
return new HdfNumericalArrayOf<T>(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);
}

View File

@ -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
{