Implement EnumAdapter

#17
This commit is contained in:
Alexander Luzgarev 2025-03-15 09:04:47 +01:00
parent d41020c6b8
commit 5a2226629a
9 changed files with 130 additions and 22 deletions

View File

@ -10,8 +10,11 @@ namespace MatFileHandler.Tests
/// <summary> /// <summary>
/// Tests of Matlab array manipulation. /// Tests of Matlab array manipulation.
/// </summary> /// </summary>
public class ArrayHandlingTests : IDisposable public sealed class ArrayHandlingTests : IDisposable
{ {
/// <summary>
/// Setup for array handling tests.
/// </summary>
public ArrayHandlingTests() public ArrayHandlingTests()
{ {
Builder = new DataBuilder(); Builder = new DataBuilder();
@ -108,7 +111,7 @@ namespace MatFileHandler.Tests
var file = Builder.NewFile(new List<IVariable>()); var file = Builder.NewFile(new List<IVariable>());
Assert.NotNull(file); Assert.NotNull(file);
} }
private static void TestCreateArrayOf<T>() private static void TestCreateArrayOf<T>()
where T : struct where T : struct
{ {
@ -116,8 +119,12 @@ namespace MatFileHandler.Tests
Assert.NotNull(array); Assert.NotNull(array);
} }
/// <summary>
/// Cleanup.
/// </summary>
public void Dispose() public void Dispose()
{ {
} }
} }
} }

View File

@ -164,6 +164,23 @@ namespace MatFileHandler.Tests
Assert.Null(array.ConvertToComplexArray()); Assert.Null(array.ConvertToComplexArray());
} }
/// <summary>
/// Test reading an enumeration.
/// </summary>
[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]]);
}
/// <summary> /// <summary>
/// Test reading a structure array. /// Test reading a structure array.
/// </summary> /// </summary>
@ -487,6 +504,9 @@ namespace MatFileHandler.Tests
Assert.Null(d0); Assert.Null(d0);
} }
/// <summary>
/// Test 3-dimensional arrays.
/// </summary>
[Fact] [Fact]
public void Test_3DArrays() public void Test_3DArrays()
{ {
@ -518,7 +538,10 @@ namespace MatFileHandler.Tests
Assert.Equal(expected, obj.ConvertToMultidimensionalDoubleArray()); Assert.Equal(expected, obj.ConvertToMultidimensionalDoubleArray());
Assert.Null(obj.ConvertTo2dDoubleArray()); Assert.Null(obj.ConvertTo2dDoubleArray());
} }
/// <summary>
/// Test four-dimensional arrays.
/// </summary>
[Fact] [Fact]
public void Test_4DArrays() public void Test_4DArrays()
{ {

Binary file not shown.

View File

@ -258,7 +258,7 @@ namespace MatFileHandler
} }
else else
{ {
return new Opaque(name, typeDescription, className, new int[] { }, data); return new Opaque(name, typeDescription, className, new int[] { }, data, subsystemData);
} }
} }

View File

@ -0,0 +1,60 @@
// Copyright 2017-2018 Alexander Luzgarev
namespace MatFileHandler
{
/// <summary>
/// A better interface for using enum adapter.
/// </summary>
public class EnumAdapter
{
/// <summary>
/// Initializes a new instance of the <see cref="EnumAdapter"/> class.
/// </summary>
/// <param name="array">Source enum object.</param>
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<uint> 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<uint> valueIndices)
{
throw new HandlerException("Cannot extract data for the enum adapter.");
}
ClassName = matObject.ClassName;
ValueNames = valueNames;
Values = valueIndices;
}
/// <summary>
/// Gets name of the enumeration class.
/// </summary>
public string ClassName { get; }
/// <summary>
/// Gets names of the enumeration values.
/// </summary>
public string[] ValueNames { get; }
/// <summary>
/// Gets indices of values stored in the variable.
/// </summary>
public IArrayOf<uint> Values { get; }
}
}

View File

