Use C# 8.0 and enable nullable reference types #10

Merged
mahalex merged 8 commits from nullable into master 2019-10-05 13:24:42 +00:00
24 changed files with 1542 additions and 324 deletions

View File

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework> <TargetFramework>netcoreapp3.0</TargetFramework>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">

View File

@ -33,8 +33,8 @@ namespace MatFileHandler
DataElement data, DataElement data,
DataElement imaginaryData) DataElement imaginaryData)
{ {
var realParts = DataExtraction.GetDataAsDouble(data).ToArrayLazily(); var realParts = DataExtraction.GetDataAsDouble(data);
var imaginaryParts = DataExtraction.GetDataAsDouble(imaginaryData).ToArrayLazily(); var imaginaryParts = DataExtraction.GetDataAsDouble(imaginaryData);
if (realParts == null) if (realParts == null)
{ {
throw new HandlerException("Couldn't read sparse array."); throw new HandlerException("Couldn't read sparse array.");
@ -106,12 +106,12 @@ namespace MatFileHandler
int[] dimensions, int[] dimensions,
string name, string name,
DataElement realData, DataElement realData,
DataElement imaginaryData) DataElement? imaginaryData)
where T : struct where T : struct
{ {
if (flags.Variable.HasFlag(Variable.IsLogical)) if (flags.Variable.HasFlag(Variable.IsLogical))
{ {
var data = DataExtraction.GetDataAsUInt8(realData).ToArrayLazily().Select(x => x != 0).ToArray(); var data = DataExtraction.GetDataAsUInt8(realData).Select(x => x != 0).ToArray();
return new MatNumericalArrayOf<bool>(flags, dimensions, name, data); return new MatNumericalArrayOf<bool>(flags, dimensions, name, data);
} }
switch (flags.Class) switch (flags.Class)
@ -139,6 +139,11 @@ namespace MatFileHandler
var dataArray = ConvertDataToProperType<T>(realData, flags.Class); var dataArray = ConvertDataToProperType<T>(realData, flags.Class);
if (flags.Variable.HasFlag(Variable.IsComplex)) if (flags.Variable.HasFlag(Variable.IsComplex))
{ {
if (imaginaryData is null)
{
throw new HandlerException("Imaginary part of a complex variable not found.");
}
var dataArray2 = ConvertDataToProperType<T>(imaginaryData, flags.Class); var dataArray2 = ConvertDataToProperType<T>(imaginaryData, flags.Class);
if (flags.Class == ArrayType.MxDouble) if (flags.Class == ArrayType.MxDouble)
{ {
@ -163,49 +168,26 @@ namespace MatFileHandler
string name, string name,
MiNum<byte> dataElement) MiNum<byte> dataElement)
{ {
var data = dataElement?.Data; var data = dataElement.Data;
return new MatCharArrayOf<byte>(flags, dimensions, name, data, Encoding.UTF8.GetString(data)); return new MatCharArrayOf<byte>(flags, dimensions, name, data, Encoding.UTF8.GetString(data));
} }
private static T[] ConvertDataToProperType<T>(DataElement data, ArrayType arrayType) private static T[] ConvertDataToProperType<T>(DataElement data, ArrayType arrayType)
{ {
switch (arrayType) return TryConvertDataToProperType<T>(data, arrayType)
{ ?? throw new HandlerException($"Unexpected data type.");
case ArrayType.MxDouble:
return DataExtraction.GetDataAsDouble(data).ToArrayLazily() as T[];
case ArrayType.MxSingle:
return DataExtraction.GetDataAsSingle(data).ToArrayLazily() as T[];
case ArrayType.MxInt8:
return DataExtraction.GetDataAsInt8(data).ToArrayLazily() as T[];
case ArrayType.MxUInt8:
return DataExtraction.GetDataAsUInt8(data).ToArrayLazily() as T[];
case ArrayType.MxInt16:
return DataExtraction.GetDataAsInt16(data).ToArrayLazily() as T[];
case ArrayType.MxUInt16:
return DataExtraction.GetDataAsUInt16(data).ToArrayLazily() as T[];
case ArrayType.MxInt32:
return DataExtraction.GetDataAsInt32(data).ToArrayLazily() as T[];
case ArrayType.MxUInt32:
return DataExtraction.GetDataAsUInt32(data).ToArrayLazily() as T[];
case ArrayType.MxInt64:
return DataExtraction.GetDataAsInt64(data).ToArrayLazily() as T[];
case ArrayType.MxUInt64:
return DataExtraction.GetDataAsUInt64(data).ToArrayLazily() as T[];
default:
throw new NotSupportedException();
}
} }
private static T[] ConvertDataToSparseProperType<T>(DataElement data, bool isLogical) private static T[]? ConvertDataToSparseProperType<T>(DataElement data, bool isLogical)
{ {
if (isLogical) if (isLogical)
{ {
return DataExtraction.GetDataAsUInt8(data).ToArrayLazily().Select(x => x != 0).ToArray() as T[]; return DataExtraction.GetDataAsUInt8(data).Select(x => x != 0).ToArray() as T[];
} }
switch (data) switch (data)
{ {
case MiNum<double> _: case MiNum<double> _:
return DataExtraction.GetDataAsDouble(data).ToArrayLazily() as T[]; return DataExtraction.GetDataAsDouble(data) as T[];
default: default:
throw new NotSupportedException(); throw new NotSupportedException();
} }
@ -217,7 +199,7 @@ namespace MatFileHandler
string name, string name,
MiNum<ushort> dataElement) MiNum<ushort> dataElement)
{ {
var data = dataElement?.Data; var data = dataElement.Data;
return new MatCharArrayOf<ushort>( return new MatCharArrayOf<ushort>(
flags, flags,
dimensions, dimensions,
@ -226,7 +208,7 @@ namespace MatFileHandler
new string(data.Select(x => (char)x).ToArray())); new string(data.Select(x => (char)x).ToArray()));
} }
private static Dictionary<(int, int), T> ConvertMatlabSparseToDictionary<T>( private static Dictionary<(int row, int column), T> ConvertMatlabSparseToDictionary<T>(
int[] rowIndex, int[] rowIndex,
int[] columnIndex, int[] columnIndex,
Func<int, T> get) Func<int, T> get)
@ -242,5 +224,23 @@ namespace MatFileHandler
} }
return result; return result;
} }
private static T[]? TryConvertDataToProperType<T>(DataElement data, ArrayType arrayType)
{
return arrayType switch
{
ArrayType.MxDouble => DataExtraction.GetDataAsDouble(data) as T[],
ArrayType.MxSingle => DataExtraction.GetDataAsSingle(data) as T[],
ArrayType.MxInt8 => DataExtraction.GetDataAsInt8(data) as T[],
ArrayType.MxUInt8 => DataExtraction.GetDataAsUInt8(data) as T[],
ArrayType.MxInt16 => DataExtraction.GetDataAsInt16(data) as T[],
ArrayType.MxUInt16 => DataExtraction.GetDataAsUInt16(data) as T[],
ArrayType.MxInt32 => DataExtraction.GetDataAsInt32(data) as T[],
ArrayType.MxUInt32 => DataExtraction.GetDataAsUInt32(data) as T[],
ArrayType.MxInt64 => DataExtraction.GetDataAsInt64(data) as T[],
ArrayType.MxUInt64 => DataExtraction.GetDataAsUInt64(data) as T[],
_ => throw new NotSupportedException()
};
}
} }
} }

