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 0000000..b86a9c9
Binary files /dev/null and b/MatFileHandler.Tests/test-data/good/string.mat differ
diff --git a/MatFileHandler/StringAdapter.cs b/MatFileHandler/StringAdapter.cs
new file mode 100644
index 0000000..13cfa1d
--- /dev/null
+++ b/MatFileHandler/StringAdapter.cs
@@ -0,0 +1,80 @@
+// 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.
+ ///
+ 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