Compare commits

..

No commits in common. "span" and "master" have entirely different histories.
span ... master

6 changed files with 124 additions and 126 deletions

View File

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net462;net472;net8.0</TargetFrameworks>
<TargetFrameworks>net461;net472;net8.0</TargetFrameworks>
<IsPackable>false</IsPackable>
<LangVersion>latest</LangVersion>
</PropertyGroup>
@ -10,7 +10,11 @@
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
<PackageReference Include="xunit" Version="2.9.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
<PackageReference Condition=" '$(TargetFramework)' != 'net461' " Include="xunit.runner.visualstudio" Version="2.8.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Condition=" '$(TargetFramework)' == 'net461' " Include="xunit.runner.visualstudio" Version="2.4.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

View File

@ -1,10 +1,8 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
namespace MatFileHandler
@ -146,18 +144,17 @@ namespace MatFileHandler
return Encoding.ASCII.GetString(element.Data.Select(x => (byte)x).ToArray());
}
private static MiNum<T> ReadNum<T>(Tag tag, BinaryReader reader)
where T : unmanaged
private static DataElement ReadNum<T>(Tag tag, BinaryReader reader)
where T : struct
{
T[] result;
unsafe
var bytes = reader.ReadBytes(tag.Length);
if (tag.Type == DataType.MiUInt8)
{
Debug.Assert(tag.ElementSize == sizeof(T));
result = new T[tag.Length / sizeof(T)];
return new MiNum<byte>(bytes);
}
reader.BaseStream.ReadExactly(MemoryMarshal.AsBytes<T>(result));
var result = new T[bytes.Length / tag.ElementSize];
Buffer.BlockCopy(bytes, 0, result, 0, bytes.Length);
return new MiNum<T>(result);
}

View File

@ -2,7 +2,7 @@ using System;
using System.Globalization;
using System.IO;
using System.Linq;
#if !NET462
#if !NET461
using System.Runtime.InteropServices;
#endif
@ -79,7 +79,7 @@ namespace MatFileHandler
private static string GetOperatingSystem()
{
#if NET462
#if NET461
return "Windows";
#else
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))

View File

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net462;net8.0</TargetFrameworks>
<TargetFrameworks>netstandard2.0;net461;net472</TargetFrameworks>
<PackageVersion>1.4.0-beta6</PackageVersion>
<PackageId>MatFileHandler</PackageId>
<Title>A library for reading and writing MATLAB .mat files.</Title>
@ -28,7 +28,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Bcl.Memory" Version="9.0.7" Condition="!$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net8.0'))" />
<PackageReference Include="System.ValueTuple" Version="4.4.0" Condition="'$(TargetFramework)' == 'net461'" />
</ItemGroup>
<ItemGroup>

View File