View File

@ -194,7 +194,7 @@ namespace MatFileHandler
}; };
} }
private static (BinaryReader, Tag) ReadTag(BinaryReader reader) private static (BinaryReader reader, Tag tag) ReadTag(BinaryReader reader)
{ {
var type = reader.ReadInt32(); var type = reader.ReadInt32();
var typeHi = type >> 16; var typeHi = type >> 16;
@ -222,7 +222,8 @@ namespace MatFileHandler
var elements = new List<IArray>(); var elements = new List<IArray>();
for (var i = 0; i < numberOfElements; i++) for (var i = 0; i < numberOfElements; i++)
{ {
var element = Read(reader) as IArray; var element = Read(reader) as IArray
?? throw new HandlerException("Unable to read cell array.");
elements.Add(element); elements.Add(element);
} }
@ -319,8 +320,9 @@ namespace MatFileHandler
string name, string name,
int fieldNameLength) int fieldNameLength)
{ {
var element = Read(reader); var element = Read(reader) as MiNum<sbyte>
var fieldNames = ReadFieldNames(element as MiNum<sbyte>, fieldNameLength); ?? throw new HandlerException("Unable to parse structure field names.");
var fieldNames = ReadFieldNames(element, fieldNameLength);
var fields = new Dictionary<string, List<IArray>>(); var fields = new Dictionary<string, List<IArray>>();
foreach (var fieldName in fieldNames) foreach (var fieldName in fieldNames)
{ {
@ -332,7 +334,8 @@ namespace MatFileHandler
{ {
foreach (var fieldName in fieldNames) foreach (var fieldName in fieldNames)
{ {
var field = Read(reader) as IArray; var field = Read(reader) as IArray
?? throw new HandlerException("Unable to parse field name.");
fields[fieldName].Add(field); fields[fieldName].Add(field);
} }
} }
@ -398,7 +401,7 @@ namespace MatFileHandler
var element4 = Read(reader); var element4 = Read(reader);
var data = ReadData(element4); var data = ReadData(element4);
DataElement imaginaryData = null; DataElement? imaginaryData = null;
if (flags.Variable.HasFlag(Variable.IsComplex)) if (flags.Variable.HasFlag(Variable.IsComplex))
{ {
var element5 = Read(reader); var element5 = Read(reader);

File diff suppressed because it is too large Load Diff

View File

@ -30,20 +30,24 @@ namespace MatFileHandler
} }
epoch = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); epoch = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
var dataArray = matObject["data", 0] as IArrayOf<double>;
if (dataArray is null) switch (matObject["data", 0])
{ {
var dataComplex = matObject["data", 0] as IArrayOf<Complex>; case IArrayOf<double> dataArray:
var complexData = dataComplex.ConvertToComplexArray(); data = dataArray.ConvertToDoubleArray()
?? throw new HandlerException("Cannot extract data for the datetime adapter.");
data2 = new double[data.Length];
dimensions = dataArray.Dimensions;
break;
case IArrayOf<Complex> dataComplex:
var complexData = dataComplex.ConvertToComplexArray()
?? throw new HandlerException("Cannot extract data for the datetime adapter.");
data = complexData.Select(c => c.Real).ToArray(); data = complexData.Select(c => c.Real).ToArray();
data2 = complexData.Select(c => c.Imaginary).ToArray(); data2 = complexData.Select(c => c.Imaginary).ToArray();
dimensions = dataComplex.Dimensions; dimensions = dataComplex.Dimensions;
} break;
else default:
{ throw new HandlerException("Datetime data not found.");
data = dataArray.ConvertToDoubleArray();
data2 = new double[data.Length];
dimensions = dataArray.Dimensions;
} }
} }

View File

@ -25,7 +25,8 @@ namespace MatFileHandler
} }
var dataObject = matObject["millis", 0]; var dataObject = matObject["millis", 0];
data = dataObject.ConvertToDoubleArray(); data = dataObject.ConvertToDoubleArray()
?? throw new HandlerException("Cannot extract data for the duration adapter.");
dimensions = dataObject.Dimensions; dimensions = dataObject.Dimensions;
} }

View File

