Implement strings

This commit is contained in:
Alexander Luzgarev 2019-03-03 16:18:36 +01:00
parent 262c3a8314
commit 69599c12b5
5 changed files with 289 additions and 101 deletions

View File

@ -418,6 +418,22 @@ namespace MatFileHandler.Tests
Assert.That(diff - datetime[0] < TimeSpan.FromMilliseconds(1));
}
/// <summary>
/// Test string objects.
/// </summary>
[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<IMatFile> GetTests(string factoryName) =>
new MatTestDataFactory(Path.Combine(TestDirectory, factoryName));

Binary file not shown.

View File

@ -0,0 +1,80 @@
// Copyright 2017-2018 Alexander Luzgarev
using System;
using System.Linq;
using System.Numerics;
using System.Text;
namespace MatFileHandler
{
/// <summary>
/// A better interface for using datetime objects.
/// </summary>
public class StringAdapter
{
private readonly int[] dimensions;
private readonly string[] strings;
/// <summary>
/// Initializes a new instance of the <see cref="StringAdapter"/> class.
/// </summary>
/// <param name="array">Source datetime object.</param>
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<ulong>;
(dimensions, strings) = ParseBinaryData(binaryData.Data);
}
/// <summary>
/// Gets datetime array dimensions.
/// </summary>
public int[] Dimensions => dimensions;
/// <summary>
/// Gets string object at given position.
/// </summary>
/// <param name="list">Indices.</param>
/// <returns>Value.</returns>
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);
}
}
}

View File

@ -125,11 +125,9 @@ namespace MatFileHandler
/// <summary>
/// Initializes a new instance of the <see cref="ObjectInfo"/> class.
/// </summary>
/// <param name="position">Position of object in the object information table.</param>
/// <param name="fieldLinks">A dictionary mapping the field indices to "field values" indices.</param>
public ObjectInfo(int position, Dictionary<int, int> fieldLinks)
public ObjectInfo(Dictionary<int, int> 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.
/// </summary>
public IReadOnlyDictionary<int, int> FieldLinks => fieldLinks;
/// <summary>
/// Gets position of object in the object information table.
/// </summary>
public int Position { get; }
}
}
}

View File

@ -50,8 +50,19 @@ namespace MatFileHandler
}
}
var numberOfEmbeddedObjects = (offsets[4] - offsets[3] - 8) / 16;
Dictionary<int, EmbeddedObjectInformation> 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<int, (int objectPosition, int loadingOrder, int classId)> objectClasses = null;
Dictionary<int, ObjectClassInformation> 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<int, Dictionary<int, int>> 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<int, IArray>();
@ -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<uint> 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<int, (int objectPosition, int loadingOrder, int classId)> ReadObjectClasses(BinaryReader reader, int numberOfObjects)
{
var result = new Dictionary<int, (int, int, int)>();
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<int, SubsystemData.ClassInfo>, Dictionary<int, SubsystemData.ObjectInfo>) GatherClassAndObjectInformation(
Dictionary<int, string> classIdToName,
string[] fieldNames,
Dictionary<int, (int objectPosition, int loadingOrder, int classId)> objectClasses,
Dictionary<int, Dictionary<int, int>> objectPositionsToValues)
private static (Dictionary<int, SubsystemData.ClassInfo>, Dictionary<int, SubsystemData.ObjectInfo>)
GatherClassAndObjectInformation(
Dictionary<int, string> classIdToName,
string[] fieldNames,
Dictionary<int, ObjectClassInformation> objectClasses,
Dictionary<int, Dictionary<int, int>> objectPositionsToValues,
Dictionary<int, EmbeddedObjectInformation> embeddedObjectPositionsToValues)
{
var classInfos = new Dictionary<int, SubsystemData.ClassInfo>();
var newEmbeddedObjectPositionsToValues = new Dictionary<int, Dictionary<int, int>>();
foreach (var classId in classIdToName.Keys)
{
var className = classIdToName[classId];
var fieldIds = new SortedSet<int>();
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<int, int>();
var embeddedInfo = embeddedObjectPositionsToValues[objectPosition];
d[embeddedInfo.FieldIndex] = embeddedInfo.ValueIndex;
newEmbeddedObjectPositionsToValues[objectPosition] = d;
}
var fieldToIndex = new Dictionary<string, int>();
foreach (var fieldId in fieldIds)
{
fieldToIndex[fieldNames[fieldId - 1]] = fieldId;
}
classInfos[classId] = new SubsystemData.ClassInfo(className, fieldToIndex);
}
var objectInfos = new Dictionary<int, SubsystemData.ObjectInfo>();
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<int, int> ReadFieldToFieldDataMapping(BinaryReader reader)
{
var length = reader.ReadInt32();
var result = new Dictionary<int, int>();
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<int, Dictionary<int, int>> ReadObjectPositionsToValuesMapping(BinaryReader reader, int numberOfValues)
{
var result = new Dictionary<int, Dictionary<int, int>>();
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<int, string> ReadClassNames(
BinaryReader reader,
string[] fieldNames,
@ -221,6 +192,98 @@ namespace MatFileHandler
return result;
}
private static Dictionary<int, EmbeddedObjectInformation> ReadEmbeddedObjectPositionsToValuesMapping(
BinaryReader reader, int numberOfObjects)
{
var result = new Dictionary<int, EmbeddedObjectInformation>();
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<byte>();
while (bytes[position] != 0)
{
list.Add(bytes[position]);
position++;
}
result[i] = Encoding.ASCII.GetString(list.ToArray());
position++;
}
return result;
}
private static Dictionary<int, int> ReadFieldToFieldDataMapping(BinaryReader reader)
{
var length = reader.ReadInt32();
var result = new Dictionary<int, int>();
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<int, ObjectClassInformation> ReadObjectClasses(
BinaryReader reader,
int numberOfObjects)
{
var result = new Dictionary<int, ObjectClassInformation>();
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<int, Dictionary<int, int>> ReadObjectPositionsToValuesMapping(
BinaryReader reader,
int numberOfObjects)
{
var result = new Dictionary<int, Dictionary<int, int>>();
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<uint> uintArray)
{
var list = new List<byte>();
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; }
}
}
}
}