From 5a2226629a45c9adef0fb6b83484a7b3c2eaf172 Mon Sep 17 00:00:00 2001 From: Alexander Luzgarev Date: Sat, 15 Mar 2025 09:04:47 +0100 Subject: [PATCH 1/3] Implement EnumAdapter #17 --- MatFileHandler.Tests/ArrayHandlingTests.cs | 11 +++- MatFileHandler.Tests/MatFileReaderTests.cs | 25 +++++++- MatFileHandler.Tests/test-data/good/enum.mat | Bin 0 -> 569 bytes MatFileHandler/DataElementReader.cs | 2 +- MatFileHandler/EnumAdapter.cs | 60 +++++++++++++++++++ MatFileHandler/Opaque.cs | 9 ++- MatFileHandler/OpaqueLink.cs | 15 ++--- MatFileHandler/SubsystemData.cs | 18 ++++++ MatFileHandler/SubsystemDataReader.cs | 12 ++-- 9 files changed, 130 insertions(+), 22 deletions(-) create mode 100644 MatFileHandler.Tests/test-data/good/enum.mat create mode 100644 MatFileHandler/EnumAdapter.cs diff --git a/MatFileHandler.Tests/ArrayHandlingTests.cs b/MatFileHandler.Tests/ArrayHandlingTests.cs index f15599e..7ac644b 100755 --- a/MatFileHandler.Tests/ArrayHandlingTests.cs +++ b/MatFileHandler.Tests/ArrayHandlingTests.cs @@ -10,8 +10,11 @@ namespace MatFileHandler.Tests /// /// Tests of Matlab array manipulation. /// - public class ArrayHandlingTests : IDisposable + public sealed class ArrayHandlingTests : IDisposable { + /// + /// Setup for array handling tests. + /// public ArrayHandlingTests() { Builder = new DataBuilder(); @@ -108,7 +111,7 @@ namespace MatFileHandler.Tests var file = Builder.NewFile(new List()); Assert.NotNull(file); } - + private static void TestCreateArrayOf() where T : struct { @@ -116,8 +119,12 @@ namespace MatFileHandler.Tests Assert.NotNull(array); } + /// + /// Cleanup. + /// public void Dispose() { + } } } \ No newline at end of file diff --git a/MatFileHandler.Tests/MatFileReaderTests.cs b/MatFileHandler.Tests/MatFileReaderTests.cs index 8645b82..a06ad71 100755 --- a/MatFileHandler.Tests/MatFileReaderTests.cs +++ b/MatFileHandler.Tests/MatFileReaderTests.cs @@ -164,6 +164,23 @@ namespace MatFileHandler.Tests Assert.Null(array.ConvertToComplexArray()); } + /// + /// Test reading an enumeration. + /// + [Fact] + public void TestEnum() + { + var matFile = GetTests("good")["enum"]; + var days = matFile["days"].Value; + var enumeration = new EnumAdapter(days); + Assert.Equal(5, enumeration.Values.Count); + Assert.Equal("Wednesday", enumeration.ValueNames[enumeration.Values[0]]); + Assert.Equal("Saturday", enumeration.ValueNames[enumeration.Values[1]]); + Assert.Equal("Monday", enumeration.ValueNames[enumeration.Values[2]]); + Assert.Equal("Wednesday", enumeration.ValueNames[enumeration.Values[3]]); + Assert.Equal("Saturday", enumeration.ValueNames[enumeration.Values[4]]); + } + /// /// Test reading a structure array. /// @@ -487,6 +504,9 @@ namespace MatFileHandler.Tests Assert.Null(d0); } + /// + /// Test 3-dimensional arrays. + /// [Fact] public void Test_3DArrays() { @@ -518,7 +538,10 @@ namespace MatFileHandler.Tests Assert.Equal(expected, obj.ConvertToMultidimensionalDoubleArray()); Assert.Null(obj.ConvertTo2dDoubleArray()); } - + + /// + /// Test four-dimensional arrays. + /// [Fact] public void Test_4DArrays() { diff --git a/MatFileHandler.Tests/test-data/good/enum.mat b/MatFileHandler.Tests/test-data/good/enum.mat new file mode 100644 index 0000000000000000000000000000000000000000..8450715eaf3a8a9b046f80c2d75e6cb48db1d1bd GIT binary patch literal 569 zcmeZu4DoSvQZUssQ1EpO(M`+DN!3vZ$Vn_o%P-2cQV4Jk_w+L}(NSJWftX&`3y1 zNMODs>QRMT*KgX-5Y&>NH*3~gCXwgNn{+39F?9U8(dlQ8hlJ9( zLkfIP%NaLj$7SYa1txi>)y+$fEA&i?E6VanvT82&ODfzLpOmM?%*Ek-M$?y-Q#_e# zG1tLX+s{EplLJ-edKnjc8GYUm&G@OGk@=AQ9+p3c*}m`#+);IxSH5D$Zu4t~;TJ=L z4+aj0Bvp<#J;~!$DVDf+Cegu|eaXG%hU&w8 zeASjH_`RFS*H$>Bhqa5jVFv>{+e&VK-?L?&YjoI*f<6Ux2McLSHVJ!sbvy9f|6?Q2 zDr}G>`1tU{ypnpO#E;8ang9JrOmOIxk9zf7@j=`Si%t96XVqIve0aW`gY8O*D5IIc U#Q8QHH~e$zm>3y)Ua2tx04TQ1-~a#s literal 0 HcmV?d00001 diff --git a/MatFileHandler/DataElementReader.cs b/MatFileHandler/DataElementReader.cs index a116c80..c5e2679 100755 --- a/MatFileHandler/DataElementReader.cs +++ b/MatFileHandler/DataElementReader.cs @@ -258,7 +258,7 @@ namespace MatFileHandler } else { - return new Opaque(name, typeDescription, className, new int[] { }, data); + return new Opaque(name, typeDescription, className, new int[] { }, data, subsystemData); } } diff --git a/MatFileHandler/EnumAdapter.cs b/MatFileHandler/EnumAdapter.cs new file mode 100644 index 0000000..ada3cba --- /dev/null +++ b/MatFileHandler/EnumAdapter.cs @@ -0,0 +1,60 @@ +// Copyright 2017-2018 Alexander Luzgarev + +namespace MatFileHandler +{ + /// + /// A better interface for using enum adapter. + /// + public class EnumAdapter + { + /// + /// Initializes a new instance of the class. + /// + /// Source enum object. + public EnumAdapter(IArray array) + { + var matObject = array as Opaque; + if (matObject?.RawData is not IStructureArray rawData) + { + throw new HandlerException("Cannot extract data for the enum adapter."); + } + + if (rawData["ValueNames"] is not IArrayOf valueNamesData) + { + throw new HandlerException("Cannot extract data for the enum adapter."); + } + + var numberOfNames = valueNamesData.Count; + var valueNames = new string[numberOfNames]; + var names = matObject.SubsystemData.FieldNames; + for (var i = 0; i < numberOfNames; i++) + { + valueNames[i] = names[valueNamesData[i] - 1]; + } + + if (rawData["ValueIndices"] is not IArrayOf valueIndices) + { + throw new HandlerException("Cannot extract data for the enum adapter."); + } + + ClassName = matObject.ClassName; + ValueNames = valueNames; + Values = valueIndices; + } + + /// + /// Gets name of the enumeration class. + /// + public string ClassName { get; } + + /// + /// Gets names of the enumeration values. + /// + public string[] ValueNames { get; } + + /// + /// Gets indices of values stored in the variable. + /// + public IArrayOf Values { get; } + } +} \ No newline at end of file diff --git a/MatFileHandler/Opaque.cs b/MatFileHandler/Opaque.cs index 9ce69b7..ba5711a 100644 --- a/MatFileHandler/Opaque.cs +++ b/MatFileHandler/Opaque.cs @@ -26,12 +26,14 @@ namespace MatFileHandler /// Class name. /// Dimensions of the object. /// Raw object's data. - public Opaque(string name, string typeDescription, string className, int[] dimensions, DataElement rawData) + /// Subsystem data. + public Opaque(string name, string typeDescription, string className, int[] dimensions, DataElement rawData, SubsystemData subsystemData) : base(new ArrayFlags(ArrayType.MxOpaque, 0), dimensions, name) { TypeDescription = typeDescription ?? throw new ArgumentNullException(nameof(typeDescription)); ClassName = className ?? throw new ArgumentNullException(nameof(className)); RawData = rawData ?? throw new ArgumentNullException(nameof(rawData)); + SubsystemData = subsystemData ?? throw new ArgumentNullException(nameof(subsystemData)); } /// @@ -49,6 +51,11 @@ namespace MatFileHandler /// public string TypeDescription { get; } + /// + /// Gets subsystem data. + /// + public SubsystemData SubsystemData { get; } + /// public override Complex[]? ConvertToComplexArray() => null; diff --git a/MatFileHandler/OpaqueLink.cs b/MatFileHandler/OpaqueLink.cs index da69fc1..003b4dc 100644 --- a/MatFileHandler/OpaqueLink.cs +++ b/MatFileHandler/OpaqueLink.cs @@ -12,8 +12,6 @@ namespace MatFileHandler /// internal class OpaqueLink : Opaque, IMatObject { - private readonly SubsystemData subsystemData; - /// /// Initializes a new instance of the class. /// @@ -34,11 +32,10 @@ namespace MatFileHandler int[] indexToObjectId, int classIndex, SubsystemData subsystemData) - : base(name, typeDescription, className, dimensions, data) + : base(name, typeDescription, className, dimensions, data, subsystemData) { IndexToObjectId = indexToObjectId ?? throw new ArgumentNullException(nameof(indexToObjectId)); ClassIndex = classIndex; - this.subsystemData = subsystemData ?? throw new ArgumentNullException(nameof(subsystemData)); } /// @@ -74,9 +71,9 @@ namespace MatFileHandler /// /// Gets name of this object's class. /// - public override string ClassName => subsystemData.ClassInformation[ClassIndex].Name; + public override string ClassName => SubsystemData.ClassInformation[ClassIndex].Name; - private string[] FieldNamesArray => subsystemData.ClassInformation[ClassIndex].FieldNames.ToArray(); + private string[] FieldNamesArray => SubsystemData.ClassInformation[ClassIndex].FieldNames.ToArray(); /// public IArray this[string field, params int[] list] @@ -108,7 +105,7 @@ namespace MatFileHandler private bool TryGetValue(string field, out IArray? output, params int[] 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)) { output = default; @@ -122,9 +119,9 @@ namespace MatFileHandler } var objectId = IndexToObjectId[index]; - var objectInfo = subsystemData.ObjectInformation[objectId]; + var objectInfo = SubsystemData.ObjectInformation[objectId]; var fieldId = objectInfo.FieldLinks[fieldIndex]; - output = subsystemData.Data[fieldId]; + output = SubsystemData.Data[fieldId]; return true; } diff --git a/MatFileHandler/SubsystemData.cs b/MatFileHandler/SubsystemData.cs index f14ffae..fe1947e 100644 --- a/MatFileHandler/SubsystemData.cs +++ b/MatFileHandler/SubsystemData.cs @@ -29,15 +29,18 @@ namespace MatFileHandler /// /// Information about the classes. /// Information about the objects. + /// Field names. /// Field values. public SubsystemData( Dictionary classInformation, Dictionary objectInformation, + string[] fieldNames, Dictionary data) { _realData = new RealSubsystemData( classInformation, objectInformation, + fieldNames, data); } @@ -59,6 +62,12 @@ namespace MatFileHandler public Dictionary ObjectInformation => _realData?.ObjectInformation ?? throw new HandlerException("Subsystem data missing."); + /// + /// Gets field names. + /// + public string[] FieldNames => + _realData?.FieldNames ?? throw new HandlerException("Subsystem data missing."); + /// /// Initialize this object from another object. /// This ugly hack allows us to read the opaque objects and store references to @@ -71,6 +80,7 @@ namespace MatFileHandler _realData = new RealSubsystemData( data.ClassInformation, data.ObjectInformation, + data.FieldNames, data.Data); } @@ -147,14 +157,17 @@ namespace MatFileHandler /// /// Class information. /// Object information. + /// Field names. /// Data. public RealSubsystemData( Dictionary classInformation, Dictionary objectInformation, + string[] fieldNames, IReadOnlyDictionary data) { ClassInformation = classInformation ?? throw new ArgumentNullException(nameof(classInformation)); ObjectInformation = objectInformation ?? throw new ArgumentNullException(nameof(objectInformation)); + FieldNames = fieldNames; Data = data ?? throw new ArgumentNullException(nameof(data)); } @@ -172,6 +185,11 @@ namespace MatFileHandler /// Gets information about all the objects occurring in the file. /// public Dictionary ObjectInformation { get; } + + /// + /// Gets field names. + /// + public string[] FieldNames { get; } } } } \ No newline at end of file diff --git a/MatFileHandler/SubsystemDataReader.cs b/MatFileHandler/SubsystemDataReader.cs index 39ee74b..b6141d8 100644 --- a/MatFileHandler/SubsystemDataReader.cs +++ b/MatFileHandler/SubsystemDataReader.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Text; @@ -58,7 +59,7 @@ namespace MatFileHandler data[i] = TransformOpaqueData(opaqueData[i + 2], subsystemData); } - return new SubsystemData(classInformation, objectInformation, data); + return new SubsystemData(classInformation, objectInformation, fieldNames, data); } private static Dictionary> ReadObjectPositionsToValues(byte[] info, int[] offsets, int numberOfObjectPositions) @@ -303,20 +304,15 @@ namespace MatFileHandler { var next = BitConverter.ToInt32(bytes, position); position += 4; - if (next == 0) + if (next == bytes.Length) { - if (position % 8 != 0) - { - position += 4; - } - break; } offsets.Add(next); } - return (offsets.ToArray(), position); + return (offsets.ToArray(), 40); } private static IArray TransformOpaqueData(IArray array, SubsystemData subsystemData) From 106c839b1088f9b3b788fe1d9d39e65246dfe86b Mon Sep 17 00:00:00 2001 From: Alexander Luzgarev Date: Sat, 15 Mar 2025 09:05:20 +0100 Subject: [PATCH 2/3] Get rid of analyzers in test project --- MatFileHandler.Tests/MatFileHandler.Tests.csproj | 8 -------- 1 file changed, 8 deletions(-) diff --git a/MatFileHandler.Tests/MatFileHandler.Tests.csproj b/MatFileHandler.Tests/MatFileHandler.Tests.csproj index 9dacef9..3f44415 100755 --- a/MatFileHandler.Tests/MatFileHandler.Tests.csproj +++ b/MatFileHandler.Tests/MatFileHandler.Tests.csproj @@ -7,9 +7,6 @@ ..\MatFileHandler.ruleset bin\Debug\net5.0\MatFileHandler.Tests.xml - - - @@ -24,9 +21,4 @@ - - - All - - \ No newline at end of file From f342abab7bf9516fb9eaf6ed333dae4bcf701eb9 Mon Sep 17 00:00:00 2001 From: Alexander Luzgarev Date: Sat, 15 Mar 2025 09:05:26 +0100 Subject: [PATCH 3/3] Bump version & year in LICENSE --- LICENSE.md | 2 +- MatFileHandler/MatFileHandler.csproj | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/LICENSE.md b/LICENSE.md index 3996297..be2b4bb 100755 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright 2017-2020 Alexander Luzgarev +Copyright 2017-2025 Alexander Luzgarev Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/MatFileHandler/MatFileHandler.csproj b/MatFileHandler/MatFileHandler.csproj index 62e831d..903b54f 100755 --- a/MatFileHandler/MatFileHandler.csproj +++ b/MatFileHandler/MatFileHandler.csproj @@ -1,7 +1,7 @@  netstandard2.0;net461;net472 - 1.4.0-beta2 + 1.4.0-beta3 MatFileHandler A library for reading and writing MATLAB .mat files. Alexander Luzgarev @@ -15,7 +15,7 @@ bin\$(Configuration)\ $(OutputPath)\$(TargetFramework)\$(AssemblyName).xml true - 8.0 + 10.0 enable true true