@ -5,7 +5,7 @@ using System;
namespace MatFileHandler namespace MatFileHandler
{ {
/// <summary> /// <summary>
/// Exception related to Matlab data handling /// Exception related to Matlab data handling.
/// </summary> /// </summary>
public class HandlerException : Exception public class HandlerException : Exception
{ {
@ -14,7 +14,7 @@ namespace MatFileHandler
/// </summary> /// </summary>
/// <param name="message">Error message.</param> /// <param name="message">Error message.</param>
/// <param name="innerException">Inner exception.</param> /// <param name="innerException">Inner exception.</param>
public HandlerException(string message, Exception innerException = null) public HandlerException(string message, Exception? innerException = null)
: base(message, innerException) : base(message, innerException)
{ {
} }

View File

@ -28,12 +28,12 @@ namespace MatFileHandler
/// Tries to convert the array to an array of Double values. /// Tries to convert the array to an array of Double values.
/// </summary> /// </summary>
/// <returns>Array of values of the array, converted to Double, or null if the conversion is not possible.</returns> /// <returns>Array of values of the array, converted to Double, or null if the conversion is not possible.</returns>
double[] ConvertToDoubleArray(); double[]? ConvertToDoubleArray();
/// <summary> /// <summary>
/// Tries to convert the array to an array of Complex values. /// Tries to convert the array to an array of Complex values.
/// </summary> /// </summary>
/// <returns>Array of values of the array, converted to Complex, or null if the conversion is not possible.</returns> /// <returns>Array of values of the array, converted to Complex, or null if the conversion is not possible.</returns>
Complex[] ConvertToComplexArray(); Complex[]? ConvertToComplexArray();
} }
} }

View File

@ -21,7 +21,7 @@ namespace MatFileHandler
/// * for cell arrays: /// * for cell arrays:
/// IArray; /// IArray;
/// * for structure arrays: /// * for structure arrays:
/// IReadOnlyDictionary&lt;string, IArray&gt;; /// IReadOnlyDictionary&lt;string, IArray&gt;.
/// </remarks> /// </remarks>
public interface IArrayOf<T> : IArray public interface IArrayOf<T> : IArray
{ {

View File

@ -15,6 +15,6 @@ namespace MatFileHandler
/// <summary> /// <summary>
/// Gets a dictionary mapping indices to values. /// Gets a dictionary mapping indices to values.
/// </summary> /// </summary>
new IReadOnlyDictionary<(int, int), T> Data { get; } new IReadOnlyDictionary<(int row, int column), T> Data { get; }
} }
} }

View File

@ -54,13 +54,13 @@ namespace MatFileHandler
} }
/// <inheritdoc /> /// <inheritdoc />
public virtual double[] ConvertToDoubleArray() public virtual double[]? ConvertToDoubleArray()
{ {
return null; return null;
} }
/// <inheritdoc /> /// <inheritdoc />
public virtual Complex[] ConvertToComplexArray() public virtual Complex[]? ConvertToComplexArray()
{ {
return null; return null;
} }

View File

@ -15,6 +15,8 @@
<OutputPath>bin\$(Configuration)\</OutputPath> <OutputPath>bin\$(Configuration)\</OutputPath>
<DocumentationFile>$(OutputPath)\$(TargetFramework)\$(AssemblyName).xml</DocumentationFile> <DocumentationFile>$(OutputPath)\$(TargetFramework)\$(AssemblyName).xml</DocumentationFile>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild> <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<LangVersion>8.0</LangVersion>
<Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<CodeAnalysisRuleSet>..\MatFileHandler.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>..\MatFileHandler.ruleset</CodeAnalysisRuleSet>
@ -26,7 +28,7 @@
<AdditionalFiles Include="..\stylecop.json" /> <AdditionalFiles Include="..\stylecop.json" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="StyleCop.Analyzers" Version="1.1.0-beta004"> <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.66">
<PrivateAssets>All</PrivateAssets> <PrivateAssets>All</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="System.ValueTuple" Version="4.4.0" Condition="'$(TargetFramework)' == 'net461'" /> <PackageReference Include="System.ValueTuple" Version="4.4.0" Condition="'$(TargetFramework)' == 'net461'" />

View File

@ -57,7 +57,8 @@ namespace MatFileHandler
var dataElement = dataElementReader.Read(reader); var dataElement = dataElementReader.Read(reader);
if (position == subsystemDataOffset) if (position == subsystemDataOffset)
{ {
var subsystemDataElement = dataElement as IArrayOf<byte>; var subsystemDataElement = dataElement as IArrayOf<byte>
?? throw new HandlerException("Cannot parse subsystem data element.");
var newSubsystemData = ReadSubsystemData(subsystemDataElement.Data, subsystemData); var newSubsystemData = ReadSubsystemData(subsystemDataElement.Data, subsystemData);
subsystemData.Set(newSubsystemData); subsystemData.Set(newSubsystemData);
} }

View File

