392 lines
14 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
namespace MatFileHandler
{
/// <summary>
/// A simulated writer of .mat files that just calculate the length of data that would be written.
/// </summary>
internal class FakeWriter
{
/// <summary>
/// Gets current position of the writer.
/// </summary>
public int Position { get; private set; }
/// <summary>
/// Write contents of a numerical array.
/// </summary>
/// <param name="array">A numerical array.</param>
/// <param name="name">Name of the array.</param>
public void WriteNumericalArrayContents(IArray array, string name)
{
WriteArrayFlags();
WriteDimensions(array.Dimensions);
WriteName(name);
WriteNumericalArrayValues(array);
}
/// <summary>
/// Write contents of a char array.
/// </summary>
/// <param name="charArray">A char array.</param>
/// <param name="name">Name of the array.</param>
public void WriteCharArrayContents(ICharArray charArray, string name)
{
WriteArrayFlags();
WriteDimensions(charArray.Dimensions);
WriteName(name);
WriteDataElement(GetLengthOfByteArray<ushort>(charArray.String.Length));
}
/// <summary>
/// Write contents of a sparse array.
/// </summary>
/// <typeparam name="T">Array element type.</typeparam>
/// <param name="array">A sparse array.</param>
/// <param name="name">Name of the array.</param>
public void WriteSparseArrayContents<T>(
ISparseArrayOf<T> array,
string name)
where T : unmanaged, IEquatable<T>
{
(var rowsLength, var columnsLength, var dataLength, var nonZero) = PrepareSparseArrayData(array);
WriteSparseArrayFlags();
WriteDimensions(array.Dimensions);
WriteName(name);
WriteSparseArrayValues<T>(rowsLength, columnsLength, dataLength);
}
/// <summary>
/// Write contents of a structure array.
/// </summary>
/// <param name="array">A structure array.</param>
/// <param name="name">Name of the array.</param>
public void WriteStructureArrayContents(IStructureArray array, string name)
{
WriteArrayFlags();
WriteDimensions(array.Dimensions);
WriteName(name);
WriteFieldNames(array.FieldNames);
WriteStructureArrayValues(array);
}
/// <summary>
/// Write contents of a cell array.
/// </summary>
/// <param name="array">A cell array.</param>
/// <param name="name">Name of the array.</param>
public void WriteCellArrayContents(ICellArray array, string name)
{
WriteArrayFlags();
WriteDimensions(array.Dimensions);
WriteName(name);
WriteCellArrayValues(array);
}
private void WriteTag()
{
Position += 8;
}
private void WriteShortTag()
{
Position += 4;
}
private void WriteWrappingContents<T>(T array, Action<FakeWriter> writeContents)
where T : IArray
{
if (array.IsEmpty)
{
WriteTag();
return;
}
WriteTag();
writeContents(this);
}
private void WriteNumericalArrayValues(IArray value)
{
switch (value)
{
case IArrayOf<sbyte> sbyteArray:
WriteDataElement(GetLengthOfByteArray<sbyte>(sbyteArray.Data.Length));
break;
case IArrayOf<byte> byteArray:
WriteDataElement(GetLengthOfByteArray<byte>(byteArray.Data.Length));
break;
case IArrayOf<short> shortArray:
WriteDataElement(GetLengthOfByteArray<short>(shortArray.Data.Length));
break;
case IArrayOf<ushort> ushortArray:
WriteDataElement(GetLengthOfByteArray<ushort>(ushortArray.Data.Length));
break;
case IArrayOf<int> intArray:
WriteDataElement(GetLengthOfByteArray<int>(intArray.Data.Length));
break;
case IArrayOf<uint> uintArray:
WriteDataElement(GetLengthOfByteArray<uint>(uintArray.Data.Length));
break;
case IArrayOf<long> longArray:
WriteDataElement(GetLengthOfByteArray<long>(longArray.Data.Length));
break;
case IArrayOf<ulong> ulongArray:
WriteDataElement(GetLengthOfByteArray<ulong>(ulongArray.Data.Length));
break;
case IArrayOf<float> floatArray:
WriteDataElement(GetLengthOfByteArray<float>(floatArray.Data.Length));
break;
case IArrayOf<double> doubleArray:
WriteDataElement(GetLengthOfByteArray<double>(doubleArray.Data.Length));
break;
case IArrayOf<bool> boolArray:
WriteDataElement(boolArray.Data.Length);
break;
case IArrayOf<ComplexOf<sbyte>> complexSbyteArray:
WriteComplexValues(GetLengthOfPairOfByteArrays(complexSbyteArray.Data));
break;
case IArrayOf<ComplexOf<byte>> complexByteArray:
WriteComplexValues(GetLengthOfPairOfByteArrays(complexByteArray.Data));
break;
case IArrayOf<ComplexOf<short>> complexShortArray:
WriteComplexValues(GetLengthOfPairOfByteArrays(complexShortArray.Data));
break;
case IArrayOf<ComplexOf<ushort>> complexUshortArray:
WriteComplexValues(GetLengthOfPairOfByteArrays(complexUshortArray.Data));
break;
case IArrayOf<ComplexOf<int>> complexIntArray:
WriteComplexValues(GetLengthOfPairOfByteArrays(complexIntArray.Data));
break;
case IArrayOf<ComplexOf<uint>> complexUintArray:
WriteComplexValues(GetLengthOfPairOfByteArrays(complexUintArray.Data));
break;
case IArrayOf<ComplexOf<long>> complexLongArray:
WriteComplexValues(GetLengthOfPairOfByteArrays(complexLongArray.Data));
break;
case IArrayOf<ComplexOf<ulong>> complexUlongArray:
WriteComplexValues(GetLengthOfPairOfByteArrays(complexUlongArray.Data));
break;
case IArrayOf<ComplexOf<float>> complexFloatArray:
WriteComplexValues(GetLengthOfPairOfByteArrays(complexFloatArray.Data));
break;
case IArrayOf<Complex> complexDoubleArray:
WriteComplexValues(GetLengthOfPairOfByteArrays(complexDoubleArray.Data));
break;
default:
throw new NotSupportedException();
}
}
private void WriteName(string name)
{
var nameBytes = Encoding.ASCII.GetBytes(name);
WriteDataElement(nameBytes.Length);
}
private void WriteArrayFlags()
{
WriteTag();
Position += 8;
}
private void WriteDimensions(int[] dimensions)
{
var buffer = GetLengthOfByteArray<int>(dimensions.Length);
WriteDataElement(buffer);
}
private static unsafe int GetLengthOfByteArray<T>(int dataLength)
where T : unmanaged
{
return dataLength * sizeof(T);
}
private static unsafe int GetLengthOfPairOfByteArrays<T>(ComplexOf<T>[] data)
where T : unmanaged
{
return data.Length * sizeof(T);
}
private static unsafe int GetLengthOfPairOfByteArrays(Complex[] data)
{
return data.Length * sizeof(double);
}
private static int CalculatePadding(int length)
{
var rem = length % 8;
return rem == 0 ? 0 : 8 - rem;
}
private void WriteDataElement(int dataLength)
{
var maybePadding = 0;
if (dataLength > 4)
{
WriteTag();
Position += dataLength;
maybePadding = CalculatePadding(dataLength + 8);
}
else
{
WriteShortTag();
Position += 4;
}
Position += maybePadding;
}
private void WriteComplexValues(
int dataLength)
{
WriteDataElement(dataLength);
WriteDataElement(dataLength);
}
private void WriteSparseArrayValues<T>(int rowsLength, int columnsLength, int dataLength)
where T : unmanaged
{
WriteDataElement(GetLengthOfByteArray<int>(rowsLength));
WriteDataElement(GetLengthOfByteArray<int>(columnsLength));
if (typeof(T) == typeof(double))
{
WriteDataElement(GetLengthOfByteArray<double>(dataLength));
}
else if (typeof(T) == typeof(Complex))
{
WriteDataElement(GetLengthOfByteArray<double>(dataLength));
WriteDataElement(GetLengthOfByteArray<double>(dataLength));
}
else if (typeof(T) == typeof(bool))
{
WriteDataElement(dataLength);
}
}
private static (int rowIndexLength, int columnIndexLength, int dataLength, uint nonZero) PrepareSparseArrayData<T>(
ISparseArrayOf<T> array)
where T : struct, IEquatable<T>
{
var numberOfColumns = array.Dimensions[1];
var numberOfElements = array.Data.Values.Count(value => !value.Equals(default));
return (numberOfElements, numberOfColumns + 1, numberOfElements, (uint)numberOfElements);
}
private void WriteSparseArrayFlags()
{
WriteTag();
Position += 8;
}
private void WriteFieldNames(IEnumerable<string> fieldNames)
{
var fieldNamesArray = fieldNames.Select(name => Encoding.ASCII.GetBytes(name)).ToArray();
var maxFieldName = fieldNamesArray.Max(name => name.Length) + 1;
WriteDataElement(GetLengthOfByteArray<int>(1));
var buffer = new byte[fieldNamesArray.Length * maxFieldName];
var startPosition = 0;
foreach (var name in fieldNamesArray)
{
for (var i = 0; i < name.Length; i++)
{
buffer[startPosition + i] = name[i];
}
startPosition += maxFieldName;
}
WriteDataElement(buffer.Length);
}
private void WriteStructureArrayValues(IStructureArray array)
{
for (var i = 0; i < array.Count; i++)
{
foreach (var name in array.FieldNames)
{
WriteArray(array[name, i]);
}
}
}
private void WriteArray(IArray array, string variableName = "", bool isGlobal = false)
{
switch (array)
{
case ICharArray charArray:
WriteCharArray(charArray, variableName);
break;
case ISparseArrayOf<double> doubleSparseArray:
WriteSparseArray(doubleSparseArray, variableName);
break;
case ISparseArrayOf<Complex> complexSparseArray:
WriteSparseArray(complexSparseArray, variableName);
break;
case ISparseArrayOf<bool> boolSparseArray:
WriteSparseArray(boolSparseArray, variableName);
break;
case ICellArray cellArray:
WriteCellArray(cellArray, variableName);
break;
case IStructureArray structureArray:
WriteStructureArray(structureArray, variableName);
break;
default:
WriteNumericalArray(array, variableName);
break;
}
}
private void WriteCharArray(ICharArray charArray, string name)
{
WriteWrappingContents(
charArray,
fakeWriter => fakeWriter.WriteCharArrayContents(charArray, name));
}
private void WriteSparseArray<T>(ISparseArrayOf<T> sparseArray, string name)
where T : unmanaged, IEquatable<T>
{
WriteWrappingContents(
sparseArray,
fakeWriter => fakeWriter.WriteSparseArrayContents(sparseArray, name));
}
private void WriteCellArray(ICellArray cellArray, string name)
{
WriteWrappingContents(
cellArray,
fakeWriter => fakeWriter.WriteCellArrayContents(cellArray, name));
}
private void WriteCellArrayValues(ICellArray array)
{
for (var i = 0; i < array.Count; i++)
{
WriteArray(array[i]);
}
}
private void WriteStructureArray(
IStructureArray structureArray,
string name)
{
WriteWrappingContents(
structureArray,
fakeWriter => fakeWriter.WriteStructureArrayContents(structureArray, name));
}
private void WriteNumericalArray(
IArray numericalArray,
string name = "")
{
WriteWrappingContents(
numericalArray,
fakeWriter => fakeWriter.WriteNumericalArrayContents(numericalArray, name));
}
}
}