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)); } } }