@ -198,14 +198,14 @@ namespace MatFileHandler
return buffer; return buffer;
} }
private (byte[], byte[]) ConvertToPairOfByteArrays<T>(ComplexOf<T>[] data) private (byte[] real, byte[] imaginary) ConvertToPairOfByteArrays<T>(ComplexOf<T>[] data)
where T : struct where T : struct
{ {
return (ConvertToByteArray(data.Select(x => x.Real).ToArray()), return (ConvertToByteArray(data.Select(x => x.Real).ToArray()),
ConvertToByteArray(data.Select(x => x.Imaginary).ToArray())); ConvertToByteArray(data.Select(x => x.Imaginary).ToArray()));
} }
private (byte[], byte[]) ConvertToPairOfByteArrays(Complex[] data) private (byte[] real, byte[] imaginary) ConvertToPairOfByteArrays(Complex[] data)
{ {
return (ConvertToByteArray(data.Select(x => x.Real).ToArray()), return (ConvertToByteArray(data.Select(x => x.Real).ToArray()),
ConvertToByteArray(data.Select(x => x.Imaginary).ToArray())); ConvertToByteArray(data.Select(x => x.Imaginary).ToArray()));
@ -496,8 +496,8 @@ namespace MatFileHandler
for (var column = 0; column < numberOfColumns; column++) for (var column = 0; column < numberOfColumns; column++)
{ {
var column1 = column; var column1 = column;
var thisColumn = keys.Where(pair => pair.Item2 == column1 && !dict[pair].Equals(default(T))); var thisColumn = keys.Where(pair => pair.column == column1 && !dict[pair].Equals(default));
var thisRow = thisColumn.Select(pair => pair.Item1).OrderBy(x => x).ToArray(); var thisRow = thisColumn.Select(pair => pair.row).OrderBy(x => x).ToArray();
rowIndexList.AddRange(thisRow); rowIndexList.AddRange(thisRow);
valuesList.AddRange(thisRow.Select(row => dict[(row, column1)])); valuesList.AddRange(thisRow.Select(row => dict[(row, column1)]));
columnIndex[column + 1] = rowIndexList.Count; columnIndex[column + 1] = rowIndexList.Count;

View File

@ -43,58 +43,152 @@ namespace MatFileHandler
/// <returns>Array of values of the array, converted to Double, or null if the conversion is not possible.</returns> /// <returns>Array of values of the array, converted to Double, or null if the conversion is not possible.</returns>
public override double[] ConvertToDoubleArray() public override double[] ConvertToDoubleArray()
{ {
return Data as double[] ?? Data.Select(x => Convert.ToDouble(x)).ToArray(); return Data switch
{
sbyte[] sbyteData => DataExtraction.SbyteToDouble(sbyteData),
byte[] byteData => DataExtraction.ByteToDouble(byteData),
short[] shortData => DataExtraction.ShortToDouble(shortData),
ushort[] ushortData => DataExtraction.UshortToDouble(ushortData),
int[] intData => DataExtraction.IntToDouble(intData),
uint[] uintData => DataExtraction.UintToDouble(uintData),
long[] longData => DataExtraction.LongToDouble(longData),
ulong[] ulongData => DataExtraction.UlongToDouble(ulongData),
float[] floatData => DataExtraction.FloatToDouble(floatData),
double[] doubleData => doubleData,
_ => throw new HandlerException("Cannot convert data to double array.")
};
} }
/// <summary> /// <summary>
/// Tries to convert the array to an array of Complex values. /// Tries to convert the array to an array of Complex values.
/// </summary> /// </summary>
/// <returns>Array of values of the array, converted to Complex, or null if the conversion is not possible.</returns> /// <returns>Array of values of the array, converted to Complex, or null if the conversion is not possible.</returns>
public override Complex[] ConvertToComplexArray() public override Complex[]? ConvertToComplexArray()
{ {
if (Data is Complex[]) return Data switch
{ {
return Data as Complex[]; Complex[] data => data,
} ComplexOf<sbyte>[] ofs => ConvertToComplex(ofs),
if (Data is ComplexOf<sbyte>[]) ComplexOf<byte>[] ofs => ConvertToComplex(ofs),
{ ComplexOf<short>[] ofs => ConvertToComplex(ofs),
return ConvertToComplex(Data as ComplexOf<sbyte>[]); ComplexOf<ushort>[] ofs => ConvertToComplex(ofs),
} ComplexOf<int>[] ofs => ConvertToComplex(ofs),
if (Data is ComplexOf<byte>[]) ComplexOf<uint>[] ofs => ConvertToComplex(ofs),
{ ComplexOf<long>[] ofs => ConvertToComplex(ofs),
return ConvertToComplex(Data as ComplexOf<byte>[]); ComplexOf<ulong>[] ofs => ConvertToComplex(ofs),
} ComplexOf<float>[] ofs => ConvertToComplex(ofs),
if (Data is ComplexOf<short>[]) _ => ConvertToComplex(ConvertToDoubleArray())
{ };
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) private static Complex[] ConvertToComplex(ComplexOf<sbyte>[] array)
where TS : struct
{ {
return array.Select(x => new Complex(Convert.ToDouble(x.Real), Convert.ToDouble(x.Imaginary))).ToArray(); var result = new Complex[array.Length];
for (var i = 0; i < array.Length; i++)
{
result[i] = new Complex(Convert.ToDouble(array[i].Real), Convert.ToDouble(array[i].Imaginary));
}
return result;
}
private static Complex[] ConvertToComplex(ComplexOf<byte>[] array)
{
var result = new Complex[array.Length];
for (var i = 0; i < array.Length; i++)
{
result[i] = new Complex(Convert.ToDouble(array[i].Real), Convert.ToDouble(array[i].Imaginary));
}
return result;
}
private static Complex[] ConvertToComplex(ComplexOf<short>[] array)
{
var result = new Complex[array.Length];
for (var i = 0; i < array.Length; i++)
{
result[i] = new Complex(Convert.ToDouble(array[i].Real), Convert.ToDouble(array[i].Imaginary));
}
return result;
}
private static Complex[] ConvertToComplex(ComplexOf<ushort>[] array)
{
var result = new Complex[array.Length];
for (var i = 0; i < array.Length; i++)
{
result[i] = new Complex(Convert.ToDouble(array[i].Real), Convert.ToDouble(array[i].Imaginary));
}
return result;
}
private static Complex[] ConvertToComplex(ComplexOf<int>[] array)
{
var result = new Complex[array.Length];
for (var i = 0; i < array.Length; i++)
{
result[i] = new Complex(Convert.ToDouble(array[i].Real), Convert.ToDouble(array[i].Imaginary));
}
return result;
}
private static Complex[] ConvertToComplex(ComplexOf<uint>[] array)
{
var result = new Complex[array.Length];
for (var i = 0; i < array.Length; i++)
{
result[i] = new Complex(Convert.ToDouble(array[i].Real), Convert.ToDouble(array[i].Imaginary));
}
return result;
}
private static Complex[] ConvertToComplex(ComplexOf<long>[] array)
{
var result = new Complex[array.Length];
for (var i = 0; i < array.Length; i++)
{
result[i] = new Complex(Convert.ToDouble(array[i].Real), Convert.ToDouble(array[i].Imaginary));
}
return result;
}
private static Complex[] ConvertToComplex(ComplexOf<ulong>[] array)
{
var result = new Complex[array.Length];
for (var i = 0; i < array.Length; i++)
{
result[i] = new Complex(Convert.ToDouble(array[i].Real), Convert.ToDouble(array[i].Imaginary));
}
return result;
}
private static Complex[] ConvertToComplex(ComplexOf<float>[] array)
{
var result = new Complex[array.Length];
for (var i = 0; i < array.Length; i++)
{
result[i] = new Complex(Convert.ToDouble(array[i].Real), Convert.ToDouble(array[i].Imaginary));
}
return result;
}
private static Complex[] ConvertToComplex(double[] array)
{
var result = new Complex[array.Length];
for (var i = 0; i < array.Length; i++)
{
result[i] = new Complex(array[i], 0.0);
}
return result;
} }
} }
} }

View File

@ -26,7 +26,7 @@ namespace MatFileHandler
SparseArrayFlags flags, SparseArrayFlags flags,
int[] dimensions, int[] dimensions,
string name, string name,
Dictionary<(int, int), T> data) Dictionary<(int row, int column), T> data)
: base(flags.ArrayFlags, dimensions, name) : base(flags.ArrayFlags, dimensions, name)
{ {
DataDictionary = data; DataDictionary = data;
@ -39,9 +39,9 @@ namespace MatFileHandler
.ToArray(); .ToArray();
/// <inheritdoc /> /// <inheritdoc />
public IReadOnlyDictionary<(int, int), T> Data => DataDictionary; public IReadOnlyDictionary<(int row, int column), T> Data => DataDictionary;
private Dictionary<(int, int), T> DataDictionary { get; } private Dictionary<(int row, int column), T> DataDictionary { get; }
/// <inheritdoc /> /// <inheritdoc />
public T this[params int[] list] public T this[params int[] list]

View File

@ -35,7 +35,19 @@ namespace MatFileHandler
/// <summary> /// <summary>
/// Gets null: not implemented. /// Gets null: not implemented.
/// </summary> /// </summary>
public IReadOnlyDictionary<string, IArray>[] Data => null; public IReadOnlyDictionary<string, IArray>[] Data
{
get
{
var result = new IReadOnlyDictionary<string, IArray>[Count];
for (var i = 0; i < Count; i++)
{
result[i] = FieldNames.ToDictionary(name => name, name => Fields[name][i]);
}
return result;
}
}
/// <summary> /// <summary>
/// Gets a dictionary that maps field names to lists of values. /// Gets a dictionary that maps field names to lists of values.
@ -128,7 +140,7 @@ namespace MatFileHandler
/// <summary> /// <summary>
/// Checks if the structure has a given field. /// Checks if the structure has a given field.
/// </summary> /// </summary>
/// <param name="key">Field name</param> /// <param name="key">Field name.</param>
/// <returns>True iff the structure has a given field.</returns> /// <returns>True iff the structure has a given field.</returns>
public bool ContainsKey(string key) => Parent.Fields.ContainsKey(key); public bool ContainsKey(string key) => Parent.Fields.ContainsKey(key);
@ -138,12 +150,14 @@ namespace MatFileHandler
/// <param name="key">Field name.</param> /// <param name="key">Field name.</param>
/// <param name="value">Value (or null if the field is not present).</param> /// <param name="value">Value (or null if the field is not present).</param>
/// <returns>Success status of the query.</returns> /// <returns>Success status of the query.</returns>
public bool TryGetValue(string key, out IArray value) #pragma warning disable CS8614
public bool TryGetValue(string key, out IArray? value)
#pragma warning restore CS8614
{ {
var success = Parent.Fields.TryGetValue(key, out var array); var success = Parent.Fields.TryGetValue(key, out var array);
if (!success) if (!success)
{ {
value = default(IArray); value = default;
return false; return false;
} }
value = array[Index]; value = array[Index];

View File

@ -50,9 +50,9 @@ namespace MatFileHandler
public string TypeDescription { get; } public string TypeDescription { get; }
/// <inheritdoc /> /// <inheritdoc />
public override Complex[] ConvertToComplexArray() => null; public override Complex[]? ConvertToComplexArray() => null;
/// <inheritdoc /> /// <inheritdoc />
public override double[] ConvertToDoubleArray() => null; public override double[]? ConvertToDoubleArray() => null;
} }
} }