@ -26,12 +26,14 @@ namespace MatFileHandler
/// <param name="className">Class name.</param> /// <param name="className">Class name.</param>
/// <param name="dimensions">Dimensions of the object.</param> /// <param name="dimensions">Dimensions of the object.</param>
/// <param name="rawData">Raw object's data.</param> /// <param name="rawData">Raw object's data.</param>
public Opaque(string name, string typeDescription, string className, int[] dimensions, DataElement rawData) /// <param name="subsystemData">Subsystem data.</param>
public Opaque(string name, string typeDescription, string className, int[] dimensions, DataElement rawData, SubsystemData subsystemData)
: base(new ArrayFlags(ArrayType.MxOpaque, 0), dimensions, name) : base(new ArrayFlags(ArrayType.MxOpaque, 0), dimensions, name)
{ {
TypeDescription = typeDescription ?? throw new ArgumentNullException(nameof(typeDescription)); TypeDescription = typeDescription ?? throw new ArgumentNullException(nameof(typeDescription));
ClassName = className ?? throw new ArgumentNullException(nameof(className)); ClassName = className ?? throw new ArgumentNullException(nameof(className));
RawData = rawData ?? throw new ArgumentNullException(nameof(rawData)); RawData = rawData ?? throw new ArgumentNullException(nameof(rawData));
SubsystemData = subsystemData ?? throw new ArgumentNullException(nameof(subsystemData));
} }
/// <summary> /// <summary>
@ -49,6 +51,11 @@ namespace MatFileHandler
/// </summary> /// </summary>
public string TypeDescription { get; } public string TypeDescription { get; }
/// <summary>
/// Gets subsystem data.
/// </summary>
public SubsystemData SubsystemData { get; }
/// <inheritdoc /> /// <inheritdoc />
public override Complex[]? ConvertToComplexArray() => null; public override Complex[]? ConvertToComplexArray() => null;

View File

@ -12,8 +12,6 @@ namespace MatFileHandler
/// </summary> /// </summary>
internal class OpaqueLink : Opaque, IMatObject internal class OpaqueLink : Opaque, IMatObject
{ {
private readonly SubsystemData subsystemData;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="OpaqueLink"/> class. /// Initializes a new instance of the <see cref="OpaqueLink"/> class.
/// </summary> /// </summary>
@ -34,11 +32,10 @@ namespace MatFileHandler
int[] indexToObjectId, int[] indexToObjectId,
int classIndex, int classIndex,
SubsystemData subsystemData) SubsystemData subsystemData)
: base(name, typeDescription, className, dimensions, data) : base(name, typeDescription, className, dimensions, data, subsystemData)
{ {
IndexToObjectId = indexToObjectId ?? throw new ArgumentNullException(nameof(indexToObjectId)); IndexToObjectId = indexToObjectId ?? throw new ArgumentNullException(nameof(indexToObjectId));
ClassIndex = classIndex; ClassIndex = classIndex;
this.subsystemData = subsystemData ?? throw new ArgumentNullException(nameof(subsystemData));
} }
/// <summary> /// <summary>
@ -74,9 +71,9 @@ namespace MatFileHandler
/// <summary> /// <summary>
/// Gets name of this object's class. /// Gets name of this object's class.
/// </summary> /// </summary>
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();
/// <inheritdoc /> /// <inheritdoc />
public IArray this[string field, params int[] list] 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) 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; output = default;
@ -122,9 +119,9 @@ namespace MatFileHandler
} }
var objectId = IndexToObjectId[index]; var objectId = IndexToObjectId[index];
var objectInfo = subsystemData.ObjectInformation[objectId]; var objectInfo = SubsystemData.ObjectInformation[objectId];
var fieldId = objectInfo.FieldLinks[fieldIndex]; var fieldId = objectInfo.FieldLinks[fieldIndex];
output = subsystemData.Data[fieldId]; output = SubsystemData.Data[fieldId];
return true; return true;
} }

View File

