Add XML documentation

This commit is contained in:
Alexander Luzgarev 2019-03-21 21:30:30 +01:00
parent bdd04a80b7
commit e6d236139c
22 changed files with 651 additions and 254 deletions

View File

@ -1,10 +1,15 @@
using System.IO;
// Copyright 2017-2018 Alexander Luzgarev
using System.IO;
using System.Linq;
using System.Numerics;
using NUnit.Framework;
namespace MatFileHandler.Tests
{
/// <summary>
/// Tests of file reading API (HDF cases).
/// </summary>
[TestFixture]
public class MatFileReaderHdfTests
{

View File

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

View File

@ -25,60 +25,15 @@ namespace MatFileHandler
this.subsystemData = subsystemData ?? throw new ArgumentNullException(nameof(subsystemData));
}
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 DataElementWithArrayFlags 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 DataElementWithArrayFlags(element);
}
}
/// <summary>
/// Read a data element.
/// </summary>
/// <param name="reader">Input reader.</param>
/// <returns>Data element.</returns>
public DataElementWithArrayFlags Read(BinaryReader reader)
public DataElementWithMetadata Read(BinaryReader reader)
{
var (dataReader, tag) = ReadTag(reader);
DataElementWithArrayFlags result = ReadElementWithFlags(tag, dataReader);
DataElementWithMetadata result = ReadElementWithFlags(tag, dataReader);
if (tag.Type != DataType.MiCompressed)
{
var position = reader.BaseStream.Position;
@ -211,9 +166,7 @@ namespace MatFileHandler
private DataElement ContinueReadingCellArray(
BinaryReader reader,
ArrayFlags flags,
int[] dimensions,
string name)
int[] dimensions)
{
var numberOfElements = dimensions.NumberOfElements();
var elements = new List<IArray>();
@ -226,7 +179,7 @@ namespace MatFileHandler
return new MatCellArray(dimensions, elements);
}
private DataElementWithArrayFlags ContinueReadingOpaque(BinaryReader reader)
private DataElementWithMetadata ContinueReadingOpaque(BinaryReader reader)
{
var nameElement = Read(reader).Element as MiNum<sbyte> ??
throw new HandlerException("Unexpected type in object name.");
@ -242,7 +195,7 @@ namespace MatFileHandler
if (data is MatNumericalArrayOf<uint> linkElement)
{
var (dimensions, indexToObjectId, classIndex) = ParseOpaqueData(linkElement.Data);
return new DataElementWithArrayFlags(
return new DataElementWithMetadata(
new OpaqueLink(
typeDescription,
className,
@ -256,7 +209,7 @@ namespace MatFileHandler
}
else
{
return new DataElementWithArrayFlags(
return new DataElementWithMetadata(
new Opaque(
typeDescription,
className,
@ -267,7 +220,7 @@ namespace MatFileHandler
}
}
private DataElementWithArrayFlags ContinueReadingSparseArray(
private DataElementWithMetadata ContinueReadingSparseArray(
BinaryReader reader,
DataElement firstElement,
int[] dimensions,
@ -281,7 +234,7 @@ namespace MatFileHandler
var data = Read(reader).Element;
if (arrayFlags.Variable.HasFlag(Variable.IsLogical))
{
return new DataElementWithArrayFlags(
return new DataElementWithMetadata(
DataElementConverter.ConvertToMatSparseArrayOf<bool>(
arrayFlags,
dimensions,
@ -296,7 +249,7 @@ namespace MatFileHandler
if (arrayFlags.Variable.HasFlag(Variable.IsComplex))
{
var imaginaryData = Read(reader).Element;
return new DataElementWithArrayFlags(
return new DataElementWithMetadata(
DataElementConverter.ConvertToMatSparseArrayOfComplex(
dimensions,
rowIndex.Data,
@ -311,7 +264,7 @@ namespace MatFileHandler
switch (data)
{
case MiNum<double> _:
return new DataElementWithArrayFlags(
return new DataElementWithMetadata(
DataElementConverter.ConvertToMatSparseArrayOf<double>(
arrayFlags,
dimensions,
@ -352,7 +305,7 @@ namespace MatFileHandler
return new MatStructureArray(dimensions, fields);
}
private DataElementWithArrayFlags Read(Stream stream)
private DataElementWithMetadata Read(Stream stream)
{
using (var reader = new BinaryReader(stream))
{
@ -360,7 +313,7 @@ namespace MatFileHandler
}
}
private DataElementWithArrayFlags ReadCompressed(Tag tag, BinaryReader reader)
private DataElementWithMetadata ReadCompressed(Tag tag, BinaryReader reader)
{
reader.ReadBytes(2);
var compressedData = new byte[tag.Length - 6];
@ -379,11 +332,56 @@ namespace MatFileHandler
return Read(resultStream);
}
private DataElementWithArrayFlags 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 new DataElementWithArrayFlags(MatArray.Empty());
return new DataElementWithMetadata(MatArray.Empty());
}
var element1 = Read(reader).Element;
@ -403,7 +401,7 @@ namespace MatFileHandler
var name = ReadName(element3);
if (flags.Class == ArrayType.MxCell)
{
return new DataElementWithArrayFlags(ContinueReadingCellArray(reader, flags, dimensions, name));
return new DataElementWithMetadata(ContinueReadingCellArray(reader, dimensions));
}
if (flags.Class == ArrayType.MxSparse)
@ -425,7 +423,7 @@ namespace MatFileHandler
var fieldNameLengthElement = data as MiNum<int> ??
throw new HandlerException(
"Unexpected type in structure field name length.");
return new DataElementWithArrayFlags(
return new DataElementWithMetadata(
ContinueReadingStructure(reader, dimensions, fieldNameLengthElement.Data[0]),
flags,
name);
@ -437,7 +435,7 @@ namespace MatFileHandler
switch (data)
{
case MiNum<byte> _:
return new DataElementWithArrayFlags(
return new DataElementWithMetadata(
DataElementConverter.ConvertToMatNumericalArrayOf<byte>(
flags,
dimensions,
@ -446,7 +444,7 @@ namespace MatFileHandler
flags,
name);
case MiNum<ushort> _:
return new DataElementWithArrayFlags(
return new DataElementWithMetadata(
DataElementConverter.ConvertToMatNumericalArrayOf<ushort>(
flags,
dimensions,
@ -459,7 +457,7 @@ namespace MatFileHandler
$"This type of char array ({data.GetType()}) is not supported.");
}
case ArrayType.MxInt8:
return new DataElementWithArrayFlags(
return new DataElementWithMetadata(
DataElementConverter.ConvertToMatNumericalArrayOf<sbyte>(
flags,
dimensions,
@ -470,7 +468,7 @@ namespace MatFileHandler
case ArrayType.MxUInt8:
if (flags.Variable.HasFlag(Variable.IsLogical))
{
return new DataElementWithArrayFlags(
return new DataElementWithMetadata(
DataElementConverter.ConvertToMatNumericalArrayOf<bool>(
flags,
dimensions,
@ -480,7 +478,7 @@ namespace MatFileHandler
name);
}
return new DataElementWithArrayFlags(
return new DataElementWithMetadata(
DataElementConverter.ConvertToMatNumericalArrayOf<byte>(
flags,
dimensions,
@ -489,7 +487,7 @@ namespace MatFileHandler
flags,
name);
case ArrayType.MxInt16:
return new DataElementWithArrayFlags(
return new DataElementWithMetadata(
DataElementConverter.ConvertToMatNumericalArrayOf<short>(
flags,
dimensions,
@ -498,7 +496,7 @@ namespace MatFileHandler
flags,
name);
case ArrayType.MxUInt16:
return new DataElementWithArrayFlags(
return new DataElementWithMetadata(
DataElementConverter.ConvertToMatNumericalArrayOf<ushort>(
flags,
dimensions,
@ -507,7 +505,7 @@ namespace MatFileHandler
flags,
name);
case ArrayType.MxInt32:
return new DataElementWithArrayFlags(
return new DataElementWithMetadata(
DataElementConverter.ConvertToMatNumericalArrayOf<int>(
flags,
dimensions,
@ -516,7 +514,7 @@ namespace MatFileHandler
flags,
name);
case ArrayType.MxUInt32:
return new DataElementWithArrayFlags(
return new DataElementWithMetadata(
DataElementConverter.ConvertToMatNumericalArrayOf<uint>(
flags,
dimensions,
@ -525,7 +523,7 @@ namespace MatFileHandler
flags,
name);
case ArrayType.MxInt64:
return new DataElementWithArrayFlags(
return new DataElementWithMetadata(
DataElementConverter.ConvertToMatNumericalArrayOf<long>(
flags,
dimensions,
@ -534,7 +532,7 @@ namespace MatFileHandler
flags,
name);
case ArrayType.MxUInt64:
return new DataElementWithArrayFlags(
return new DataElementWithMetadata(
DataElementConverter.ConvertToMatNumericalArrayOf<ulong>(
flags,
dimensions,
@ -543,7 +541,7 @@ namespace MatFileHandler
flags,
name);
case ArrayType.MxSingle:
return new DataElementWithArrayFlags(
return new DataElementWithMetadata(
DataElementConverter.ConvertToMatNumericalArrayOf<float>(
flags,
dimensions,
@ -552,7 +550,7 @@ namespace MatFileHandler
flags,
name);
case ArrayType.MxDouble:
return new DataElementWithArrayFlags(
return new DataElementWithMetadata(
DataElementConverter.ConvertToMatNumericalArrayOf<double>(
flags,
dimensions,

View File

@ -1,28 +0,0 @@
namespace MatFileHandler
{
internal class DataElementWithArrayFlags
{
public DataElementWithArrayFlags(DataElement element, ArrayFlags flags, string name, uint nzMax = 0)
{
Element = element;
Flags = flags;
Name = name;
NzMax = nzMax;
}
public DataElementWithArrayFlags(DataElement element)
{
Element = element;
Flags = default;
Name = default;
}
public DataElement Element { get; }
public ArrayFlags Flags { get; }
public string Name { get; }
public uint NzMax { get; }
}
}

View File

@ -0,0 +1,54 @@
// Copyright 2017-2018 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

@ -363,6 +363,14 @@ namespace MatFileHandler
$"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,

View File

@ -1,18 +1,32 @@
using System;
// Copyright 2017-2018 Alexander Luzgarev
using System;
using System.Runtime.InteropServices;
using HDF.PInvoke;
namespace MatFileHandler.Hdf
{
/// <summary>
/// Wrapper for HDF attribute.
/// </summary>
internal struct Attribute : IDisposable
{
public long Id { get; private set; }
/// <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)
@ -22,6 +36,28 @@ namespace MatFileHandler.Hdf
}
}
/// <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)))
@ -32,19 +68,14 @@ namespace MatFileHandler.Hdf
}
}
/// <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);
}
public Type GetHdfType()
{
return new Type(H5A.get_type(Id));
}
public Space GetSpace()
{
return new Space(H5A.get_space(Id));
}
}
}

View File

@ -1,23 +1,41 @@
using System;
// Copyright 2017-2018 Alexander Luzgarev
using System;
using HDF.PInvoke;
namespace MatFileHandler.Hdf
{
/// <summary>
/// HDF class.
/// </summary>
internal struct Class : IEquatable<Class>
{
public Class(H5T.class_t c)
/// <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)
{
C = c;
this.classT = classT;
}
public static Class String => new Class(H5T.class_t.STRING);
public static Class Reference => new Class(H5T.class_t.REFERENCE);
public static Class Compound => new Class(H5T.class_t.COMPOUND);
public H5T.class_t C { get; }
public static bool operator ==(Class one, Class other)
{
return one.Equals(other);
@ -28,19 +46,26 @@ namespace MatFileHandler.Hdf
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 C == other.C;
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)C;
return (int)classT;
}
}
}

View File

@ -1,22 +1,50 @@
using System;
// Copyright 2017-2018 Alexander Luzgarev
using System;
using HDF.PInvoke;
namespace MatFileHandler.Hdf
{
/// <summary>
/// HDF dataset.
/// </summary>
internal struct Dataset : IDisposable
{
public long Id { get; private set; }
/// <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)
@ -26,31 +54,48 @@ namespace MatFileHandler.Hdf
}
}
/// <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);
}
public bool AttributeExists(string name)
{
return H5A.exists_by_name(Id, ".", name) != 0;
}
public Type GetHdfType()
{
return new Type(H5D.get_type(Id));
}
public int GetStorageSize()
{
return (int)H5D.get_storage_size(Id);
}
/// <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

@ -1,17 +1,41 @@
using System;
// Copyright 2017-2018 Alexander Luzgarev
using System;
using HDF.PInvoke;
namespace MatFileHandler.Hdf
{
/// <summary>
/// Hdf group.
/// </summary>
internal struct Group : IDisposable
{
public long Id { get; private set; }
/// <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)
@ -21,14 +45,14 @@ namespace MatFileHandler.Hdf
}
}
/// <summary>
/// Get group attribute.
/// </summary>
/// <param name="name">Attribute name.</param>
/// <returns>Attribute.</returns>
public Attribute GetAttribute(string name)
{
return new Attribute(Id, name);
}
public bool AttributeExists(string name)
{
return H5A.exists_by_name(Id, ".", name) != 0;
}
}
}

View File

@ -1,20 +1,80 @@
namespace MatFileHandler.Hdf
// Copyright 2017-2018 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

@ -1,15 +1,27 @@
using System;
// Copyright 2017-2018 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/>

View File

@ -1,32 +1,35 @@
using System;
// Copyright 2017-2018 Alexander Luzgarev
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using HDF.PInvoke;
namespace MatFileHandler.Hdf
{
/// <summary>
/// Array of HDF references stored in an HDF dataset.
/// </summary>
internal struct ReferenceArray : IDisposable, IEnumerable<Dataset>
{
public Dataset Dataset { get; }
public int Size { get; }
public MemoryHandle Buf { get; }
public Dataset[] References { get; }
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];
references = new Dataset[size];
for (var i = 0; i < size; i++)
{
References[i] =
references[i] =
new Dataset(H5R.dereference(
dataset.Id,
H5P.DEFAULT,
@ -35,26 +38,48 @@ namespace MatFileHandler.Hdf
}
}
/// <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))
if (References is null)
{
foreach (var reference in References)
{
reference.Dispose();
}
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

@ -1,22 +1,42 @@
using System.Linq;
// Copyright 2017-2018 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()];

View File

@ -1,70 +1,147 @@
using System;
// Copyright 2017-2018 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);
}
public static Type NativeInt8 => new Type(H5T.NATIVE_INT8);
public static Type NativeUInt8 => new Type(H5T.NATIVE_UINT8);
public static Type NativeInt16 => new Type(H5T.NATIVE_INT16);
public static Type NativeUInt16 => new Type(H5T.NATIVE_UINT16);
public static Type NativeInt32 => new Type(H5T.NATIVE_INT32);
public static Type NativeUInt32 => new Type(H5T.NATIVE_UINT32);
public static Type NativeInt64 => new Type(H5T.NATIVE_INT64);
public static Type NativeUInt64 => new Type(H5T.NATIVE_UINT64);
public static Type NativeFloat => new Type(H5T.NATIVE_FLOAT);
public static Type NativeDouble => new Type(H5T.NATIVE_DOUBLE);
public static Type NativeInt => new Type(H5T.NATIVE_INT);
public static Type NativeUInt => new Type(H5T.NATIVE_UINT);
public static Type CS1 => new Type(H5T.C_S1);
public static Type Reference => new Type(H5T.STD_REF_OBJ);
/// <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);
}
public static Type CreateCompound(int size)
{
return new Type(H5T.create(H5T.class_t.COMPOUND, (IntPtr)size));
}
public void InsertField(string name, Type fieldType)
{
H5T.insert(Id, name, IntPtr.Zero, fieldType.Id);
}
}
}

View File

@ -1,4 +1,6 @@
using System;
// Copyright 2017-2018 Alexander Luzgarev
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
@ -9,29 +11,40 @@ 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 ClassAttributeName = "MATLAB_class";
private const string globalAttributeName = "MATLAB_global";
private const string GlobalAttributeName = "MATLAB_global";
private const string sparseAttributeName = "MATLAB_sparse";
private long fileId;
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)
{
@ -117,12 +130,22 @@ namespace MatFileHandler
Marshal.Copy(buf.Handle, matlabClassNameBytes, 0, typeIdSize);
}
return Encoding.ASCII.GetString(matlabClassNameBytes);
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))
using (var attribute = dataset.GetAttribute(ClassAttributeName))
{
return GetMatlabClassFromAttribute(attribute);
}
@ -130,7 +153,7 @@ namespace MatFileHandler
private static string GetMatlabClassOfGroup(Group group)
{
using (var attribute = group.GetAttribute(classAttributeName))
using (var attribute = group.GetAttribute(ClassAttributeName))
{
return GetMatlabClassFromAttribute(attribute);
}
@ -315,9 +338,8 @@ namespace MatFileHandler
return ReadStruct(group.Id);
}
if (group.AttributeExists(sparseAttributeName))
if (group.AttributeExists(SparseAttributeName))
{
var dims = new int[0];
var arrayType = ArrayTypeFromMatlabClassName(matlabClass);
switch (arrayType)
@ -378,7 +400,6 @@ namespace MatFileHandler
{
var numberOfElements = dims.NumberOfElements();
var dataSize = numberOfElements * SizeOfArrayElement(arrayType);
var storageSize = dataset.GetStorageSize();
var dataSetType = dataset.GetHdfType();
var dataSetTypeClass = dataSetType.GetClass();
var isCompound = dataSetTypeClass == Class.Compound;
@ -405,11 +426,6 @@ namespace MatFileHandler
}
}
if (dataSize != storageSize)
{
throw new Exception("Data size mismatch.");
}
var data = ReadDataset(dataset, H5tTypeFromHdfMatlabClass(arrayType), dataSize);
var convertedData = ConvertDataToProperType<T>(data, arrayType);
return new MatNumericalArrayOf<T>(dims, convertedData);
@ -418,7 +434,7 @@ namespace MatFileHandler
private static IArray ReadSparseArray<T>(long groupId, MatlabClass arrayType)
where T : struct
{
using (var sparseAttribute = new Hdf.Attribute(groupId, sparseAttributeName))
using (var sparseAttribute = new Hdf.Attribute(groupId, SparseAttributeName))
{
using (var numberOfRowsHandle = new MemoryHandle(sizeof(uint)))
{
@ -464,7 +480,7 @@ namespace MatFileHandler
var complexData =
CombineComplexData(
convertedRealData as double[],
convertedImaginaryData as double[])
convertedImaginaryData as double[])
.ToArray();
var complexDataDictionary =
DataExtraction.ConvertMatlabSparseToDictionary(
@ -475,7 +491,7 @@ namespace MatFileHandler
}
else
{
var complexData =
var complexData =
CombineComplexOfData<T>(
convertedRealData,
convertedImaginaryData)
@ -489,11 +505,6 @@ namespace MatFileHandler
}
}
if (dataSize != storageSize)
{
throw new Exception("Data size mismatch.");
}
var d = ReadDataset(data, H5tTypeFromHdfMatlabClass(arrayType), dataSize);
var elements = ConvertDataToProperType<T>(d, arrayType);
var dataDictionary =
@ -515,7 +526,7 @@ namespace MatFileHandler
var firstFieldType = firstField.GetHdfType();
if (firstFieldType.GetClass() == Class.Reference)
{
if (firstField.AttributeExists(classAttributeName))
if (firstField.AttributeExists(ClassAttributeName))
{
throw new NotImplementedException();
}
@ -592,12 +603,12 @@ namespace MatFileHandler
private bool ReadGlobalFlag(Group group)
{
if (!group.AttributeExists(globalAttributeName))
if (!group.AttributeExists(GlobalAttributeName))
{
return false;
}
using (var globalAttribute = group.GetAttribute(globalAttributeName))
using (var globalAttribute = group.GetAttribute(GlobalAttributeName))
{
return globalAttribute.ReadBool();
}
@ -605,12 +616,12 @@ namespace MatFileHandler
private bool ReadGlobalFlag(Dataset dataset)
{
if (!dataset.AttributeExists(globalAttributeName))
if (!dataset.AttributeExists(GlobalAttributeName))
{
return false;
}
using (var globalAttribute = dataset.GetAttribute(globalAttributeName))
using (var globalAttribute = dataset.GetAttribute(GlobalAttributeName))
{
return globalAttribute.ReadBool();
}

View File

@ -21,6 +21,9 @@ namespace MatFileHandler
Version = version;
}
/// <summary>
/// Gets raw byte contents of the header.
/// </summary>
public byte[] RawBytes { get; }
/// <summary>

View File

@ -1,12 +1,23 @@
using System;
// Copyright 2017-2018 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())

View File

@ -1,4 +1,6 @@
using System.Collections.Generic;
// Copyright 2017-2018 Alexander Luzgarev
using System.Collections.Generic;
using System.IO;
namespace MatFileHandler
@ -61,6 +63,12 @@ namespace MatFileHandler
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);

View File

@ -33,6 +33,11 @@ namespace MatFileHandler
}
}
private static Header ReadHeader(BinaryReader reader)
{
return Header.Read(reader);
}
private IMatFile Read(BinaryReader reader)
{
var header = ReadHeader(reader);
@ -46,10 +51,5 @@ namespace MatFileHandler
throw new NotSupportedException($"Unknown file format.");
}
}
private static Header ReadHeader(BinaryReader reader)
{
return Header.Read(reader);
}
}
}

View File

@ -43,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

@ -17,6 +17,8 @@ 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>
/// <param name="flags">Array flags.</param>
/// <param name="name">Variable name.</param>
internal RawVariable(long offset, DataElement dataElement, ArrayFlags flags, string name)
{
Offset = offset;
@ -30,8 +32,14 @@ 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>