View File

@ -47,7 +47,21 @@ namespace MatFileHandler
public int ClassIndex { get; } public int ClassIndex { get; }
/// <inheritdoc /> /// <inheritdoc />
public IReadOnlyDictionary<string, IArray>[] Data => null; public IReadOnlyDictionary<string, IArray>[] Data
{
get
{
var result = new IReadOnlyDictionary<string, IArray>[Count];
for (var i = 0; i < Count; i++)
{
result[i] = FieldNamesArray.ToDictionary(
name => name,
name => this[name, i]);
}
return result;
}
}
/// <inheritdoc /> /// <inheritdoc />
public IEnumerable<string> FieldNames => FieldNamesArray; public IEnumerable<string> FieldNames => FieldNamesArray;
@ -71,7 +85,7 @@ namespace MatFileHandler
{ {
if (TryGetValue(field, out var result, list)) if (TryGetValue(field, out var result, list))
{ {
return result; return result!;
} }
throw new IndexOutOfRangeException(); throw new IndexOutOfRangeException();
@ -91,19 +105,19 @@ namespace MatFileHandler
return new OpaqueObjectArrayElement(this, i); return new OpaqueObjectArrayElement(this, i);
} }
private bool TryGetValue(string field, out IArray output, params int[] list) private bool TryGetValue(string field, out IArray? output, params int[] list)
{ {
var index = Dimensions.DimFlatten(list); var index = Dimensions.DimFlatten(list);
var maybeFieldIndex = subsystemData.ClassInformation[ClassIndex].FindField(field); var maybeFieldIndex = subsystemData.ClassInformation[ClassIndex].FindField(field);
if (!(maybeFieldIndex is int fieldIndex)) if (!(maybeFieldIndex is int fieldIndex))
{ {
output = default(IArray); output = default;
return false; return false;
} }
if (index >= IndexToObjectId.Length) if (index >= IndexToObjectId.Length)
{ {
output = default(IArray); output = default;
return false; return false;
} }
@ -161,7 +175,9 @@ namespace MatFileHandler
} }
/// <inheritdoc /> /// <inheritdoc />
public bool TryGetValue(string key, out IArray value) #pragma warning disable CS8614
public bool TryGetValue(string key, out IArray? value)
#pragma warning restore CS8614
{ {
return parent.TryGetValue(key, out value, index); return parent.TryGetValue(key, out value, index);
} }