@ -1,12 +1,9 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Text;
namespace MatFileHandler
@ -117,21 +114,13 @@ namespace MatFileHandler
writer.Write((short)tag.Length);
}
private static void WriteDataElement<T>(BinaryWriter writer, DataType type, T[] data)
where T : unmanaged
private static void WriteDataElement(BinaryWriter writer, DataType type, byte[] data)
{
unsafe
if (data.Length > 4)
{
Debug.Assert(type.Size() == sizeof(T));
}
ReadOnlySpan<byte> bytes = MemoryMarshal.AsBytes<T>(data);
if (bytes.Length > 4)
{
WriteTag(writer, new Tag(type, bytes.Length));
writer.Write(bytes);
var rem = bytes.Length % 8;
WriteTag(writer, new Tag(type, data.Length));
writer.Write(data);
var rem = data.Length % 8;
if (rem > 0)
{
var padding = new byte[8 - rem];
@ -140,11 +129,11 @@ namespace MatFileHandler
}
else
{
WriteShortTag(writer, new Tag(type, bytes.Length));
writer.Write(bytes);
if (bytes.Length < 4)
WriteShortTag(writer, new Tag(type, data.Length));
writer.Write(data);
if (data.Length < 4)
{
var padding = new byte[4 - bytes.Length];
var padding = new byte[4 - data.Length];
writer.Write(padding);
}
}
@ -152,22 +141,81 @@ namespace MatFileHandler
private static void WriteDimensions(BinaryWriter writer, int[] dimensions)
{
WriteDataElement(writer, DataType.MiInt32, dimensions);
var buffer = ConvertToByteArray(dimensions);
WriteDataElement(writer, DataType.MiInt32, buffer);
}
private static (T[] real, T[] imaginary) ConvertToPairOfArrays<T>(ComplexOf<T>[] data)
private static byte[] ConvertToByteArray<T>(T[] data)
where T : struct
{
return (data.Select(x => x.Real).ToArray(), data.Select(x => x.Imaginary).ToArray());
}
private static (double[] real, double[] imaginary) ConvertToPairOfArrays(Complex[] data)
int size;
if (typeof(T) == typeof(sbyte))
{
return (data.Select(x => x.Real).ToArray(), data.Select(x => x.Imaginary).ToArray());
size = sizeof(sbyte);
}
else if (typeof(T) == typeof(byte))
{
size = sizeof(byte);
}
else if (typeof(T) == typeof(short))
{
size = sizeof(short);
}
else if (typeof(T) == typeof(ushort))
{
size = sizeof(ushort);
}
else if (typeof(T) == typeof(int))
{
size = sizeof(int);
}
else if (typeof(T) == typeof(uint))
{
size = sizeof(uint);
}
else if (typeof(T) == typeof(long))
{
size = sizeof(long);
}
else if (typeof(T) == typeof(ulong))
{
size = sizeof(ulong);
}
else if (typeof(T) == typeof(float))
{
size = sizeof(float);
}
else if (typeof(T) == typeof(double))
{
size = sizeof(double);
}
else if (typeof(T) == typeof(bool))
{
size = sizeof(bool);
}
else
{
throw new NotSupportedException();
}
var buffer = new byte[data.Length * size];
Buffer.BlockCopy(data, 0, buffer, 0, buffer.Length);
return buffer;
}
private static void WriteComplexValues<T>(BinaryWriter writer, DataType type, (T[] real, T[] complex) data)
where T : unmanaged
private static (byte[] real, byte[] imaginary) ConvertToPairOfByteArrays<T>(ComplexOf<T>[] data)
where T : struct
{
return (ConvertToByteArray(data.Select(x => x.Real).ToArray()),
ConvertToByteArray(data.Select(x => x.Imaginary).ToArray()));
}
private static (byte[] real, byte[] imaginary) ConvertToPairOfByteArrays(Complex[] data)
{
return (ConvertToByteArray(data.Select(x => x.Real).ToArray()),
ConvertToByteArray(data.Select(x => x.Imaginary).ToArray()));
}
private static void WriteComplexValues(BinaryWriter writer, DataType type, (byte[] real, byte[] complex) data)
{
WriteDataElement(writer, type, data.real);
WriteDataElement(writer, type, data.complex);
@ -203,67 +251,67 @@ namespace MatFileHandler
switch (value)
{
case IArrayOf<sbyte> sbyteArray:
WriteDataElement(writer, DataType.MiInt8, sbyteArray.Data);
WriteDataElement(writer, DataType.MiInt8, ConvertToByteArray(sbyteArray.Data));
break;
case IArrayOf<byte> byteArray:
WriteDataElement(writer, DataType.MiUInt8, byteArray.Data);
WriteDataElement(writer, DataType.MiUInt8, ConvertToByteArray(byteArray.Data));
break;
case IArrayOf<short> shortArray:
WriteDataElement(writer, DataType.MiInt16, shortArray.Data);
WriteDataElement(writer, DataType.MiInt16, ConvertToByteArray(shortArray.Data));
break;
case IArrayOf<ushort> ushortArray:
WriteDataElement(writer, DataType.MiUInt16, ushortArray.Data);
WriteDataElement(writer, DataType.MiUInt16, ConvertToByteArray(ushortArray.Data));
break;
case IArrayOf<int> intArray:
WriteDataElement(writer, DataType.MiInt32, intArray.Data);
WriteDataElement(writer, DataType.MiInt32, ConvertToByteArray(intArray.Data));
break;
case IArrayOf<uint> uintArray:
WriteDataElement(writer, DataType.MiUInt32, uintArray.Data);
WriteDataElement(writer, DataType.MiUInt32, ConvertToByteArray(uintArray.Data));
break;
case IArrayOf<long> longArray:
WriteDataElement(writer, DataType.MiInt64, longArray.Data);
WriteDataElement(writer, DataType.MiInt64, ConvertToByteArray(longArray.Data));
break;
case IArrayOf<ulong> ulongArray:
WriteDataElement(writer, DataType.MiUInt64, ulongArray.Data);
WriteDataElement(writer, DataType.MiUInt64, ConvertToByteArray(ulongArray.Data));
break;
case IArrayOf<float> floatArray:
WriteDataElement(writer, DataType.MiSingle, floatArray.Data);
WriteDataElement(writer, DataType.MiSingle, ConvertToByteArray(floatArray.Data));
break;
case IArrayOf<double> doubleArray:
WriteDataElement(writer, DataType.MiDouble, doubleArray.Data);
WriteDataElement(writer, DataType.MiDouble, ConvertToByteArray(doubleArray.Data));
break;
case IArrayOf<bool> boolArray:
WriteDataElement(writer, DataType.MiUInt8, boolArray.Data);
WriteDataElement(writer, DataType.MiUInt8, ConvertToByteArray(boolArray.Data));
break;
case IArrayOf<ComplexOf<sbyte>> complexSbyteArray:
WriteComplexValues(writer, DataType.MiInt8, ConvertToPairOfArrays(complexSbyteArray.Data));
WriteComplexValues(writer, DataType.MiInt8, ConvertToPairOfByteArrays(complexSbyteArray.Data));
break;
case IArrayOf<ComplexOf<byte>> complexByteArray:
WriteComplexValues(writer, DataType.MiUInt8, ConvertToPairOfArrays(complexByteArray.Data));
WriteComplexValues(writer, DataType.MiUInt8, ConvertToPairOfByteArrays(complexByteArray.Data));
break;
case IArrayOf<ComplexOf<short>> complexShortArray:
WriteComplexValues(writer, DataType.MiInt16, ConvertToPairOfArrays(complexShortArray.Data));
WriteComplexValues(writer, DataType.MiInt16, ConvertToPairOfByteArrays(complexShortArray.Data));
break;
case IArrayOf<ComplexOf<ushort>> complexUshortArray:
WriteComplexValues(writer, DataType.MiUInt16, ConvertToPairOfArrays(complexUshortArray.Data));
WriteComplexValues(writer, DataType.MiUInt16, ConvertToPairOfByteArrays(complexUshortArray.Data));
break;
case IArrayOf<ComplexOf<int>> complexIntArray:
WriteComplexValues(writer, DataType.MiInt32, ConvertToPairOfArrays(complexIntArray.Data));
WriteComplexValues(writer, DataType.MiInt32, ConvertToPairOfByteArrays(complexIntArray.Data));
break;
case IArrayOf<ComplexOf<uint>> complexUintArray:
WriteComplexValues(writer, DataType.MiUInt32, ConvertToPairOfArrays(complexUintArray.Data));
WriteComplexValues(writer, DataType.MiUInt32, ConvertToPairOfByteArrays(complexUintArray.Data));
break;
case IArrayOf<ComplexOf<long>> complexLongArray:
WriteComplexValues(writer, DataType.MiInt64, ConvertToPairOfArrays(complexLongArray.Data));
WriteComplexValues(writer, DataType.MiInt64, ConvertToPairOfByteArrays(complexLongArray.Data));
break;
case IArrayOf<ComplexOf<ulong>> complexUlongArray:
WriteComplexValues(writer, DataType.MiUInt64, ConvertToPairOfArrays(complexUlongArray.Data));
WriteComplexValues(writer, DataType.MiUInt64, ConvertToPairOfByteArrays(complexUlongArray.Data));
break;
case IArrayOf<ComplexOf<float>> complexFloatArray:
WriteComplexValues(writer, DataType.MiSingle, ConvertToPairOfArrays(complexFloatArray.Data));
WriteComplexValues(writer, DataType.MiSingle, ConvertToPairOfByteArrays(complexFloatArray.Data));
break;
case IArrayOf<Complex> complexDoubleArray:
WriteComplexValues(writer, DataType.MiDouble, ConvertToPairOfArrays(complexDoubleArray.Data));
WriteComplexValues(writer, DataType.MiDouble, ConvertToPairOfByteArrays(complexDoubleArray.Data));
break;
default:
throw new NotSupportedException();
@ -368,7 +416,8 @@ namespace MatFileHandler
WriteArrayFlags(writer, GetCharArrayFlags(isGlobal));
WriteDimensions(writer, charArray.Dimensions);
WriteName(writer, name);
WriteDataElement(writer, DataType.MiUtf16, charArray.String.ToCharArray());
var array = charArray.String.ToCharArray().Select(c => (ushort)c).ToArray();
WriteDataElement(writer, DataType.MiUtf16, ConvertToByteArray(array));
}
private static void WriteCharArray(BinaryWriter writer, ICharArray charArray, string name, bool isGlobal)
@ -382,31 +431,31 @@ namespace MatFileHandler
private static void WriteSparseArrayValues<T>(
BinaryWriter writer, int[] rows, int[] columns, T[] data)
where T : unmanaged
where T : struct
{
WriteDataElement(writer, DataType.MiInt32, rows);
WriteDataElement(writer, DataType.MiInt32, columns);
WriteDataElement(writer, DataType.MiInt32, ConvertToByteArray(rows));
WriteDataElement(writer, DataType.MiInt32, ConvertToByteArray(columns));
if (data is double[])
{
WriteDataElement(writer, DataType.MiDouble, data);
WriteDataElement(writer, DataType.MiDouble, ConvertToByteArray(data));
}
else if (data is Complex[] complexData)
{
WriteDataElement(
writer,
DataType.MiDouble,
complexData.Select(c => c.Real).ToArray());
ConvertToByteArray(complexData.Select(c => c.Real).ToArray()));
WriteDataElement(
writer,
DataType.MiDouble,
complexData.Select(c => c.Imaginary).ToArray());
ConvertToByteArray(complexData.Select(c => c.Imaginary).ToArray()));
}
else if (data is bool[] boolData)
{
WriteDataElement(
writer,
DataType.MiUInt8,
boolData);
ConvertToByteArray(boolData));
}
}
@ -438,7 +487,7 @@ namespace MatFileHandler
ISparseArrayOf<T> array,
string name,
bool isGlobal)
where T : unmanaged, IEquatable<T>
where T : struct, IEquatable<T>
{
(var rows, var columns, var data, var nonZero) = PrepareSparseArrayData(array);
WriteSparseArrayFlags(writer, GetSparseArrayFlags(array, isGlobal, nonZero));
@ -461,7 +510,7 @@ namespace MatFileHandler
{
var fieldNamesArray = fieldNames.Select(name => Encoding.ASCII.GetBytes(name)).ToArray();
var maxFieldName = fieldNamesArray.Max(name => name.Length) + 1;
WriteDataElement(writer, DataType.MiInt32, [maxFieldName]);
WriteDataElement(writer, DataType.MiInt32, ConvertToByteArray([maxFieldName]));
var buffer = new byte[fieldNamesArray.Length * maxFieldName];
var startPosition = 0;
foreach (var name in fieldNamesArray)

View File

@ -1,52 +0,0 @@
#if !NET
using System;
using System.Buffers;
using System.IO;
namespace MatFileHandler
{
/// <summary>
/// Polyfills for methods that do not exist on lower targets.
/// </summary>
internal static class PolyfillExtensions
{
public static void ReadExactly(this Stream stream, Span<byte> buffer)
{
var array = ArrayPool<byte>.Shared.Rent(buffer.Length);
stream.ReadExactly(array, 0, buffer.Length);
array.AsSpan(0, buffer.Length).CopyTo(buffer);
ArrayPool<byte>.Shared.Return(array);
}
public static void ReadExactly(this Stream stream, byte[] buffer, int offset, int count)
{
var totalRead = 0;
while (totalRead < count)
{
var read = stream.Read(buffer, offset + totalRead, count - totalRead);
if (read == 0)
{
throw new EndOfStreamException();
}
totalRead += read;
}
}
public static void Write(this BinaryWriter writer, ReadOnlySpan<byte> buffer)
{
var array = ArrayPool<byte>.Shared.Rent(buffer.Length);
buffer.CopyTo(array);
writer.Write(array, 0, buffer.Length);
ArrayPool<byte>.Shared.Return(array);
}
}
}
#endif