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">
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
<TargetFramework>netcoreapp3.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">

View File

@ -33,8 +33,8 @@ namespace MatFileHandler
DataElement data,
DataElement imaginaryData)
{
var realParts = DataExtraction.GetDataAsDouble(data).ToArrayLazily();
var imaginaryParts = DataExtraction.GetDataAsDouble(imaginaryData).ToArrayLazily();
var realParts = DataExtraction.GetDataAsDouble(data);
var imaginaryParts = DataExtraction.GetDataAsDouble(imaginaryData);
if (realParts == null)
{
throw new HandlerException("Couldn't read sparse array.");
@ -106,12 +106,12 @@ namespace MatFileHandler
int[] dimensions,
string name,
DataElement realData,
DataElement imaginaryData)
DataElement? imaginaryData)
where T : struct
{
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);
}
switch (flags.Class)
@ -139,6 +139,11 @@ namespace MatFileHandler
var dataArray = ConvertDataToProperType<T>(realData, flags.Class);
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);
if (flags.Class == ArrayType.MxDouble)
{
@ -163,49 +168,26 @@ namespace MatFileHandler
string name,
MiNum<byte> dataElement)
{
var data = dataElement?.Data;
var data = dataElement.Data;
return new MatCharArrayOf<byte>(flags, dimensions, name, data, Encoding.UTF8.GetString(data));
}
private static T[] ConvertDataToProperType<T>(DataElement data, ArrayType arrayType)
{
switch (arrayType)
{
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();
}
return TryConvertDataToProperType<T>(data, arrayType)
?? throw new HandlerException($"Unexpected data type.");
}
private static T[] ConvertDataToSparseProperType<T>(DataElement data, bool isLogical)
private static T[]? ConvertDataToSparseProperType<T>(DataElement data, bool 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)
{
case MiNum<double> _:
return DataExtraction.GetDataAsDouble(data).ToArrayLazily() as T[];
return DataExtraction.GetDataAsDouble(data) as T[];
default:
throw new NotSupportedException();
}
@ -217,7 +199,7 @@ namespace MatFileHandler
string name,
MiNum<ushort> dataElement)
{
var data = dataElement?.Data;
var data = dataElement.Data;
return new MatCharArrayOf<ushort>(
flags,
dimensions,
@ -226,7 +208,7 @@ namespace MatFileHandler
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[] columnIndex,
Func<int, T> get)
@ -242,5 +224,23 @@ namespace MatFileHandler
}
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 typeHi = type >> 16;
@ -222,7 +222,8 @@ namespace MatFileHandler
var elements = new List<IArray>();
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);
}
@ -319,8 +320,9 @@ namespace MatFileHandler
string name,
int fieldNameLength)
{
var element = Read(reader);
var fieldNames = ReadFieldNames(element as MiNum<sbyte>, fieldNameLength);
var element = Read(reader) as MiNum<sbyte>
?? throw new HandlerException("Unable to parse structure field names.");
var fieldNames = ReadFieldNames(element, fieldNameLength);
var fields = new Dictionary<string, List<IArray>>();
foreach (var fieldName in fieldNames)
{
@ -332,7 +334,8 @@ namespace MatFileHandler
{
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);
}
}
@ -398,7 +401,7 @@ namespace MatFileHandler
var element4 = Read(reader);
var data = ReadData(element4);
DataElement imaginaryData = null;
DataElement? imaginaryData = null;
if (flags.Variable.HasFlag(Variable.IsComplex))
{
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);
var dataArray = matObject["data", 0] as IArrayOf<double>;
if (dataArray is null)
switch (matObject["data", 0])
{
var dataComplex = matObject["data", 0] as IArrayOf<Complex>;
var complexData = dataComplex.ConvertToComplexArray();
data = complexData.Select(c => c.Real).ToArray();
data2 = complexData.Select(c => c.Imaginary).ToArray();
dimensions = dataComplex.Dimensions;
}
else
{
data = dataArray.ConvertToDoubleArray();
data2 = new double[data.Length];
dimensions = dataArray.Dimensions;
case IArrayOf<double> dataArray:
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();
data2 = complexData.Select(c => c.Imaginary).ToArray();
dimensions = dataComplex.Dimensions;
break;
default:
throw new HandlerException("Datetime data not found.");
}
}