View File

@ -25,7 +25,8 @@ namespace MatFileHandler
throw new ArgumentException("The object provided is not a string."); throw new ArgumentException("The object provided is not a string.");
} }
var binaryData = matObject["any", 0] as IArrayOf<ulong>; var binaryData = matObject["any", 0] as IArrayOf<ulong>
?? throw new HandlerException("Cannot extract string data.");
(dimensions, strings) = ParseBinaryData(binaryData.Data); (dimensions, strings) = ParseBinaryData(binaryData.Data);
} }

View File

@ -12,15 +12,15 @@ namespace MatFileHandler
/// </summary> /// </summary>
internal class SubsystemData internal class SubsystemData
{ {
private RealSubsystemData? _realData;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="SubsystemData"/> class. /// Initializes a new instance of the <see cref="SubsystemData"/> class.
/// Default constructor: initializes everything to null. /// Default constructor: initializes everything to null.
/// </summary> /// </summary>
public SubsystemData() public SubsystemData()
{ {
ClassInformation = null; _realData = null;
ObjectInformation = null;
Data = null;
} }
/// <summary> /// <summary>
@ -35,27 +35,29 @@ namespace MatFileHandler
Dictionary<int, ObjectInfo> objectInformation, Dictionary<int, ObjectInfo> objectInformation,
Dictionary<int, IArray> data) Dictionary<int, IArray> data)
{ {
this.ClassInformation = _realData = new RealSubsystemData(
classInformation ?? throw new ArgumentNullException(nameof(classInformation)); classInformation,
this.ObjectInformation = objectInformation,
objectInformation ?? throw new ArgumentNullException(nameof(objectInformation)); data);
this.Data = data ?? throw new ArgumentNullException(nameof(data));
} }
/// <summary> /// <summary>
/// Gets or sets information about all the classes occurring in the file. /// Gets information about all the classes occurring in the file.
/// </summary> /// </summary>
public Dictionary<int, ClassInfo> ClassInformation { get; set; } public Dictionary<int, ClassInfo> ClassInformation =>
_realData?.ClassInformation ?? throw new HandlerException("Subsystem data missing.");
/// <summary> /// <summary>
/// Gets or sets the actual data: mapping of "object field" indices to their values. /// Gets the actual data: mapping of "object field" indices to their values.
/// </summary> /// </summary>
public IReadOnlyDictionary<int, IArray> Data { get; set; } public IReadOnlyDictionary<int, IArray> Data =>
_realData?.Data ?? throw new HandlerException("Subsystem data missing.");
/// <summary> /// <summary>
/// Gets or sets information about all the objects occurring in the file. /// Gets information about all the objects occurring in the file.
/// </summary> /// </summary>
public Dictionary<int, ObjectInfo> ObjectInformation { get; set; } public Dictionary<int, ObjectInfo> ObjectInformation =>
_realData?.ObjectInformation ?? throw new HandlerException("Subsystem data missing.");
/// <summary> /// <summary>
/// Initialize this object from another object. /// Initialize this object from another object.
@ -66,9 +68,10 @@ namespace MatFileHandler
/// <param name="data">Another subsystem data.</param> /// <param name="data">Another subsystem data.</param>
public void Set(SubsystemData data) public void Set(SubsystemData data)
{ {
this.ClassInformation = data.ClassInformation; _realData = new RealSubsystemData(
this.ObjectInformation = data.ObjectInformation; data.ClassInformation,
this.Data = data.Data; data.ObjectInformation,
data.Data);
} }
/// <summary> /// <summary>
@ -136,5 +139,39 @@ namespace MatFileHandler
/// </summary> /// </summary>
public IReadOnlyDictionary<int, int> FieldLinks => fieldLinks; public IReadOnlyDictionary<int, int> FieldLinks => fieldLinks;
} }
private class RealSubsystemData
{
/// <summary>
/// Initializes a new instance of the <see cref="RealSubsystemData"/> class.
/// </summary>
/// <param name="classInformation">Class information.</param>
/// <param name="objectInformation">Object information.</param>
/// <param name="data">Data.</param>
public RealSubsystemData(
Dictionary<int, ClassInfo> classInformation,
Dictionary<int, ObjectInfo> objectInformation,
IReadOnlyDictionary<int, IArray> data)
{
ClassInformation = classInformation ?? throw new ArgumentNullException(nameof(classInformation));
ObjectInformation = objectInformation ?? throw new ArgumentNullException(nameof(objectInformation));
Data = data ?? throw new ArgumentNullException(nameof(data));
}
/// <summary>
/// Gets information about all the classes occurring in the file.
/// </summary>
public Dictionary<int, ClassInfo> ClassInformation { get; }
/// <summary>
/// Gets the actual data: mapping of "object field" indices to their values.
/// </summary>
public IReadOnlyDictionary<int, IArray> Data { get; }
/// <summary>
/// Gets information about all the objects occurring in the file.
/// </summary>
public Dictionary<int, ObjectInfo> ObjectInformation { get; }
}
} }
} }

