diff --git a/MatFileHandler/DataElementConverter.cs b/MatFileHandler/DataElementConverter.cs index 3a52952..0aceeb6 100755 --- a/MatFileHandler/DataElementConverter.cs +++ b/MatFileHandler/DataElementConverter.cs @@ -106,7 +106,7 @@ namespace MatFileHandler int[] dimensions, string name, DataElement realData, - DataElement imaginaryData) + DataElement? imaginaryData) where T : struct { if (flags.Variable.HasFlag(Variable.IsLogical)) @@ -139,6 +139,11 @@ namespace MatFileHandler var dataArray = ConvertDataToProperType(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(imaginaryData, flags.Class); if (flags.Class == ArrayType.MxDouble) { @@ -169,34 +174,11 @@ namespace MatFileHandler private static T[] ConvertDataToProperType(DataElement data, ArrayType arrayType) { - switch (arrayType) - { - case ArrayType.MxDouble: - return DataExtraction.GetDataAsDouble(data) as T[]; - case ArrayType.MxSingle: - return DataExtraction.GetDataAsSingle(data) as T[]; - case ArrayType.MxInt8: - return DataExtraction.GetDataAsInt8(data) as T[]; - case ArrayType.MxUInt8: - return DataExtraction.GetDataAsUInt8(data) as T[]; - case ArrayType.MxInt16: - return DataExtraction.GetDataAsInt16(data) as T[]; - case ArrayType.MxUInt16: - return DataExtraction.GetDataAsUInt16(data) as T[]; - case ArrayType.MxInt32: - return DataExtraction.GetDataAsInt32(data) as T[]; - case ArrayType.MxUInt32: - return DataExtraction.GetDataAsUInt32(data) as T[]; - case ArrayType.MxInt64: - return DataExtraction.GetDataAsInt64(data) as T[]; - case ArrayType.MxUInt64: - return DataExtraction.GetDataAsUInt64(data) as T[]; - default: - throw new NotSupportedException(); - } + return TryConvertDataToProperType(data, arrayType) + ?? throw new HandlerException($"Unexpected data type."); } - private static T[] ConvertDataToSparseProperType(DataElement data, bool isLogical) + private static T[]? ConvertDataToSparseProperType(DataElement data, bool isLogical) { if (isLogical) { @@ -217,7 +199,7 @@ namespace MatFileHandler string name, MiNum dataElement) { - var data = dataElement?.Data; + var data = dataElement.Data; return new MatCharArrayOf( flags, dimensions, @@ -226,7 +208,7 @@ namespace MatFileHandler new string(data.Select(x => (char)x).ToArray())); } - private static Dictionary<(int, int), T> ConvertMatlabSparseToDictionary( + private static Dictionary<(int row, int column), T> ConvertMatlabSparseToDictionary( int[] rowIndex, int[] columnIndex, Func get) @@ -242,5 +224,23 @@ namespace MatFileHandler } return result; } + + private static T[]? TryConvertDataToProperType(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() + }; + } } } \ No newline at end of file diff --git a/MatFileHandler/DataElementReader.cs b/MatFileHandler/DataElementReader.cs index f01af12..b678566 100755 --- a/MatFileHandler/DataElementReader.cs +++ b/MatFileHandler/DataElementReader.cs @@ -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(); 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, fieldNameLength); + var element = Read(reader) as MiNum + ?? throw new HandlerException("Unable to parse structure field names."); + var fieldNames = ReadFieldNames(element, fieldNameLength); var fields = new Dictionary>(); 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); diff --git a/MatFileHandler/DataExtraction.cs b/MatFileHandler/DataExtraction.cs index a975acc..455fd92 100755 --- a/MatFileHandler/DataExtraction.cs +++ b/MatFileHandler/DataExtraction.cs @@ -350,7 +350,13 @@ namespace MatFileHandler } // * to double - private static double[] SbyteToDouble(sbyte[] source) + + /// + /// Convert an array of signed bytes to an array of doubles. + /// + /// Source array. + /// Converted array. + public static double[] SbyteToDouble(sbyte[] source) { var result = new double[source.Length]; for (var i = 0; i < source.Length; i++) @@ -361,7 +367,12 @@ namespace MatFileHandler return result; } - private static double[] ByteToDouble(byte[] source) + /// + /// Convert an array of bytes to an array of doubles. + /// + /// Source array. + /// Converted array. + public static double[] ByteToDouble(byte[] source) { var result = new double[source.Length]; for (var i = 0; i < source.Length; i++) @@ -372,7 +383,12 @@ namespace MatFileHandler return result; } - private static double[] ShortToDouble(short[] source) + /// + /// Convert an array of shorts to an array of doubles. + /// + /// Source array. + /// Converted array. + public static double[] ShortToDouble(short[] source) { var result = new double[source.Length]; for (var i = 0; i < source.Length; i++) @@ -383,7 +399,12 @@ namespace MatFileHandler return result; } - private static double[] UshortToDouble(ushort[] source) + /// + /// Convert an array of unsigned shorts to an array of doubles. + /// + /// Source array. + /// Converted array. + public static double[] UshortToDouble(ushort[] source) { var result = new double[source.Length]; for (var i = 0; i < source.Length; i++) @@ -394,7 +415,12 @@ namespace MatFileHandler return result; } - private static double[] IntToDouble(int[] source) + /// + /// Convert an array of integers to an array of doubles. + /// + /// Source array. + /// Converted array. + public static double[] IntToDouble(int[] source) { var result = new double[source.Length]; for (var i = 0; i < source.Length; i++) @@ -405,7 +431,12 @@ namespace MatFileHandler return result; } - private static double[] UintToDouble(uint[] source) + /// + /// Convert an array of unsigned integers to an array of doubles. + /// + /// Source array. + /// Converted array. + public static double[] UintToDouble(uint[] source) { var result = new double[source.Length]; for (var i = 0; i < source.Length; i++) @@ -416,7 +447,12 @@ namespace MatFileHandler return result; } - private static double[] LongToDouble(long[] source) + /// + /// Convert an array of longs to an array of doubles. + /// + /// Source array. + /// Converted array. + public static double[] LongToDouble(long[] source) { var result = new double[source.Length]; for (var i = 0; i < source.Length; i++) @@ -427,7 +463,12 @@ namespace MatFileHandler return result; } - private static double[] UlongToDouble(ulong[] source) + /// + /// Convert an array of unsigned longs to an array of doubles. + /// + /// Source array. + /// Converted array. + public static double[] UlongToDouble(ulong[] source) { var result = new double[source.Length]; for (var i = 0; i < source.Length; i++) @@ -438,7 +479,12 @@ namespace MatFileHandler return result; } - private static double[] FloatToDouble(float[] source) + /// + /// Convert an array of floats to an array of doubles. + /// + /// Source array. + /// Converted array. + public static double[] FloatToDouble(float[] source) { var result = new double[source.Length]; for (var i = 0; i < source.Length; i++) diff --git a/MatFileHandler/DatetimeAdapter.cs b/MatFileHandler/DatetimeAdapter.cs index bdac59f..5f0f604 100644 --- a/MatFileHandler/DatetimeAdapter.cs +++ b/MatFileHandler/DatetimeAdapter.cs @@ -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; - if (dataArray is null) + + switch (matObject["data", 0]) { - var dataComplex = matObject["data", 0] as IArrayOf; - 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 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 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."); } } diff --git a/MatFileHandler/DurationAdapter.cs b/MatFileHandler/DurationAdapter.cs index c7a6a35..aad282e 100644 --- a/MatFileHandler/DurationAdapter.cs +++ b/MatFileHandler/DurationAdapter.cs @@ -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; } diff --git a/MatFileHandler/HandlerException.cs b/MatFileHandler/HandlerException.cs index 38ec92b..fbc6c96 100755 --- a/MatFileHandler/HandlerException.cs +++ b/MatFileHandler/HandlerException.cs @@ -5,7 +5,7 @@ using System; namespace MatFileHandler { /// - /// Exception related to Matlab data handling + /// Exception related to Matlab data handling. /// public class HandlerException : Exception { @@ -14,7 +14,7 @@ namespace MatFileHandler /// /// Error message. /// Inner exception. - public HandlerException(string message, Exception innerException = null) + public HandlerException(string message, Exception? innerException = null) : base(message, innerException) { } diff --git a/MatFileHandler/IArray.cs b/MatFileHandler/IArray.cs index adda6fb..a1e88b2 100755 --- a/MatFileHandler/IArray.cs +++ b/MatFileHandler/IArray.cs @@ -28,12 +28,12 @@ namespace MatFileHandler /// Tries to convert the array to an array of Double values. /// /// Array of values of the array, converted to Double, or null if the conversion is not possible. - double[] ConvertToDoubleArray(); + double[]? ConvertToDoubleArray(); /// /// Tries to convert the array to an array of Complex values. /// /// Array of values of the array, converted to Complex, or null if the conversion is not possible. - Complex[] ConvertToComplexArray(); + Complex[]? ConvertToComplexArray(); } } \ No newline at end of file diff --git a/MatFileHandler/IArrayOf.cs b/MatFileHandler/IArrayOf.cs index 49f4054..22a959a 100755 --- a/MatFileHandler/IArrayOf.cs +++ b/MatFileHandler/IArrayOf.cs @@ -21,7 +21,7 @@ namespace MatFileHandler /// * for cell arrays: /// IArray; /// * for structure arrays: - /// IReadOnlyDictionary<string, IArray>; + /// IReadOnlyDictionary<string, IArray>. /// public interface IArrayOf : IArray { diff --git a/MatFileHandler/ISparseArrayOf.cs b/MatFileHandler/ISparseArrayOf.cs index 74c77ae..83229d2 100755 --- a/MatFileHandler/ISparseArrayOf.cs +++ b/MatFileHandler/ISparseArrayOf.cs @@ -15,6 +15,6 @@ namespace MatFileHandler /// /// Gets a dictionary mapping indices to values. /// - new IReadOnlyDictionary<(int, int), T> Data { get; } + new IReadOnlyDictionary<(int row, int column), T> Data { get; } } } \ No newline at end of file diff --git a/MatFileHandler/MatArray.cs b/MatFileHandler/MatArray.cs index b35fa50..ecf0e1e 100755 --- a/MatFileHandler/MatArray.cs +++ b/MatFileHandler/MatArray.cs @@ -54,13 +54,13 @@ namespace MatFileHandler } /// - public virtual double[] ConvertToDoubleArray() + public virtual double[]? ConvertToDoubleArray() { return null; } /// - public virtual Complex[] ConvertToComplexArray() + public virtual Complex[]? ConvertToComplexArray() { return null; } diff --git a/MatFileHandler/MatFileReader.cs b/MatFileHandler/MatFileReader.cs index 0050272..a4e3ae6 100755 --- a/MatFileHandler/MatFileReader.cs +++ b/MatFileHandler/MatFileReader.cs @@ -57,7 +57,8 @@ namespace MatFileHandler var dataElement = dataElementReader.Read(reader); if (position == subsystemDataOffset) { - var subsystemDataElement = dataElement as IArrayOf; + var subsystemDataElement = dataElement as IArrayOf + ?? throw new HandlerException("Cannot parse subsystem data element."); var newSubsystemData = ReadSubsystemData(subsystemDataElement.Data, subsystemData); subsystemData.Set(newSubsystemData); } diff --git a/MatFileHandler/MatFileWriter.cs b/MatFileHandler/MatFileWriter.cs index 414ab39..4f9a932 100755 --- a/MatFileHandler/MatFileWriter.cs +++ b/MatFileHandler/MatFileWriter.cs @@ -198,14 +198,14 @@ namespace MatFileHandler return buffer; } - private (byte[], byte[]) ConvertToPairOfByteArrays(ComplexOf[] data) + private (byte[] real, byte[] imaginary) ConvertToPairOfByteArrays(ComplexOf[] 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; diff --git a/MatFileHandler/MatNumericalArrayOf.cs b/MatFileHandler/MatNumericalArrayOf.cs index 80bde49..0fce0c3 100755 --- a/MatFileHandler/MatNumericalArrayOf.cs +++ b/MatFileHandler/MatNumericalArrayOf.cs @@ -43,58 +43,152 @@ namespace MatFileHandler /// Array of values of the array, converted to Double, or null if the conversion is not possible. public override double[] ConvertToDoubleArray() { - return Data as double[] ?? Data.Select(x => Convert.ToDouble(x)).ToArray(); + 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.") + }; } /// /// Tries to convert the array to an array of Complex values. /// /// Array of values of the array, converted to Complex, or null if the conversion is not possible. - public override Complex[] ConvertToComplexArray() + public override Complex[]? ConvertToComplexArray() { - if (Data is Complex[]) + return Data switch { - return Data as Complex[]; - } - if (Data is ComplexOf[]) - { - return ConvertToComplex(Data as ComplexOf[]); - } - if (Data is ComplexOf[]) - { - return ConvertToComplex(Data as ComplexOf[]); - } - if (Data is ComplexOf[]) - { - return ConvertToComplex(Data as ComplexOf[]); - } - if (Data is ComplexOf[]) - { - return ConvertToComplex(Data as ComplexOf[]); - } - if (Data is ComplexOf[]) - { - return ConvertToComplex(Data as ComplexOf[]); - } - if (Data is ComplexOf[]) - { - return ConvertToComplex(Data as ComplexOf[]); - } - if (Data is ComplexOf[]) - { - return ConvertToComplex(Data as ComplexOf[]); - } - if (Data is ComplexOf[]) - { - return ConvertToComplex(Data as ComplexOf[]); - } - return ConvertToDoubleArray().Select(x => new Complex(x, 0.0)).ToArray(); + Complex[] data => data, + ComplexOf[] ofs => ConvertToComplex(ofs), + ComplexOf[] ofs => ConvertToComplex(ofs), + ComplexOf[] ofs => ConvertToComplex(ofs), + ComplexOf[] ofs => ConvertToComplex(ofs), + ComplexOf[] ofs => ConvertToComplex(ofs), + ComplexOf[] ofs => ConvertToComplex(ofs), + ComplexOf[] ofs => ConvertToComplex(ofs), + ComplexOf[] ofs => ConvertToComplex(ofs), + ComplexOf[] ofs => ConvertToComplex(ofs), + _ => ConvertToComplex(ConvertToDoubleArray()) + }; } - private static Complex[] ConvertToComplex(IEnumerable> array) - where TS : struct + private static Complex[] ConvertToComplex(ComplexOf[] 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[] 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[] 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[] 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[] 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[] 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[] 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[] 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[] 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; } } } \ No newline at end of file diff --git a/MatFileHandler/MatSparseArrayOf.cs b/MatFileHandler/MatSparseArrayOf.cs index db9fd5d..8c60e4e 100755 --- a/MatFileHandler/MatSparseArrayOf.cs +++ b/MatFileHandler/MatSparseArrayOf.cs @@ -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(); /// - 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; } /// public T this[params int[] list] diff --git a/MatFileHandler/MatStructureArray.cs b/MatFileHandler/MatStructureArray.cs index 9422d06..9a91530 100755 --- a/MatFileHandler/MatStructureArray.cs +++ b/MatFileHandler/MatStructureArray.cs @@ -35,7 +35,19 @@ namespace MatFileHandler /// /// Gets null: not implemented. /// - public IReadOnlyDictionary[] Data => null; + public IReadOnlyDictionary[] Data + { + get + { + var result = new IReadOnlyDictionary[Count]; + for (var i = 0; i < Count; i++) + { + result[i] = FieldNames.ToDictionary(name => name, name => Fields[name][i]); + } + + return result; + } + } /// /// Gets a dictionary that maps field names to lists of values. @@ -128,7 +140,7 @@ namespace MatFileHandler /// /// Checks if the structure has a given field. /// - /// Field name + /// Field name. /// True iff the structure has a given field. public bool ContainsKey(string key) => Parent.Fields.ContainsKey(key); @@ -138,12 +150,14 @@ namespace MatFileHandler /// Field name. /// Value (or null if the field is not present). /// Success status of the query. - 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]; diff --git a/MatFileHandler/Opaque.cs b/MatFileHandler/Opaque.cs index b574d22..9ce69b7 100644 --- a/MatFileHandler/Opaque.cs +++ b/MatFileHandler/Opaque.cs @@ -50,9 +50,9 @@ namespace MatFileHandler public string TypeDescription { get; } /// - public override Complex[] ConvertToComplexArray() => null; + public override Complex[]? ConvertToComplexArray() => null; /// - public override double[] ConvertToDoubleArray() => null; + public override double[]? ConvertToDoubleArray() => null; } } \ No newline at end of file diff --git a/MatFileHandler/OpaqueLink.cs b/MatFileHandler/OpaqueLink.cs index ed71d2f..563e96e 100644 --- a/MatFileHandler/OpaqueLink.cs +++ b/MatFileHandler/OpaqueLink.cs @@ -47,7 +47,21 @@ namespace MatFileHandler public int ClassIndex { get; } /// - public IReadOnlyDictionary[] Data => null; + public IReadOnlyDictionary[] Data + { + get + { + var result = new IReadOnlyDictionary[Count]; + for (var i = 0; i < Count; i++) + { + result[i] = FieldNamesArray.ToDictionary( + name => name, + name => this[name, i]); + } + + return result; + } + } /// public IEnumerable 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 } /// - 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); } diff --git a/MatFileHandler/StringAdapter.cs b/MatFileHandler/StringAdapter.cs index afc5f80..8815d1c 100644 --- a/MatFileHandler/StringAdapter.cs +++ b/MatFileHandler/StringAdapter.cs @@ -25,7 +25,8 @@ namespace MatFileHandler throw new ArgumentException("The object provided is not a string."); } - var binaryData = matObject["any", 0] as IArrayOf; + var binaryData = matObject["any", 0] as IArrayOf + ?? throw new HandlerException("Cannot extract string data."); (dimensions, strings) = ParseBinaryData(binaryData.Data); } diff --git a/MatFileHandler/SubsystemData.cs b/MatFileHandler/SubsystemData.cs index fd987b8..f14ffae 100644 --- a/MatFileHandler/SubsystemData.cs +++ b/MatFileHandler/SubsystemData.cs @@ -12,15 +12,15 @@ namespace MatFileHandler /// internal class SubsystemData { + private RealSubsystemData? _realData; + /// /// Initializes a new instance of the class. /// Default constructor: initializes everything to null. /// public SubsystemData() { - ClassInformation = null; - ObjectInformation = null; - Data = null; + _realData = null; } /// @@ -35,27 +35,29 @@ namespace MatFileHandler Dictionary objectInformation, Dictionary 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); } /// - /// Gets or sets information about all the classes occurring in the file. + /// Gets information about all the classes occurring in the file. /// - public Dictionary ClassInformation { get; set; } + public Dictionary ClassInformation => + _realData?.ClassInformation ?? throw new HandlerException("Subsystem data missing."); /// - /// 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. /// - public IReadOnlyDictionary Data { get; set; } + public IReadOnlyDictionary Data => + _realData?.Data ?? throw new HandlerException("Subsystem data missing."); /// - /// Gets or sets information about all the objects occurring in the file. + /// Gets information about all the objects occurring in the file. /// - public Dictionary ObjectInformation { get; set; } + public Dictionary ObjectInformation => + _realData?.ObjectInformation ?? throw new HandlerException("Subsystem data missing."); /// /// Initialize this object from another object. @@ -66,9 +68,10 @@ namespace MatFileHandler /// Another subsystem data. 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); } /// @@ -136,5 +139,39 @@ namespace MatFileHandler /// public IReadOnlyDictionary FieldLinks => fieldLinks; } + + private class RealSubsystemData + { + /// + /// Initializes a new instance of the class. + /// + /// Class information. + /// Object information. + /// Data. + public RealSubsystemData( + Dictionary classInformation, + Dictionary objectInformation, + IReadOnlyDictionary data) + { + ClassInformation = classInformation ?? throw new ArgumentNullException(nameof(classInformation)); + ObjectInformation = objectInformation ?? throw new ArgumentNullException(nameof(objectInformation)); + Data = data ?? throw new ArgumentNullException(nameof(data)); + } + + /// + /// Gets information about all the classes occurring in the file. + /// + public Dictionary ClassInformation { get; } + + /// + /// Gets the actual data: mapping of "object field" indices to their values. + /// + public IReadOnlyDictionary Data { get; } + + /// + /// Gets information about all the objects occurring in the file. + /// + public Dictionary ObjectInformation { get; } + } } } \ No newline at end of file diff --git a/MatFileHandler/SubsystemDataReader.cs b/MatFileHandler/SubsystemDataReader.cs index 9b1b6fc..229bc82 100644 --- a/MatFileHandler/SubsystemDataReader.cs +++ b/MatFileHandler/SubsystemDataReader.cs @@ -23,65 +23,27 @@ namespace MatFileHandler /// Subsystem data read. public static SubsystemData Read(byte[] bytes, SubsystemData subsystemData) { - List 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).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)?.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 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> 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 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> 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(); foreach (var i in allFields) @@ -100,7 +61,44 @@ namespace MatFileHandler return new SubsystemData(classInformation, objectInformation, data); } - private static (Dictionary, Dictionary) + private static Dictionary> 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 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> 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 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 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 classInfos, + Dictionary objectInfos) GatherClassAndObjectInformation( Dictionary classIdToName, string[] fieldNames, @@ -318,7 +316,7 @@ namespace MatFileHandler string.Empty, string.Empty, dimensions, - array as DataElement, + uintArray, indexToObjectId, classIndex, subsystemData); diff --git a/MatFileHandler/TableAdapter.cs b/MatFileHandler/TableAdapter.cs index f6aa641..1c4d44d 100644 --- a/MatFileHandler/TableAdapter.cs +++ b/MatFileHandler/TableAdapter.cs @@ -18,25 +18,34 @@ namespace MatFileHandler /// Source table object. 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; } /// - /// Gets all the data for a given variable + /// Gets all the data for a given variable. /// /// Variable name. /// All data associated with the variable. @@ -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]; } }