View File

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

View File

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

View File

@ -28,12 +28,12 @@ namespace MatFileHandler
/// 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>
double[] ConvertToDoubleArray();
double[]? ConvertToDoubleArray();
/// <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>
Complex[] ConvertToComplexArray();
Complex[]? ConvertToComplexArray();
}
}

View File

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

View File

@ -15,6 +15,6 @@ namespace MatFileHandler
/// <summary>
/// Gets a dictionary mapping indices to values.
/// </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 />
public virtual double[] ConvertToDoubleArray()
public virtual double[]? ConvertToDoubleArray()
{
return null;
}
/// <inheritdoc />
public virtual Complex[] ConvertToComplexArray()
public virtual Complex[]? ConvertToComplexArray()
{
return null;
}

View File

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

View File

@ -57,7 +57,8 @@ namespace MatFileHandler
var dataElement = dataElementReader.Read(reader);
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);
subsystemData.Set(newSubsystemData);
}

View File

@ -198,14 +198,14 @@ namespace MatFileHandler
return buffer;
}
private (byte[], byte[]) ConvertToPairOfByteArrays<T>(ComplexOf<T>[] data)
private (byte[] real, byte[] imaginary) ConvertToPairOfByteArrays<T>(ComplexOf<T>[] data)
where T : struct
{
return (ConvertToByteArray(data.Select(x => x.Real).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()),
ConvertToByteArray(data.Select(x => x.Imaginary).ToArray()));
@ -496,8 +496,8 @@ namespace MatFileHandler
for (var column = 0; column < numberOfColumns; column++)
{
var column1 = column;
var thisColumn = keys.Where(pair => pair.Item2 == column1 && !dict[pair].Equals(default(T)));
var thisRow = thisColumn.Select(pair => pair.Item1).OrderBy(x => x).ToArray();
var thisColumn = keys.Where(pair => pair.column == column1 && !dict[pair].Equals(default));
var thisRow = thisColumn.Select(pair => pair.row).OrderBy(x => x).ToArray();
rowIndexList.AddRange(thisRow);
valuesList.AddRange(thisRow.Select(row => dict[(row, column1)]));
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>
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>
/// 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()
public override Complex[]? ConvertToComplexArray()
{
if (Data is Complex[])
return Data switch
{
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();
Complex[] data => data,
ComplexOf<sbyte>[] ofs => ConvertToComplex(ofs),
ComplexOf<byte>[] ofs => ConvertToComplex(ofs),
ComplexOf<short>[] ofs => ConvertToComplex(ofs),
ComplexOf<ushort>[] ofs => ConvertToComplex(ofs),
ComplexOf<int>[] ofs => ConvertToComplex(ofs),
ComplexOf<uint>[] ofs => ConvertToComplex(ofs),
ComplexOf<long>[] ofs => ConvertToComplex(ofs),
ComplexOf<ulong>[] ofs => ConvertToComplex(ofs),
ComplexOf<float>[] ofs => ConvertToComplex(ofs),
_ => ConvertToComplex(ConvertToDoubleArray())
};
}
private static Complex[] ConvertToComplex<TS>(IEnumerable<ComplexOf<TS>> array)
where TS : struct
private static Complex[] ConvertToComplex(ComplexOf<sbyte>[] array)
{
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,
int[] dimensions,
string name,
Dictionary<(int, int), T> data)
Dictionary<(int row, int column), T> data)
: base(flags.ArrayFlags, dimensions, name)
{
DataDictionary = data;
@ -39,9 +39,9 @@ namespace MatFileHandler
.ToArray();
/// <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 />
public T this[params int[] list]

View File

@ -35,7 +35,19 @@ namespace MatFileHandler
/// <summary>
/// Gets null: not implemented.
/// </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>
/// Gets a dictionary that maps field names to lists of values.
@ -128,7 +140,7 @@ namespace MatFileHandler
/// <summary>
/// Checks if the structure has a given field.
/// </summary>
/// <param name="key">Field name</param>
/// <param name="key">Field name.</param>
/// <returns>True iff the structure has a given field.</returns>
public bool ContainsKey(string key) => Parent.Fields.ContainsKey(key);
@ -138,12 +150,14 @@ namespace MatFileHandler
/// <param name="key">Field name.</param>
/// <param name="value">Value (or null if the field is not present).</param>
/// <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);
if (!success)
{
value = default(IArray);
value = default;
return false;
}
value = array[Index];

View File

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

View File

@ -47,7 +47,21 @@ namespace MatFileHandler
public int ClassIndex { get; }
/// <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 />
public IEnumerable<string> FieldNames => FieldNamesArray;
@ -71,7 +85,7 @@ namespace MatFileHandler
{
if (TryGetValue(field, out var result, list))
{
return result;
return result!;
}
throw new IndexOutOfRangeException();
@ -91,19 +105,19 @@ namespace MatFileHandler
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 maybeFieldIndex = subsystemData.ClassInformation[ClassIndex].FindField(field);
if (!(maybeFieldIndex is int fieldIndex))
{
output = default(IArray);
output = default;
return false;
}
if (index >= IndexToObjectId.Length)
{
output = default(IArray);
output = default;
return false;
}
@ -161,7 +175,9 @@ namespace MatFileHandler
}
/// <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);
}

View File

@ -25,7 +25,8 @@ namespace MatFileHandler
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);
}