View File

@ -23,65 +23,27 @@ namespace MatFileHandler
/// <returns>Subsystem data read.</returns> /// <returns>Subsystem data read.</returns>
public static SubsystemData Read(byte[] bytes, SubsystemData subsystemData) public static SubsystemData Read(byte[] bytes, SubsystemData subsystemData)
{ {
List<RawVariable> rawVariables = null; var rawVariables = ReadRawVariables(bytes, subsystemData);
using (var stream = new MemoryStream(bytes))
{
using (var reader = new BinaryReader(stream))
{
reader.ReadBytes(8);
rawVariables = MatFileReader.ReadRawVariables(reader, -1, subsystemData);
}
}
// Parse subsystem data. // Parse subsystem data.
var mainVariable = rawVariables[0].DataElement as IStructureArray; var mainVariable = rawVariables[0].DataElement as IStructureArray
var mcosData = mainVariable["MCOS", 0] as Opaque; ?? throw new HandlerException("Subsystem data must be a structure array.");
var opaqueData = mcosData.RawData as ICellArray; var mcosData = mainVariable["MCOS", 0] as Opaque
var info = (opaqueData[0] as IArrayOf<byte>).Data; ?? throw new HandlerException("MCOS data must be an opaque object.");
var opaqueData = mcosData.RawData as ICellArray
?? throw new HandlerException("Opaque data must be a cell array.");
var info = (opaqueData[0] as IArrayOf<byte>)?.Data
?? throw new HandlerException("Opaque data info must be a byte array.");
var (offsets, position) = ReadOffsets(info, 0); var (offsets, position) = ReadOffsets(info, 0);
var fieldNames = ReadFieldNames(info, position, offsets[1]); var fieldNames = ReadFieldNames(info, position, offsets[1]);
var numberOfClasses = ((offsets[3] - offsets[2]) / 16) - 1; var numberOfClasses = ((offsets[3] - offsets[2]) / 16) - 1;
Dictionary<int, string> classIdToName = null; var classIdToName = ReadClassIdToName(info, offsets, fieldNames, numberOfClasses);
using (var stream = new MemoryStream(info, offsets[2], offsets[3] - offsets[2]))
{
using (var reader = new BinaryReader(stream))
{
classIdToName = ReadClassNames(reader, fieldNames, numberOfClasses);
}
}
var numberOfEmbeddedObjects = (offsets[4] - offsets[3] - 8) / 16; var numberOfEmbeddedObjects = (offsets[4] - offsets[3] - 8) / 16;
Dictionary<int, Dictionary<int, int>> embeddedObjectPositionsToValues = null; var embeddedObjectPositionsToValues = ReadEmbeddedObjectPositionsToValues(info, offsets, numberOfEmbeddedObjects);
using (var stream = new MemoryStream(info, offsets[3], offsets[4] - offsets[3]))
{
using (var reader = new BinaryReader(stream))
{
embeddedObjectPositionsToValues =
ReadEmbeddedObjectPositionsToValuesMapping(reader, numberOfEmbeddedObjects);
}
}
var numberOfObjects = ((offsets[5] - offsets[4]) / 24) - 1; var numberOfObjects = ((offsets[5] - offsets[4]) / 24) - 1;
Dictionary<int, ObjectClassInformation> objectClasses = null; var objectClasses = ReadObjectClassInformations(info, offsets, numberOfObjects);
using (var stream = new MemoryStream(info, offsets[4], offsets[5] - offsets[4]))
{
using (var reader = new BinaryReader(stream))
{
objectClasses = ReadObjectClasses(reader, numberOfObjects);
}
}
var numberOfObjectPositions = objectClasses.Values.Count(x => x.ObjectPosition != 0); var numberOfObjectPositions = objectClasses.Values.Count(x => x.ObjectPosition != 0);
var objectPositionsToValues = ReadObjectPositionsToValues(info, offsets, numberOfObjectPositions);
Dictionary<int, Dictionary<int, int>> objectPositionsToValues = null;
using (var stream = new MemoryStream(info, offsets[5], offsets[6] - offsets[5]))
{
using (var reader = new BinaryReader(stream))
{
objectPositionsToValues = ReadObjectPositionsToValuesMapping(reader, numberOfObjectPositions);
}
}
var (classInformation, objectInformation) = var (classInformation, objectInformation) =
GatherClassAndObjectInformation( GatherClassAndObjectInformation(
classIdToName, classIdToName,
@ -89,7 +51,6 @@ namespace MatFileHandler
objectClasses, objectClasses,
objectPositionsToValues, objectPositionsToValues,
embeddedObjectPositionsToValues); embeddedObjectPositionsToValues);
var allFields = objectInformation.Values.SelectMany(obj => obj.FieldLinks.Values); var allFields = objectInformation.Values.SelectMany(obj => obj.FieldLinks.Values);
var data = new Dictionary<int, IArray>(); var data = new Dictionary<int, IArray>();
foreach (var i in allFields) foreach (var i in allFields)
@ -100,7 +61,44 @@ namespace MatFileHandler
return new SubsystemData(classInformation, objectInformation, data); return new SubsystemData(classInformation, objectInformation, data);
} }
private static (Dictionary<int, SubsystemData.ClassInfo>, Dictionary<int, SubsystemData.ObjectInfo>) private static Dictionary<int, Dictionary<int, int>> ReadObjectPositionsToValues(byte[] info, int[] offsets, int numberOfObjectPositions)
{
using var stream = new MemoryStream(info, offsets[5], offsets[6] - offsets[5]);
using var reader = new BinaryReader(stream);
return ReadObjectPositionsToValuesMapping(reader, numberOfObjectPositions);
}
private static Dictionary<int, ObjectClassInformation> ReadObjectClassInformations(byte[] info, int[] offsets, int numberOfObjects)
{
using var stream = new MemoryStream(info, offsets[4], offsets[5] - offsets[4]);
using var reader = new BinaryReader(stream);
return ReadObjectClasses(reader, numberOfObjects);
}
private static Dictionary<int, Dictionary<int, int>> ReadEmbeddedObjectPositionsToValues(byte[] info, int[] offsets, int numberOfEmbeddedObjects)
{
using var stream = new MemoryStream(info, offsets[3], offsets[4] - offsets[3]);
using var reader = new BinaryReader(stream);
return ReadEmbeddedObjectPositionsToValuesMapping(reader, numberOfEmbeddedObjects);
}
private static Dictionary<int, string> ReadClassIdToName(byte[] info, int[] offsets, string[] fieldNames, int numberOfClasses)
{
using var stream = new MemoryStream(info, offsets[2], offsets[3] - offsets[2]);
using var reader = new BinaryReader(stream);
return ReadClassNames(reader, fieldNames, numberOfClasses);
}
private static List<RawVariable> ReadRawVariables(byte[] bytes, SubsystemData subsystemData)
{
using var stream = new MemoryStream(bytes);
using var reader = new BinaryReader(stream);
reader.ReadBytes(8);
return MatFileReader.ReadRawVariables(reader, -1, subsystemData);
}
private static (Dictionary<int, SubsystemData.ClassInfo> classInfos,
Dictionary<int, SubsystemData.ObjectInfo> objectInfos)
GatherClassAndObjectInformation( GatherClassAndObjectInformation(
Dictionary<int, string> classIdToName, Dictionary<int, string> classIdToName,
string[] fieldNames, string[] fieldNames,
@ -318,7 +316,7 @@ namespace MatFileHandler
string.Empty, string.Empty,
string.Empty, string.Empty,
dimensions, dimensions,
array as DataElement, uintArray,
indexToObjectId, indexToObjectId,
classIndex, classIndex,
subsystemData); subsystemData);

