From 5e70d7fdfded1f7bcccd34da2bd38e6466d043ce Mon Sep 17 00:00:00 2001 From: Alexander Luzgarev Date: Sat, 2 Mar 2019 12:24:15 +0100 Subject: [PATCH 1/7] Implement DatetimeAdapter --- MatFileHandler.Tests/MatFileReaderTests.cs | 30 +++++++++ .../test-data/good/datetime.mat | Bin 0 -> 532 bytes .../test-data/good/datetime2.mat | Bin 0 -> 538 bytes MatFileHandler/DatetimeAdapter.cs | 62 ++++++++++++++++++ 4 files changed, 92 insertions(+) create mode 100644 MatFileHandler.Tests/test-data/good/datetime.mat create mode 100644 MatFileHandler.Tests/test-data/good/datetime2.mat create mode 100644 MatFileHandler/DatetimeAdapter.cs diff --git a/MatFileHandler.Tests/MatFileReaderTests.cs b/MatFileHandler.Tests/MatFileReaderTests.cs index 88fd0e8..0757839 100755 --- a/MatFileHandler.Tests/MatFileReaderTests.cs +++ b/MatFileHandler.Tests/MatFileReaderTests.cs @@ -1,5 +1,6 @@ // Copyright 2017-2018 Alexander Luzgarev +using System; using System.IO; using System.Linq; using System.Numerics; @@ -388,6 +389,35 @@ namespace MatFileHandler.Tests Assert.That(ppp["y"].ConvertToDoubleArray(), Is.EquivalentTo(new[] { 200.0 })); } + /// + /// Test datetime objects. + /// + [Test] + public void TestDatetime() + { + var matFile = GetTests("good")["datetime"]; + var d = matFile["d"].Value as IMatObject; + var datetime = new DatetimeAdapter(d); + Assert.That(datetime.Dimensions, Is.EquivalentTo(new[] { 1, 2 })); + Assert.That(datetime[0], Is.EqualTo(new DateTimeOffset(2000, 1, 1, 0, 0, 0, TimeSpan.Zero))); + Assert.That(datetime[1], Is.EqualTo(new DateTimeOffset(1987, 1, 2, 3, 4, 5, TimeSpan.Zero))); + } + + /// + /// Another test for datetime objects. + /// + [Test] + public void TestDatetime2() + { + var matFile = GetTests("good")["datetime2"]; + var d = matFile["d"].Value as IMatObject; + var datetime = new DatetimeAdapter(d); + Assert.That(datetime.Dimensions, Is.EquivalentTo(new[] { 1, 1 })); + var diff = new DateTimeOffset(2, 1, 1, 1, 1, 1, 235, TimeSpan.Zero); + Assert.That(datetime[0] - diff < TimeSpan.FromMilliseconds(1)); + Assert.That(diff - datetime[0] < TimeSpan.FromMilliseconds(1)); + } + private static AbstractTestDataFactory GetTests(string factoryName) => new MatTestDataFactory(Path.Combine(TestDirectory, factoryName)); diff --git a/MatFileHandler.Tests/test-data/good/datetime.mat b/MatFileHandler.Tests/test-data/good/datetime.mat new file mode 100644 index 0000000000000000000000000000000000000000..6f6899fbff2d9c8f9d9d1c6ede2c9d9e840e2547 GIT binary patch literal 532 zcmeZu4DoSvQZUssQ1EpO(M`+DN!3vZ$Vn_o%P-2cQV4Jk_w+L}(NS64^{ZLMRC zox;6?Jr~ZmxZ3kLABj9r)DvL3@7%wG2E|+x&Iq=wN^iNWldx66PG4Ym>a!(n;;LdI zOT>;Icm67^@Y4MT`@_R;S~{Dr)F&Kx`<^-D)yxG+X97&+9Of_{HEyV24v}LE67m*u zbjk8e%hPg>%HwqQ6m?G9$Q^LRUVr%{M(Kn@T3uWc8$U>H{BZH=K?ZrDw+9+N*h`1V zSl*j4;S6JIKf{cxA51ry-|lR5^gM9e>Kv(s$Dj8H9p?VwXJY8>)4L4-pWw(= literal 0 HcmV?d00001 diff --git a/MatFileHandler.Tests/test-data/good/datetime2.mat b/MatFileHandler.Tests/test-data/good/datetime2.mat new file mode 100644 index 0000000000000000000000000000000000000000..d9d19c59414af34a1345fce7b4b6ae12f8787896 GIT binary patch literal 538 zcmeZu4DoSvQZUssQ1EpO(M`+DN!3vZ$Vn_o%P-2cQV4Jk_w+L}(NS*hC4+){Fy_o+6Mp@!Q+28oRZ4Uz{Hs(B{dIoWjhzd<$2iZ%U{ qIrs0Mu|Q|SK0dZTPaRm8v+6n+tQi9N6qc)donjPVV7NWmSQr2qHOqzo literal 0 HcmV?d00001 diff --git a/MatFileHandler/DatetimeAdapter.cs b/MatFileHandler/DatetimeAdapter.cs new file mode 100644 index 0000000..c86371f --- /dev/null +++ b/MatFileHandler/DatetimeAdapter.cs @@ -0,0 +1,62 @@ +// Copyright 2017-2018 Alexander Luzgarev + +using System; +using System.Linq; +using System.Numerics; + +namespace MatFileHandler +{ + /// + /// A better interface for using datetime objects. + /// + public class DatetimeAdapter + { + private readonly double[] data; + private readonly double[] data2; + private readonly int[] dimensions; + + private readonly DateTimeOffset epoch; + + /// + /// Initializes a new instance of the class. + /// + /// Source datetime object. + public DatetimeAdapter(IArray array) + { + var matObject = array as IMatObject; + if (matObject?.ClassName != "datetime") + { + throw new ArgumentException("The object provided is not a datetime."); + } + + epoch = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); + var dataArray = matObject["data", 0] as IArrayOf; + if (dataArray is null) + { + 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; + } + } + + /// + /// Gets datetime array dimensions. + /// + public int[] Dimensions => dimensions; + + /// + /// Gets values of datetime object at given position in the array converted to . + /// + /// Indices. + /// Value converted to . + public DateTimeOffset this[params int[] list] => epoch.AddMilliseconds(data[Dimensions.DimFlatten(list)]); + } +} \ No newline at end of file From 262c3a8314ad558e8cb05cc8b1510006825276c5 Mon Sep 17 00:00:00 2001 From: Alexander Luzgarev Date: Sat, 2 Mar 2019 20:10:03 +0100 Subject: [PATCH 2/7] Fix parsing of subsystem data --- MatFileHandler/OpaqueLink.cs | 4 +-- MatFileHandler/SubsystemDataReader.cs | 40 ++++++++++++++------------- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/MatFileHandler/OpaqueLink.cs b/MatFileHandler/OpaqueLink.cs index fcb0cfe..ed71d2f 100644 --- a/MatFileHandler/OpaqueLink.cs +++ b/MatFileHandler/OpaqueLink.cs @@ -107,8 +107,8 @@ namespace MatFileHandler return false; } - var objectPosition = IndexToObjectId[index]; - var objectInfo = subsystemData.ObjectInformation.First(pair => pair.Value.Position == objectPosition).Value; + var objectId = IndexToObjectId[index]; + var objectInfo = subsystemData.ObjectInformation[objectId]; var fieldId = objectInfo.FieldLinks[fieldIndex]; output = subsystemData.Data[fieldId]; return true; diff --git a/MatFileHandler/SubsystemDataReader.cs b/MatFileHandler/SubsystemDataReader.cs index ce7f18b..593cf6e 100644 --- a/MatFileHandler/SubsystemDataReader.cs +++ b/MatFileHandler/SubsystemDataReader.cs @@ -51,7 +51,7 @@ namespace MatFileHandler } var numberOfObjects = ((offsets[5] - offsets[4]) / 24) - 1; - Dictionary objectClasses = null; + Dictionary objectClasses = null; using (var stream = new MemoryStream(info, offsets[4], offsets[5] - offsets[4])) { using (var reader = new BinaryReader(stream)) @@ -60,12 +60,14 @@ namespace MatFileHandler } } - Dictionary> objectToFields = null; + 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)) { - objectToFields = ReadObjectToFieldsMapping(reader, numberOfObjects); + objectPositionsToValues = ReadObjectPositionsToValuesMapping(reader, numberOfObjectPositions); } } @@ -74,7 +76,7 @@ namespace MatFileHandler classIdToName, fieldNames, objectClasses, - objectToFields); + objectPositionsToValues); var allFields = objectInformation.Values.SelectMany(obj => obj.FieldLinks.Values); var data = new Dictionary(); @@ -107,17 +109,17 @@ namespace MatFileHandler return array; } - private static Dictionary ReadObjectClasses(BinaryReader reader, int numberOfObjects) + private static Dictionary ReadObjectClasses(BinaryReader reader, int numberOfObjects) { - var result = new Dictionary(); + var result = new Dictionary(); reader.ReadBytes(24); for (var i = 0; i < numberOfObjects; i++) { var classId = reader.ReadInt32(); reader.ReadBytes(12); var objectPosition = reader.ReadInt32(); - var objectId = reader.ReadInt32(); - result[objectPosition] = (objectId, classId); + var loadingOrder = reader.ReadInt32(); + result[i + 1] = (objectPosition, loadingOrder, classId); } return result; @@ -126,23 +128,23 @@ namespace MatFileHandler private static (Dictionary, Dictionary) GatherClassAndObjectInformation( Dictionary classIdToName, string[] fieldNames, - Dictionary objectClasses, - Dictionary> objectToFields) + Dictionary objectClasses, + Dictionary> objectPositionsToValues) { var classInfos = new Dictionary(); foreach (var classId in classIdToName.Keys) { var className = classIdToName[classId]; var fieldIds = new SortedSet(); - foreach (var objectPosition in objectToFields.Keys) + foreach (var objectPosition in objectPositionsToValues.Keys) { - var (_, thisObjectClassId) = objectClasses[objectPosition]; - if (thisObjectClassId != classId) + var keyValuePair = objectClasses.First(pair => pair.Value.objectPosition == objectPosition); + if (keyValuePair.Value.classId != classId) { continue; } - foreach (var fieldId in objectToFields[objectPosition].Keys) + foreach (var fieldId in objectPositionsToValues[objectPosition].Keys) { fieldIds.Add(fieldId); } @@ -156,10 +158,10 @@ namespace MatFileHandler } var objectInfos = new Dictionary(); - foreach (var objectPosition in objectToFields.Keys) + foreach (var objectPosition in objectPositionsToValues.Keys) { - var (objectId, _) = objectClasses[objectPosition]; - objectInfos[objectId] = new SubsystemData.ObjectInfo(objectPosition, objectToFields[objectPosition]); + var keyValuePair = objectClasses.First(pair => pair.Value.objectPosition == objectPosition); + objectInfos[keyValuePair.Key] = new SubsystemData.ObjectInfo(objectPosition, objectPositionsToValues[objectPosition]); } return (classInfos, objectInfos); @@ -180,11 +182,11 @@ namespace MatFileHandler return result; } - private static Dictionary> ReadObjectToFieldsMapping(BinaryReader reader, int numberOfObjects) + private static Dictionary> ReadObjectPositionsToValuesMapping(BinaryReader reader, int numberOfValues) { var result = new Dictionary>(); reader.ReadBytes(8); - for (var objectPosition = 1; objectPosition <= numberOfObjects; objectPosition++) + for (var objectPosition = 1; objectPosition <= numberOfValues; objectPosition++) { result[objectPosition] = ReadFieldToFieldDataMapping(reader); var position = reader.BaseStream.Position; From 69599c12b502de86e748ad83911119f448e60feb Mon Sep 17 00:00:00 2001 From: Alexander Luzgarev Date: Sun, 3 Mar 2019 16:18:36 +0100 Subject: [PATCH 3/7] Implement strings --- MatFileHandler.Tests/MatFileReaderTests.cs | 16 + .../test-data/good/string.mat | Bin 0 -> 505 bytes MatFileHandler/StringAdapter.cs | 80 +++++ MatFileHandler/SubsystemData.cs | 9 +- MatFileHandler/SubsystemDataReader.cs | 285 ++++++++++++------ 5 files changed, 289 insertions(+), 101 deletions(-) create mode 100644 MatFileHandler.Tests/test-data/good/string.mat create mode 100644 MatFileHandler/StringAdapter.cs diff --git a/MatFileHandler.Tests/MatFileReaderTests.cs b/MatFileHandler.Tests/MatFileReaderTests.cs index 0757839..bdb91e8 100755 --- a/MatFileHandler.Tests/MatFileReaderTests.cs +++ b/MatFileHandler.Tests/MatFileReaderTests.cs @@ -418,6 +418,22 @@ namespace MatFileHandler.Tests Assert.That(diff - datetime[0] < TimeSpan.FromMilliseconds(1)); } + /// + /// Test string objects. + /// + [Test] + public void TestString() + { + var matFile = GetTests("good")["string"]; + var s = matFile["s"].Value as IMatObject; + var str = new StringAdapter(s); + Assert.That(str.Dimensions, Is.EquivalentTo(new[] { 4, 1 })); + Assert.That(str[0], Is.EqualTo("abc")); + Assert.That(str[1], Is.EqualTo("defgh")); + Assert.That(str[2], Is.EqualTo("абвгд")); + Assert.That(str[3], Is.EqualTo("æøå")); + } + private static AbstractTestDataFactory GetTests(string factoryName) => new MatTestDataFactory(Path.Combine(TestDirectory, factoryName)); diff --git a/MatFileHandler.Tests/test-data/good/string.mat b/MatFileHandler.Tests/test-data/good/string.mat new file mode 100644 index 0000000000000000000000000000000000000000..b86a9c9e98ac47129268956780fe8e43268f6019 GIT binary patch literal 505 zcmeZu4DoSvQZUssQ1EpO(M`+DN!3vZ$Vn_o%P-2cQV4Jk_w+L}(NSqnSK5l)L-1_O}fhxrrjHjkI z7`!)9JbBQ7LFO%6J;*#oMu>R~Ok!+oW(r3ckIdl_IKcJPnCT;H8V4_vPkmtxz*#u0i33TAm$$_cHt13WxMap4!Inh(ke-;SsCC?uDNWCd>#w z831v4gRnuOpfkUIOoJe|^n5uxl`1CYL-+p#6h8U!$kB*fiiZqLIvHg~$}^jN z4j&PgoFp+xGDspw@{+_QNh1lK2%gBEh@MEFh`V=0W z_!oV!!TR6DuZwTD>)tgoSg-GDD1M}Z2Ndz3WWy(=dx?d@ + /// A better interface for using datetime objects. + /// + public class StringAdapter + { + private readonly int[] dimensions; + private readonly string[] strings; + + /// + /// Initializes a new instance of the class. + /// + /// Source datetime object. + public StringAdapter(IArray array) + { + var matObject = array as IMatObject; + if (matObject?.ClassName != "string") + { + throw new ArgumentException("The object provided is not a string."); + } + + var binaryData = matObject["any", 0] as IArrayOf; + + (dimensions, strings) = ParseBinaryData(binaryData.Data); + } + + /// + /// Gets datetime array dimensions. + /// + public int[] Dimensions => dimensions; + + /// + /// Gets string object at given position. + /// + /// Indices. + /// Value. + public string this[params int[] list] => strings[Dimensions.DimFlatten(list)]; + + private static (int[] dimensions, string[] strings) ParseBinaryData(ulong[] binaryData) + { + var numberOfDimensions = (int)binaryData[1]; + var d = new int[numberOfDimensions]; + for (var i = 0; i < numberOfDimensions; i++) + { + d[i] = (int)binaryData[i + 2]; + } + + var numberOfElements = d.NumberOfElements(); + var start = numberOfDimensions + 2; + var lengths = new int[numberOfElements]; + for (var i = 0; i < numberOfElements; i++) + { + lengths[i] = (int)binaryData[start + i]; + } + + var strings = new string[numberOfElements]; + + start += numberOfElements; + var numberOfUlongsLeft = binaryData.Length - start; + var bytes = new byte[numberOfUlongsLeft * sizeof(ulong)]; + Buffer.BlockCopy(binaryData, start * sizeof(ulong), bytes, 0, bytes.Length); + var counter = 0; + for (var i = 0; i < numberOfElements; i++) + { + strings[i] = Encoding.Unicode.GetString(bytes, counter * 2, lengths[i] * 2); + counter += lengths[i]; + } + + return (d, strings); + } + } +} \ No newline at end of file diff --git a/MatFileHandler/SubsystemData.cs b/MatFileHandler/SubsystemData.cs index ab65292..fd987b8 100644 --- a/MatFileHandler/SubsystemData.cs +++ b/MatFileHandler/SubsystemData.cs @@ -125,11 +125,9 @@ namespace MatFileHandler /// /// Initializes a new instance of the class. /// - /// Position of object in the object information table. /// A dictionary mapping the field indices to "field values" indices. - public ObjectInfo(int position, Dictionary fieldLinks) + public ObjectInfo(Dictionary fieldLinks) { - Position = position; this.fieldLinks = fieldLinks ?? throw new ArgumentNullException(nameof(fieldLinks)); } @@ -137,11 +135,6 @@ namespace MatFileHandler /// Gets mapping between the field indices and "field values" indices. /// public IReadOnlyDictionary FieldLinks => fieldLinks; - - /// - /// Gets position of object in the object information table. - /// - public int Position { get; } } } } \ No newline at end of file diff --git a/MatFileHandler/SubsystemDataReader.cs b/MatFileHandler/SubsystemDataReader.cs index 593cf6e..affe427 100644 --- a/MatFileHandler/SubsystemDataReader.cs +++ b/MatFileHandler/SubsystemDataReader.cs @@ -50,8 +50,19 @@ namespace MatFileHandler } } + 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 numberOfObjects = ((offsets[5] - offsets[4]) / 24) - 1; - Dictionary objectClasses = null; + Dictionary objectClasses = null; using (var stream = new MemoryStream(info, offsets[4], offsets[5] - offsets[4])) { using (var reader = new BinaryReader(stream)) @@ -60,7 +71,7 @@ namespace MatFileHandler } } - var numberOfObjectPositions = objectClasses.Values.Count(x => x.objectPosition != 0); + var numberOfObjectPositions = objectClasses.Values.Count(x => x.ObjectPosition != 0); Dictionary> objectPositionsToValues = null; using (var stream = new MemoryStream(info, offsets[5], offsets[6] - offsets[5])) @@ -76,7 +87,8 @@ namespace MatFileHandler classIdToName, fieldNames, objectClasses, - objectPositionsToValues); + objectPositionsToValues, + embeddedObjectPositionsToValues); var allFields = objectInformation.Values.SelectMany(obj => obj.FieldLinks.Values); var data = new Dictionary(); @@ -84,62 +96,28 @@ namespace MatFileHandler { data[i] = TransformOpaqueData(opaqueData[i + 2], subsystemData); } + return new SubsystemData(classInformation, objectInformation, data); } - private static IArray TransformOpaqueData(IArray array, SubsystemData subsystemData) - { - if (array is MatNumericalArrayOf uintArray) - { - if (uintArray.Data[0] == 3707764736u) - { - var (dimensions, indexToObjectId, classIndex) = DataElementReader.ParseOpaqueData(uintArray.Data); - return new OpaqueLink( - uintArray.Name, - string.Empty, - string.Empty, - dimensions, - array as DataElement, - indexToObjectId, - classIndex, - subsystemData); - } - } - - return array; - } - - private static Dictionary ReadObjectClasses(BinaryReader reader, int numberOfObjects) - { - var result = new Dictionary(); - reader.ReadBytes(24); - for (var i = 0; i < numberOfObjects; i++) - { - var classId = reader.ReadInt32(); - reader.ReadBytes(12); - var objectPosition = reader.ReadInt32(); - var loadingOrder = reader.ReadInt32(); - result[i + 1] = (objectPosition, loadingOrder, classId); - } - - return result; - } - - private static (Dictionary, Dictionary) GatherClassAndObjectInformation( - Dictionary classIdToName, - string[] fieldNames, - Dictionary objectClasses, - Dictionary> objectPositionsToValues) + private static (Dictionary, Dictionary) + GatherClassAndObjectInformation( + Dictionary classIdToName, + string[] fieldNames, + Dictionary objectClasses, + Dictionary> objectPositionsToValues, + Dictionary embeddedObjectPositionsToValues) { var classInfos = new Dictionary(); + var newEmbeddedObjectPositionsToValues = new Dictionary>(); foreach (var classId in classIdToName.Keys) { var className = classIdToName[classId]; var fieldIds = new SortedSet(); foreach (var objectPosition in objectPositionsToValues.Keys) { - var keyValuePair = objectClasses.First(pair => pair.Value.objectPosition == objectPosition); - if (keyValuePair.Value.classId != classId) + var keyValuePair = objectClasses.First(pair => pair.Value.ObjectPosition == objectPosition); + if (keyValuePair.Value.ClassId != classId) { continue; } @@ -149,55 +127,48 @@ namespace MatFileHandler fieldIds.Add(fieldId); } } + + foreach (var objectPosition in embeddedObjectPositionsToValues.Keys) + { + var keyValuePair = objectClasses.First(pair => pair.Value.EmbeddedObjectPosition == objectPosition); + if (keyValuePair.Value.ClassId != classId) + { + continue; + } + + fieldIds.Add(embeddedObjectPositionsToValues[objectPosition].FieldIndex); + var d = new Dictionary(); + var embeddedInfo = embeddedObjectPositionsToValues[objectPosition]; + d[embeddedInfo.FieldIndex] = embeddedInfo.ValueIndex; + newEmbeddedObjectPositionsToValues[objectPosition] = d; + } + var fieldToIndex = new Dictionary(); foreach (var fieldId in fieldIds) { fieldToIndex[fieldNames[fieldId - 1]] = fieldId; } + classInfos[classId] = new SubsystemData.ClassInfo(className, fieldToIndex); } var objectInfos = new Dictionary(); foreach (var objectPosition in objectPositionsToValues.Keys) { - var keyValuePair = objectClasses.First(pair => pair.Value.objectPosition == objectPosition); - objectInfos[keyValuePair.Key] = new SubsystemData.ObjectInfo(objectPosition, objectPositionsToValues[objectPosition]); + var keyValuePair = objectClasses.First(pair => pair.Value.ObjectPosition == objectPosition); + objectInfos[keyValuePair.Key] = new SubsystemData.ObjectInfo(objectPositionsToValues[objectPosition]); + } + + foreach (var objectPosition in embeddedObjectPositionsToValues.Keys) + { + var keyValuePair = objectClasses.First(pair => pair.Value.EmbeddedObjectPosition == objectPosition); + objectInfos[keyValuePair.Key] = + new SubsystemData.ObjectInfo(newEmbeddedObjectPositionsToValues[objectPosition]); } return (classInfos, objectInfos); } - private static Dictionary ReadFieldToFieldDataMapping(BinaryReader reader) - { - var length = reader.ReadInt32(); - var result = new Dictionary(); - for (var i = 0; i < length; i++) - { - var x = reader.ReadInt32(); - var y = reader.ReadInt32(); - var index = x * y; - var link = reader.ReadInt32(); - result[index] = link; - } - return result; - } - - private static Dictionary> ReadObjectPositionsToValuesMapping(BinaryReader reader, int numberOfValues) - { - var result = new Dictionary>(); - reader.ReadBytes(8); - for (var objectPosition = 1; objectPosition <= numberOfValues; objectPosition++) - { - result[objectPosition] = ReadFieldToFieldDataMapping(reader); - var position = reader.BaseStream.Position; - if (position % 8 != 0) - { - reader.ReadBytes(8 - (int)(position % 8)); - } - } - return result; - } - private static Dictionary ReadClassNames( BinaryReader reader, string[] fieldNames, @@ -221,6 +192,98 @@ namespace MatFileHandler return result; } + private static Dictionary ReadEmbeddedObjectPositionsToValuesMapping( + BinaryReader reader, int numberOfObjects) + { + var result = new Dictionary(); + reader.ReadBytes(8); + for (var objectPosition = 1; objectPosition <= numberOfObjects; objectPosition++) + { + var a = reader.ReadInt32(); + var fieldIndex = reader.ReadInt32(); + var c = reader.ReadInt32(); + var valueIndex = reader.ReadInt32(); + result[objectPosition] = new EmbeddedObjectInformation(fieldIndex, valueIndex); + } + + return result; + } + + private static string[] ReadFieldNames(byte[] bytes, int startPosition, int numberOfFields) + { + var result = new string[numberOfFields]; + var position = startPosition; + for (var i = 0; i < numberOfFields; i++) + { + var list = new List(); + while (bytes[position] != 0) + { + list.Add(bytes[position]); + position++; + } + + result[i] = Encoding.ASCII.GetString(list.ToArray()); + position++; + } + + return result; + } + + private static Dictionary ReadFieldToFieldDataMapping(BinaryReader reader) + { + var length = reader.ReadInt32(); + var result = new Dictionary(); + for (var i = 0; i < length; i++) + { + var x = reader.ReadInt32(); + var y = reader.ReadInt32(); + var index = x * y; + var link = reader.ReadInt32(); + result[index] = link; + } + + return result; + } + + private static Dictionary ReadObjectClasses( + BinaryReader reader, + int numberOfObjects) + { + var result = new Dictionary(); + reader.ReadBytes(24); + for (var i = 0; i < numberOfObjects; i++) + { + var classId = reader.ReadInt32(); + reader.ReadBytes(8); + var embeddedObjectPosition = reader.ReadInt32(); + var objectPosition = reader.ReadInt32(); + var loadingOrder = reader.ReadInt32(); + result[i + 1] = + new ObjectClassInformation(embeddedObjectPosition, objectPosition, loadingOrder, classId); + } + + return result; + } + + private static Dictionary> ReadObjectPositionsToValuesMapping( + BinaryReader reader, + int numberOfObjects) + { + var result = new Dictionary>(); + reader.ReadBytes(8); + for (var objectPosition = 1; objectPosition <= numberOfObjects; objectPosition++) + { + result[objectPosition] = ReadFieldToFieldDataMapping(reader); + var position = reader.BaseStream.Position; + if (position % 8 != 0) + { + reader.ReadBytes(8 - (int)(position % 8)); + } + } + + return result; + } + private static (int[] offsets, int newPosition) ReadOffsets(byte[] bytes, int startPosition) { var position = startPosition; @@ -238,29 +301,65 @@ namespace MatFileHandler break; } + offsets.Add(next); } return (offsets.ToArray(), position); } - private static string[] ReadFieldNames(byte[] bytes, int startPosition, int numberOfFields) + private static IArray TransformOpaqueData(IArray array, SubsystemData subsystemData) { - var result = new string[numberOfFields]; - var position = startPosition; - for (var i = 0; i < numberOfFields; i++) + if (array is MatNumericalArrayOf uintArray) { - var list = new List(); - while (bytes[position] != 0) + if (uintArray.Data[0] == 3707764736u) { - list.Add(bytes[position]); - position++; + var (dimensions, indexToObjectId, classIndex) = DataElementReader.ParseOpaqueData(uintArray.Data); + return new OpaqueLink( + uintArray.Name, + string.Empty, + string.Empty, + dimensions, + array as DataElement, + indexToObjectId, + classIndex, + subsystemData); } - result[i] = Encoding.ASCII.GetString(list.ToArray()); - position++; } - return result; + return array; + } + + private struct EmbeddedObjectInformation + { + public EmbeddedObjectInformation(int fieldIndex, int valueIndex) + { + FieldIndex = fieldIndex; + ValueIndex = valueIndex; + } + + public int FieldIndex { get; } + + public int ValueIndex { get; } + } + + private struct ObjectClassInformation + { + public ObjectClassInformation(int embeddedObjectPosition, int objectPosition, int loadingOrder, int classId) + { + EmbeddedObjectPosition = embeddedObjectPosition; + ObjectPosition = objectPosition; + LoadingOrder = loadingOrder; + ClassId = classId; + } + + public int ClassId { get; } + + public int EmbeddedObjectPosition { get; } + + public int LoadingOrder { get; } + + public int ObjectPosition { get; } } } -} +} \ No newline at end of file From 78f10338366e05788f6ecf6ebd675b3192412064 Mon Sep 17 00:00:00 2001 From: Alexander Luzgarev Date: Sun, 3 Mar 2019 16:23:55 +0100 Subject: [PATCH 4/7] Code cleanup --- MatFileHandler/SubsystemDataReader.cs | 35 ++++++++------------------- 1 file changed, 10 insertions(+), 25 deletions(-) diff --git a/MatFileHandler/SubsystemDataReader.cs b/MatFileHandler/SubsystemDataReader.cs index affe427..9b1b6fc 100644 --- a/MatFileHandler/SubsystemDataReader.cs +++ b/MatFileHandler/SubsystemDataReader.cs @@ -51,7 +51,7 @@ namespace MatFileHandler } var numberOfEmbeddedObjects = (offsets[4] - offsets[3] - 8) / 16; - Dictionary embeddedObjectPositionsToValues = null; + Dictionary> embeddedObjectPositionsToValues = null; using (var stream = new MemoryStream(info, offsets[3], offsets[4] - offsets[3])) { using (var reader = new BinaryReader(stream)) @@ -106,10 +106,9 @@ namespace MatFileHandler string[] fieldNames, Dictionary objectClasses, Dictionary> objectPositionsToValues, - Dictionary embeddedObjectPositionsToValues) + Dictionary> embeddedObjectPositionsToValues) { var classInfos = new Dictionary(); - var newEmbeddedObjectPositionsToValues = new Dictionary>(); foreach (var classId in classIdToName.Keys) { var className = classIdToName[classId]; @@ -136,11 +135,10 @@ namespace MatFileHandler continue; } - fieldIds.Add(embeddedObjectPositionsToValues[objectPosition].FieldIndex); - var d = new Dictionary(); - var embeddedInfo = embeddedObjectPositionsToValues[objectPosition]; - d[embeddedInfo.FieldIndex] = embeddedInfo.ValueIndex; - newEmbeddedObjectPositionsToValues[objectPosition] = d; + foreach (var fieldId in embeddedObjectPositionsToValues[objectPosition].Keys) + { + fieldIds.Add(fieldId); + } } var fieldToIndex = new Dictionary(); @@ -163,7 +161,7 @@ namespace MatFileHandler { var keyValuePair = objectClasses.First(pair => pair.Value.EmbeddedObjectPosition == objectPosition); objectInfos[keyValuePair.Key] = - new SubsystemData.ObjectInfo(newEmbeddedObjectPositionsToValues[objectPosition]); + new SubsystemData.ObjectInfo(embeddedObjectPositionsToValues[objectPosition]); } return (classInfos, objectInfos); @@ -192,10 +190,10 @@ namespace MatFileHandler return result; } - private static Dictionary ReadEmbeddedObjectPositionsToValuesMapping( + private static Dictionary> ReadEmbeddedObjectPositionsToValuesMapping( BinaryReader reader, int numberOfObjects) { - var result = new Dictionary(); + var result = new Dictionary>(); reader.ReadBytes(8); for (var objectPosition = 1; objectPosition <= numberOfObjects; objectPosition++) { @@ -203,7 +201,7 @@ namespace MatFileHandler var fieldIndex = reader.ReadInt32(); var c = reader.ReadInt32(); var valueIndex = reader.ReadInt32(); - result[objectPosition] = new EmbeddedObjectInformation(fieldIndex, valueIndex); + result[objectPosition] = new Dictionary { [fieldIndex] = valueIndex }; } return result; @@ -330,19 +328,6 @@ namespace MatFileHandler return array; } - private struct EmbeddedObjectInformation - { - public EmbeddedObjectInformation(int fieldIndex, int valueIndex) - { - FieldIndex = fieldIndex; - ValueIndex = valueIndex; - } - - public int FieldIndex { get; } - - public int ValueIndex { get; } - } - private struct ObjectClassInformation { public ObjectClassInformation(int embeddedObjectPosition, int objectPosition, int loadingOrder, int classId) From 8fa9d124701267ae7cae851bb0f37f3704d434eb Mon Sep 17 00:00:00 2001 From: Alexander Luzgarev Date: Sun, 3 Mar 2019 16:45:45 +0100 Subject: [PATCH 5/7] Implement DurationAdapter --- MatFileHandler.Tests/MatFileReaderTests.cs | 15 ++++++ .../test-data/good/duration.mat | Bin 0 -> 534 bytes MatFileHandler/DurationAdapter.cs | 44 ++++++++++++++++++ 3 files changed, 59 insertions(+) create mode 100644 MatFileHandler.Tests/test-data/good/duration.mat create mode 100644 MatFileHandler/DurationAdapter.cs diff --git a/MatFileHandler.Tests/MatFileReaderTests.cs b/MatFileHandler.Tests/MatFileReaderTests.cs index bdb91e8..b317cf3 100755 --- a/MatFileHandler.Tests/MatFileReaderTests.cs +++ b/MatFileHandler.Tests/MatFileReaderTests.cs @@ -434,6 +434,21 @@ namespace MatFileHandler.Tests Assert.That(str[3], Is.EqualTo("æøå")); } + /// + /// Test duration objects. + /// + [Test] + public void TestDuration() + { + var matFile = GetTests("good")["duration"]; + var d = matFile["d"].Value as IMatObject; + var duration = new DurationAdapter(d); + Assert.That(duration.Dimensions, Is.EquivalentTo(new[] { 1, 3 })); + Assert.That(duration[0], Is.EqualTo(TimeSpan.FromTicks(12345678L))); + Assert.That(duration[1], Is.EqualTo(new TimeSpan(0, 2, 4))); + Assert.That(duration[2], Is.EqualTo(new TimeSpan(1, 3, 5))); + } + private static AbstractTestDataFactory GetTests(string factoryName) => new MatTestDataFactory(Path.Combine(TestDirectory, factoryName)); diff --git a/MatFileHandler.Tests/test-data/good/duration.mat b/MatFileHandler.Tests/test-data/good/duration.mat new file mode 100644 index 0000000000000000000000000000000000000000..162bbe524bfb761526e502bff222a8d73a6dab69 GIT binary patch literal 534 zcmeZu4DoSvQZUssQ1EpO(M`+DN!3vZ$Vn_o%P-2cQV4Jk_w+L}(NSo>!ow@%=yR6n`|e1`RMd@qtnlvguMc17@m4N z%qX8Z;Yor7L&PO^N051Tj4<;)BqSyzFkg~!ICGMLaZP}ovVivnhYW#)rcaU*wzZBm zb_(kf++L&~S|s15_(&(g(rtxQeER={8J27w&p4c}8Xw9O+Ylu%U%cb4QE|}WZXs^2 zpzcSH7yhz0Q2e6&rujo+*`Xtjui_0JeEZI4^3^B6FioS3;Sr0*Jcc9M3qBc4m_2jy z8Gg_J3Lfk zJd$01^vSL&-!ca7U4NM`2h07iXgagm!Kk18tc@DmmmlpN%DYcD36}3!_}YCte{@xH zf`f1Vi(mc%7y9SL-CS5RAueOjp~b>?ZEqYBzG>r^^UwX#`6gHUP5W6E{ORFYeJ5u1 kqofJ{_}KpZG;d-wm341m=9pMN@iMdOPjME8ea^c70K41GL;wH) literal 0 HcmV?d00001 diff --git a/MatFileHandler/DurationAdapter.cs b/MatFileHandler/DurationAdapter.cs new file mode 100644 index 0000000..c7a6a35 --- /dev/null +++ b/MatFileHandler/DurationAdapter.cs @@ -0,0 +1,44 @@ +// Copyright 2017-2018 Alexander Luzgarev + +using System; + +namespace MatFileHandler +{ + /// + /// A better interface for using duration objects. + /// + public class DurationAdapter + { + private readonly int[] dimensions; + private readonly double[] data; + + /// + /// Initializes a new instance of the class. + /// + /// Source duration object. + public DurationAdapter(IArray array) + { + var matObject = array as IMatObject; + if (matObject?.ClassName != "duration") + { + throw new ArgumentException("The object provided is not a duration."); + } + + var dataObject = matObject["millis", 0]; + data = dataObject.ConvertToDoubleArray(); + dimensions = dataObject.Dimensions; + } + + /// + /// Gets duration array dimensions. + /// + public int[] Dimensions => dimensions; + + /// + /// Gets duration object at given position. + /// + /// Indices. + /// Value. + public TimeSpan this[params int[] list] => TimeSpan.FromTicks((long)(10000.0 * data[Dimensions.DimFlatten(list)])); + } +} \ No newline at end of file From de253bf9c0adae014e020e9a7a1e9cd0a1398ca5 Mon Sep 17 00:00:00 2001 From: Alexander Luzgarev Date: Sun, 3 Mar 2019 16:46:04 +0100 Subject: [PATCH 6/7] Fix typos in XML documentation --- MatFileHandler/StringAdapter.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/MatFileHandler/StringAdapter.cs b/MatFileHandler/StringAdapter.cs index 13cfa1d..afc5f80 100644 --- a/MatFileHandler/StringAdapter.cs +++ b/MatFileHandler/StringAdapter.cs @@ -1,14 +1,12 @@ // Copyright 2017-2018 Alexander Luzgarev using System; -using System.Linq; -using System.Numerics; using System.Text; namespace MatFileHandler { /// - /// A better interface for using datetime objects. + /// A better interface for using string objects. /// public class StringAdapter { @@ -18,7 +16,7 @@ namespace MatFileHandler /// /// Initializes a new instance of the class. /// - /// Source datetime object. + /// Source string object. public StringAdapter(IArray array) { var matObject = array as IMatObject; @@ -33,7 +31,7 @@ namespace MatFileHandler } /// - /// Gets datetime array dimensions. + /// Gets string array dimensions. /// public int[] Dimensions => dimensions; From 9ecad85c2746b5e865c1b21165bf3c9b4f3db659 Mon Sep 17 00:00:00 2001 From: Alexander Luzgarev Date: Sun, 3 Mar 2019 16:53:37 +0100 Subject: [PATCH 7/7] Version bump --- MatFileHandler/MatFileHandler.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MatFileHandler/MatFileHandler.csproj b/MatFileHandler/MatFileHandler.csproj index ebd7993..7152830 100755 --- a/MatFileHandler/MatFileHandler.csproj +++ b/MatFileHandler/MatFileHandler.csproj @@ -1,7 +1,7 @@  netstandard2.0;net461 - 1.3.0-beta2 + 1.3.0-beta3 MatFileHandler A library for reading and writing MATLAB .mat files. Alexander Luzgarev