View File

@ -12,15 +12,15 @@ namespace MatFileHandler
/// </summary>
internal class SubsystemData
{
private RealSubsystemData? _realData;
/// <summary>
/// Initializes a new instance of the <see cref="SubsystemData"/> class.
/// Default constructor: initializes everything to null.
/// </summary>
public SubsystemData()
{
ClassInformation = null;
ObjectInformation = null;
Data = null;
_realData = null;
}
/// <summary>
@ -35,27 +35,29 @@ namespace MatFileHandler
Dictionary<int, ObjectInfo> objectInformation,
Dictionary<int, IArray> data)
{
this.ClassInformation =
classInformation ?? throw new ArgumentNullException(nameof(classInformation));
this.ObjectInformation =
objectInformation ?? throw new ArgumentNullException(nameof(objectInformation));
this.Data = data ?? throw new ArgumentNullException(nameof(data));
_realData = new RealSubsystemData(
classInformation,
objectInformation,
data);
}
/// <summary>
/// Gets or sets information about all the classes occurring in the file.
/// Gets information about all the classes occurring in the file.
/// </summary>
public Dictionary<int, ClassInfo> ClassInformation { get; set; }
public Dictionary<int, ClassInfo> ClassInformation =>
_realData?.ClassInformation ?? throw new HandlerException("Subsystem data missing.");
/// <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>
public IReadOnlyDictionary<int, IArray> Data { get; set; }
public IReadOnlyDictionary<int, IArray> Data =>
_realData?.Data ?? throw new HandlerException("Subsystem data missing.");
/// <summary>
/// Gets or sets information about all the objects occurring in the file.
/// Gets information about all the objects occurring in the file.
/// </summary>
public Dictionary<int, ObjectInfo> ObjectInformation { get; set; }
public Dictionary<int, ObjectInfo> ObjectInformation =>
_realData?.ObjectInformation ?? throw new HandlerException("Subsystem data missing.");
/// <summary>
/// Initialize this object from another object.
@ -66,9 +68,10 @@ namespace MatFileHandler
/// <param name="data">Another subsystem data.</param>
public void Set(SubsystemData data)
{
this.ClassInformation = data.ClassInformation;
this.ObjectInformation = data.ObjectInformation;
this.Data = data.Data;
_realData = new RealSubsystemData(
data.ClassInformation,
data.ObjectInformation,
data.Data);
}
/// <summary>
@ -136,5 +139,39 @@ namespace MatFileHandler
/// </summary>
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>
public static SubsystemData Read(byte[] bytes, SubsystemData subsystemData)
{
List<RawVariable> rawVariables = null;
using (var stream = new MemoryStream(bytes))
{
using (var reader = new BinaryReader(stream))
{
reader.ReadBytes(8);
rawVariables = MatFileReader.ReadRawVariables(reader, -1, subsystemData);
}
}
var rawVariables = ReadRawVariables(bytes, subsystemData);
// Parse subsystem data.
var mainVariable = rawVariables[0].DataElement as IStructureArray;
var mcosData = mainVariable["MCOS", 0] as Opaque;
var opaqueData = mcosData.RawData as ICellArray;
var info = (opaqueData[0] as IArrayOf<byte>).Data;
var mainVariable = rawVariables[0].DataElement as IStructureArray
?? throw new HandlerException("Subsystem data must be a structure array.");
var mcosData = mainVariable["MCOS", 0] as Opaque
?? 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 fieldNames = ReadFieldNames(info, position, offsets[1]);
var numberOfClasses = ((offsets[3] - offsets[2]) / 16) - 1;
Dictionary<int, string> classIdToName = null;
using (var stream = new MemoryStream(info, offsets[2], offsets[3] - offsets[2]))
{
using (var reader = new BinaryReader(stream))
{
classIdToName = ReadClassNames(reader, fieldNames, numberOfClasses);
}
}
var classIdToName = ReadClassIdToName(info, offsets, fieldNames, numberOfClasses);
var numberOfEmbeddedObjects = (offsets[4] - offsets[3] - 8) / 16;
Dictionary<int, Dictionary<int, int>> embeddedObjectPositionsToValues = null;
using (var stream = new MemoryStream(info, offsets[3], offsets[4] - offsets[3]))
{
using (var reader = new BinaryReader(stream))
{
embeddedObjectPositionsToValues =
ReadEmbeddedObjectPositionsToValuesMapping(reader, numberOfEmbeddedObjects);
}
}
var embeddedObjectPositionsToValues = ReadEmbeddedObjectPositionsToValues(info, offsets, numberOfEmbeddedObjects);
var numberOfObjects = ((offsets[5] - offsets[4]) / 24) - 1;
Dictionary<int, ObjectClassInformation> objectClasses = null;
using (var stream = new MemoryStream(info, offsets[4], offsets[5] - offsets[4]))
{
using (var reader = new BinaryReader(stream))
{
objectClasses = ReadObjectClasses(reader, numberOfObjects);
}
}
var objectClasses = ReadObjectClassInformations(info, offsets, numberOfObjects);
var numberOfObjectPositions = objectClasses.Values.Count(x => x.ObjectPosition != 0);
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 objectPositionsToValues = ReadObjectPositionsToValues(info, offsets, numberOfObjectPositions);
var (classInformation, objectInformation) =
GatherClassAndObjectInformation(
classIdToName,
@ -89,7 +51,6 @@ namespace MatFileHandler
objectClasses,
objectPositionsToValues,
embeddedObjectPositionsToValues);
var allFields = objectInformation.Values.SelectMany(obj => obj.FieldLinks.Values);
var data = new Dictionary<int, IArray>();
foreach (var i in allFields)
@ -100,7 +61,44 @@ namespace MatFileHandler
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(
Dictionary<int, string> classIdToName,
string[] fieldNames,
@ -318,7 +316,7 @@ namespace MatFileHandler
string.Empty,
string.Empty,
dimensions,
array as DataElement,
uintArray,
indexToObjectId,
classIndex,
subsystemData);

View File

@ -18,25 +18,34 @@ namespace MatFileHandler
/// <param name="array">Source table object.</param>
public TableAdapter(IArray array)
{
matObject = array as IMatObject;
if (matObject?.ClassName != "table")
matObject = array as IMatObject
?? 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.");
}
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
.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();
NumberOfVariables = VariableNames.Length;
var props = matObject["props"] as IStructureArray;
Description = (props["Description"] as ICharArray).String;
NumberOfRows = (int)matObject["nrows"].ConvertToDoubleArray()[0];
var rowNamesArrays = matObject["rownames"] as ICellArray;
var props = matObject["props"] as IStructureArray
?? throw new HandlerException("Table properties must be a structure array.");
Description = (props["Description"] as ICharArray
?? 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
.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();
}
@ -66,7 +75,7 @@ namespace MatFileHandler
public string[] VariableNames { get; }
/// <summary>
/// Gets all the data for a given variable
/// Gets all the data for a given variable.
/// </summary>
/// <param name="variableName">Variable name.</param>
/// <returns>All data associated with the variable.</returns>
@ -83,7 +92,8 @@ namespace MatFileHandler
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];
}
}

View File

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