View File

@ -18,25 +18,34 @@ namespace MatFileHandler
/// <param name="array">Source table object.</param> /// <param name="array">Source table object.</param>
public TableAdapter(IArray array) public TableAdapter(IArray array)
{ {
matObject = array as IMatObject; matObject = array as IMatObject
if (matObject?.ClassName != "table") ?? throw new HandlerException("Table adapter must be initialized with a MATLAB object.");
if (matObject.ClassName != "table")
{ {
throw new ArgumentException("The object provided is not a table."); throw new ArgumentException("The object provided is not a table.");
} }
var cellArray = matObject["varnames"] as ICellArray; var cellArray = matObject["varnames"] as ICellArray
?? throw new HandlerException("Table variable names must be in a cell array.");
VariableNames = Enumerable VariableNames = Enumerable
.Range(0, cellArray.Count) .Range(0, cellArray.Count)
.Select(i => (cellArray[i] as ICharArray).String) .Select(i =>
(cellArray[i] as ICharArray ??
throw new HandlerException("Variable name must be a char array.")).String)
.ToArray(); .ToArray();
NumberOfVariables = VariableNames.Length; NumberOfVariables = VariableNames.Length;
var props = matObject["props"] as IStructureArray; var props = matObject["props"] as IStructureArray
Description = (props["Description"] as ICharArray).String; ?? throw new HandlerException("Table properties must be a structure array.");
NumberOfRows = (int)matObject["nrows"].ConvertToDoubleArray()[0]; Description = (props["Description"] as ICharArray
var rowNamesArrays = matObject["rownames"] as ICellArray; ?? throw new HandlerException("Table description must be a char array.")).String;
NumberOfRows = (int)(matObject["nrows"].ConvertToDoubleArray()
?? throw new HandlerException("Cannot find number of rows in a table."))[0];
var rowNamesArrays = matObject["rownames"] as ICellArray
?? throw new HandlerException("Table row names must be a cell array.");
RowNames = Enumerable RowNames = Enumerable
.Range(0, rowNamesArrays.Count) .Range(0, rowNamesArrays.Count)
.Select(i => (cellArray[i] as ICharArray).String) .Select(i => (cellArray[i] as ICharArray
?? throw new HandlerException("Each table row name must be a char array.")).String)
.ToArray(); .ToArray();
} }
@ -66,7 +75,7 @@ namespace MatFileHandler
public string[] VariableNames { get; } public string[] VariableNames { get; }
/// <summary> /// <summary>
/// Gets all the data for a given variable /// Gets all the data for a given variable.
/// </summary> /// </summary>
/// <param name="variableName">Variable name.</param> /// <param name="variableName">Variable name.</param>
/// <returns>All data associated with the variable.</returns> /// <returns>All data associated with the variable.</returns>
@ -83,7 +92,8 @@ namespace MatFileHandler
throw new IndexOutOfRangeException($"Variable '{variableName}' not found."); throw new IndexOutOfRangeException($"Variable '{variableName}' not found.");
} }
var data = matObject["data"] as ICellArray; var data = matObject["data"] as ICellArray
?? throw new HandlerException("Table data must be stored in a cell array.");
return data[index]; return data[index];
} }
} }

View File

@ -4,12 +4,17 @@
# https://docs.microsoft.com/azure/devops/pipelines/languages/dotnet-core # https://docs.microsoft.com/azure/devops/pipelines/languages/dotnet-core
pool: pool:
vmImage: 'vs2017-win2016' vmImage: 'windows-latest'
variables: variables:
buildConfiguration: 'Release' buildConfiguration: 'Release'
steps: steps:
- task: UseDotNet@2
displayName: 'Use .NET Core SDK 3.x'
inputs:
version: 3.x
- script: dotnet build --configuration $(buildConfiguration) - script: dotnet build --configuration $(buildConfiguration)
displayName: 'dotnet build $(buildConfiguration)' displayName: 'dotnet build $(buildConfiguration)'