using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
namespace MatFileHandler
{
///
/// A simulated writer of .mat files that just calculate the length of data that would be written.
///
internal class FakeWriter
{
///
/// Gets current position of the writer.
///
public int Position { get; private set; }
///
/// Write contents of a numerical array.
///
/// A numerical array.
/// Name of the array.
public void WriteNumericalArrayContents(IArray array, string name)
{
WriteArrayFlags();
WriteDimensions(array.Dimensions);
WriteName(name);
WriteNumericalArrayValues(array);
}
///
/// Write contents of a char array.
///
/// A char array.
/// Name of the array.
public void WriteCharArrayContents(ICharArray charArray, string name)
{
WriteArrayFlags();
WriteDimensions(charArray.Dimensions);
WriteName(name);
WriteDataElement(GetLengthOfByteArray(charArray.String.Length));
}
///
/// Write contents of a sparse array.
///
/// Array element type.
/// A sparse array.
/// Name of the array.
public void WriteSparseArrayContents(
ISparseArrayOf array,
string name)
where T : unmanaged, IEquatable
{
(var rowsLength, var columnsLength, var dataLength, var nonZero) = PrepareSparseArrayData(array);
WriteSparseArrayFlags();
WriteDimensions(array.Dimensions);
WriteName(name);
WriteSparseArrayValues(rowsLength, columnsLength, dataLength);
}
///
/// Write contents of a structure array.
///
/// A structure array.
/// Name of the array.
public void WriteStructureArrayContents(IStructureArray array, string name)
{
WriteArrayFlags();
WriteDimensions(array.Dimensions);
WriteName(name);
WriteFieldNames(array.FieldNames);
WriteStructureArrayValues(array);
}
///
/// Write contents of a cell array.
///
/// A cell array.
/// Name of the array.
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 array, Action writeContents)
where T : IArray
{
if (array.IsEmpty)
{
WriteTag();
return;
}
WriteTag();
writeContents(this);
}
private void WriteNumericalArrayValues(IArray value)
{
switch (value)
{
case IArrayOf sbyteArray:
WriteDataElement(GetLengthOfByteArray(sbyteArray.Data.Length));
break;
case IArrayOf byteArray:
WriteDataElement(GetLengthOfByteArray(byteArray.Data.Length));
break;
case IArrayOf shortArray:
WriteDataElement(GetLengthOfByteArray(shortArray.Data.Length));
break;
case IArrayOf ushortArray:
WriteDataElement(GetLengthOfByteArray(ushortArray.Data.Length));
break;
case IArrayOf intArray:
WriteDataElement(GetLengthOfByteArray(intArray.Data.Length));
break;
case IArrayOf uintArray:
WriteDataElement(GetLengthOfByteArray(uintArray.Data.Length));
break;
case IArrayOf longArray:
WriteDataElement(GetLengthOfByteArray(longArray.Data.Length));
break;
case IArrayOf ulongArray:
WriteDataElement(GetLengthOfByteArray(ulongArray.Data.Length));
break;
case IArrayOf floatArray:
WriteDataElement(GetLengthOfByteArray(floatArray.Data.Length));
break;
case IArrayOf doubleArray:
WriteDataElement(GetLengthOfByteArray(doubleArray.Data.Length));
break;
case IArrayOf boolArray:
WriteDataElement(boolArray.Data.Length);
break;
case IArrayOf> complexSbyteArray:
WriteComplexValues(GetLengthOfPairOfByteArrays(complexSbyteArray.Data));
break;
case IArrayOf> complexByteArray:
WriteComplexValues(GetLengthOfPairOfByteArrays(complexByteArray.Data));
break;
case IArrayOf> complexShortArray:
WriteComplexValues(GetLengthOfPairOfByteArrays(complexShortArray.Data));
break;
case IArrayOf> complexUshortArray:
WriteComplexValues(GetLengthOfPairOfByteArrays(complexUshortArray.Data));
break;
case IArrayOf> complexIntArray:
WriteComplexValues(GetLengthOfPairOfByteArrays(complexIntArray.Data));
break;
case IArrayOf> complexUintArray:
WriteComplexValues(GetLengthOfPairOfByteArrays(complexUintArray.Data));
break;
case IArrayOf> complexLongArray:
WriteComplexValues(GetLengthOfPairOfByteArrays(complexLongArray.Data));
break;
case IArrayOf> complexUlongArray:
WriteComplexValues(GetLengthOfPairOfByteArrays(complexUlongArray.Data));
break;
case IArrayOf> complexFloatArray:
WriteComplexValues(GetLengthOfPairOfByteArrays(complexFloatArray.Data));
break;
case IArrayOf 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(dimensions.Length);
WriteDataElement(buffer);
}
private static unsafe int GetLengthOfByteArray(int dataLength)
where T : unmanaged
{
return dataLength * sizeof(T);
}
private static unsafe int GetLengthOfPairOfByteArrays(ComplexOf[] 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(int rowsLength, int columnsLength, int dataLength)
where T : unmanaged
{
WriteDataElement(GetLengthOfByteArray(rowsLength));
WriteDataElement(GetLengthOfByteArray(columnsLength));
if (typeof(T) == typeof(double))
{
WriteDataElement(GetLengthOfByteArray(dataLength));
}
else if (typeof(T) == typeof(Complex))
{
WriteDataElement(GetLengthOfByteArray(dataLength));
WriteDataElement(GetLengthOfByteArray(dataLength));
}
else if (typeof(T) == typeof(bool))
{
WriteDataElement(dataLength);
}
}
private static (int rowIndexLength, int columnIndexLength, int dataLength, uint nonZero) PrepareSparseArrayData(
ISparseArrayOf array)
where T : struct, IEquatable
{
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 fieldNames)
{
var fieldNamesArray = fieldNames.Select(name => Encoding.ASCII.GetBytes(name)).ToArray();
var maxFieldName = fieldNamesArray.Max(name => name.Length) + 1;
WriteDataElement(GetLengthOfByteArray(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 doubleSparseArray:
WriteSparseArray(doubleSparseArray, variableName);
break;
case ISparseArrayOf complexSparseArray:
WriteSparseArray(complexSparseArray, variableName);
break;
case ISparseArrayOf 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(ISparseArrayOf sparseArray, string name)
where T : unmanaged, IEquatable
{
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));
}
}
}