@ -29,15 +29,18 @@ namespace MatFileHandler
/// </summary> /// </summary>
/// <param name="classInformation">Information about the classes.</param> /// <param name="classInformation">Information about the classes.</param>
/// <param name="objectInformation">Information about the objects.</param> /// <param name="objectInformation">Information about the objects.</param>
/// <param name="fieldNames">Field names.</param>
/// <param name="data">Field values.</param> /// <param name="data">Field values.</param>
public SubsystemData( public SubsystemData(
Dictionary<int, ClassInfo> classInformation, Dictionary<int, ClassInfo> classInformation,
Dictionary<int, ObjectInfo> objectInformation, Dictionary<int, ObjectInfo> objectInformation,
string[] fieldNames,
Dictionary<int, IArray> data) Dictionary<int, IArray> data)
{ {
_realData = new RealSubsystemData( _realData = new RealSubsystemData(
classInformation, classInformation,
objectInformation, objectInformation,
fieldNames,
data); data);
} }
@ -59,6 +62,12 @@ namespace MatFileHandler
public Dictionary<int, ObjectInfo> ObjectInformation => public Dictionary<int, ObjectInfo> ObjectInformation =>
_realData?.ObjectInformation ?? throw new HandlerException("Subsystem data missing."); _realData?.ObjectInformation ?? throw new HandlerException("Subsystem data missing.");
/// <summary>
/// Gets field names.
/// </summary>
public string[] FieldNames =>
_realData?.FieldNames ?? throw new HandlerException("Subsystem data missing.");
/// <summary> /// <summary>
/// Initialize this object from another object. /// Initialize this object from another object.
/// This ugly hack allows us to read the opaque objects and store references to /// This ugly hack allows us to read the opaque objects and store references to
@ -71,6 +80,7 @@ namespace MatFileHandler
_realData = new RealSubsystemData( _realData = new RealSubsystemData(
data.ClassInformation, data.ClassInformation,
data.ObjectInformation, data.ObjectInformation,
data.FieldNames,
data.Data); data.Data);
} }
@ -147,14 +157,17 @@ namespace MatFileHandler
/// </summary> /// </summary>
/// <param name="classInformation">Class information.</param> /// <param name="classInformation">Class information.</param>
/// <param name="objectInformation">Object information.</param> /// <param name="objectInformation">Object information.</param>
/// <param name="fieldNames">Field names.</param>
/// <param name="data">Data.</param> /// <param name="data">Data.</param>
public RealSubsystemData( public RealSubsystemData(
Dictionary<int, ClassInfo> classInformation, Dictionary<int, ClassInfo> classInformation,
Dictionary<int, ObjectInfo> objectInformation, Dictionary<int, ObjectInfo> objectInformation,
string[] fieldNames,
IReadOnlyDictionary<int, IArray> data) IReadOnlyDictionary<int, IArray> data)
{ {
ClassInformation = classInformation ?? throw new ArgumentNullException(nameof(classInformation)); ClassInformation = classInformation ?? throw new ArgumentNullException(nameof(classInformation));
ObjectInformation = objectInformation ?? throw new ArgumentNullException(nameof(objectInformation)); ObjectInformation = objectInformation ?? throw new ArgumentNullException(nameof(objectInformation));
FieldNames = fieldNames;
Data = data ?? throw new ArgumentNullException(nameof(data)); Data = data ?? throw new ArgumentNullException(nameof(data));
} }
@ -172,6 +185,11 @@ namespace MatFileHandler
/// Gets 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; } public Dictionary<int, ObjectInfo> ObjectInformation { get; }
/// <summary>
/// Gets field names.
/// </summary>
public string[] FieldNames { get; }
} }
} }
} }

View File

@ -2,6 +2,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
@ -58,7 +59,7 @@ namespace MatFileHandler
data[i] = TransformOpaqueData(opaqueData[i + 2], subsystemData); data[i] = TransformOpaqueData(opaqueData[i + 2], subsystemData);
} }
return new SubsystemData(classInformation, objectInformation, data); return new SubsystemData(classInformation, objectInformation, fieldNames, data);
} }
private static Dictionary<int, Dictionary<int, int>> ReadObjectPositionsToValues(byte[] info, int[] offsets, int numberOfObjectPositions) private static Dictionary<int, Dictionary<int, int>> ReadObjectPositionsToValues(byte[] info, int[] offsets, int numberOfObjectPositions)
@ -303,20 +304,15 @@ namespace MatFileHandler
{ {
var next = BitConverter.ToInt32(bytes, position); var next = BitConverter.ToInt32(bytes, position);
position += 4; position += 4;
if (next == 0) if (next == bytes.Length)
{ {
if (position % 8 != 0)
{
position += 4;
}
break; break;
} }
offsets.Add(next); offsets.Add(next);
} }
return (offsets.ToArray(), position); return (offsets.ToArray(), 40);
} }
private static IArray TransformOpaqueData(IArray array, SubsystemData subsystemData) private static IArray TransformOpaqueData(IArray array, SubsystemData subsystemData)