Merge pull request #30 from mahalex/dev/enums

Support for enumerations
This commit is contained in:
Alexander Luzgarev 2025-03-15 09:25:59 +01:00 committed by GitHub
commit f56508c3ad
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 133 additions and 33 deletions

View File

@ -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:

View File

@ -10,8 +10,11 @@ namespace MatFileHandler.Tests
/// <summary>
/// Tests of Matlab array manipulation.
/// </summary>
public class ArrayHandlingTests : IDisposable
public sealed class ArrayHandlingTests : IDisposable
{
/// <summary>
/// Setup for array handling tests.
/// </summary>
public ArrayHandlingTests()
{
Builder = new DataBuilder();
@ -116,8 +119,12 @@ namespace MatFileHandler.Tests
Assert.NotNull(array);
}
/// <summary>
/// Cleanup.
/// </summary>
public void Dispose()
{
}
}
}

View File

@ -7,9 +7,6 @@
<CodeAnalysisRuleSet>..\MatFileHandler.ruleset</CodeAnalysisRuleSet>
<DocumentationFile>bin\Debug\net5.0\MatFileHandler.Tests.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<AdditionalFiles Include="..\stylecop.json" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
<PackageReference Include="xunit" Version="2.9.0" />
@ -24,9 +21,4 @@
<ItemGroup>
<Content Include="test-data\**\*.mat" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
<PrivateAssets>All</PrivateAssets>
</PackageReference>
</ItemGroup>
</Project>

View File

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

Binary file not shown.

View File

@ -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);
}
}

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

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net461;net472</TargetFrameworks>
<PackageVersion>1.4.0-beta2</PackageVersion>
<PackageVersion>1.4.0-beta3</PackageVersion>
<PackageId>MatFileHandler</PackageId>
<Title>A library for reading and writing MATLAB .mat files.</Title>
<Authors>Alexander Luzgarev</Authors>
@ -15,7 +15,7 @@
<OutputPath>bin\$(Configuration)\</OutputPath>
<DocumentationFile>$(OutputPath)\$(TargetFramework)\$(AssemblyName).xml</DocumentationFile>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<LangVersion>8.0</LangVersion>
<LangVersion>10.0</LangVersion>
<Nullable>enable</Nullable>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<IncludeSymbols>true</IncludeSymbols>

View File

@ -26,12 +26,14 @@ namespace MatFileHandler
/// <param name="className">Class name.</param>
/// <param name="dimensions">Dimensions of the object.</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)
{
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));
}
/// <summary>
@ -49,6 +51,11 @@ namespace MatFileHandler
/// </summary>
public string TypeDescription { get; }
/// <summary>
/// Gets subsystem data.
/// </summary>
public SubsystemData SubsystemData { get; }
/// <inheritdoc />
public override Complex[]? ConvertToComplexArray() => null;

View File

@ -12,8 +12,6 @@ namespace MatFileHandler
/// </summary>
internal class OpaqueLink : Opaque, IMatObject
{
private readonly SubsystemData subsystemData;
/// <summary>
/// Initializes a new instance of the <see cref="OpaqueLink"/> class.
/// </summary>
@ -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));
}
/// <summary>
@ -74,9 +71,9 @@ namespace MatFileHandler
/// <summary>
/// Gets name of this object's class.
/// </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 />
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;
}

View File

@ -29,15 +29,18 @@ namespace MatFileHandler
/// </summary>
/// <param name="classInformation">Information about the classes.</param>
/// <param name="objectInformation">Information about the objects.</param>
/// <param name="fieldNames">Field names.</param>
/// <param name="data">Field values.</param>
public SubsystemData(
Dictionary<int, ClassInfo> classInformation,
Dictionary<int, ObjectInfo> objectInformation,
string[] fieldNames,
Dictionary<int, IArray> data)
{
_realData = new RealSubsystemData(
classInformation,
objectInformation,
fieldNames,
data);
}
@ -59,6 +62,12 @@ namespace MatFileHandler
public Dictionary<int, ObjectInfo> ObjectInformation =>
_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>
/// 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
/// </summary>
/// <param name="classInformation">Class information.</param>
/// <param name="objectInformation">Object information.</param>
/// <param name="fieldNames">Field names.</param>
/// <param name="data">Data.</param>
public RealSubsystemData(
Dictionary<int, ClassInfo> classInformation,
Dictionary<int, ObjectInfo> objectInformation,
string[] fieldNames,
IReadOnlyDictionary<int, IArray> 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.
/// </summary>
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.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<int, Dictionary<int, int>> 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)