Files
GSS2Rework/GSS2.Core/Utils/MultiDimensionalArraySerializer.cs
T
amkovkov d0ecc31da2 feat: add MDA serializer
utility for serialization and deserialization of multi-dimensional
arrays
2026-04-06 14:49:25 +03:00

124 lines
4.6 KiB
C#

using System.Text;
using System.IO.Compression;
namespace GSS2.Core.Utils;
// Формат файла MultiDimensionalArray:
// 4 байта [MDA_ fourcc]
// 4 байта [int32 array_rang]
// 4 байта * array_rang [int32 array_shape]
// data compressed with zlib
public static class MultiDimensionalArraySerializer
{
public const string FOURCC = "MDA\0";
public static void Serialize(Stream stream, Array array)
{
var shape = GetShape(array);
long totalLength = GetTotalLength(shape);
using (var writer = new BinaryWriter(stream, Encoding.ASCII, true))
{
writer.Write(Encoding.ASCII.GetBytes(FOURCC));
writer.Write(array.Rank);
foreach (var length in shape)
writer.Write(length);
}
using (var zip = new ZLibStream(stream, CompressionMode.Compress))
using (var writer = new BinaryWriter(zip))
for (long i = 0; i < totalLength; i++)
{
int[] indices = GetIndices(i, shape);
var value = array.GetValue(indices);
switch (value)
{
case bool v: writer.Write(v); break;
case byte v: writer.Write(v); break;
case sbyte v: writer.Write(v); break;
case short v: writer.Write(v); break;
case ushort v: writer.Write(v); break;
case int v: writer.Write(v); break;
case uint v: writer.Write(v); break;
case long v: writer.Write(v); break;
case ulong v: writer.Write(v); break;
case double v: writer.Write(v); break;
case float v: writer.Write(v); break;
default: throw new NotSupportedException();
}
}
}
public static Array Deserialize<T>(Stream stream) where T : unmanaged
{
int rank;
int[] shape;
using (var reader = new BinaryReader(stream, Encoding.ASCII, true))
{
var fourcc = Encoding.ASCII.GetString(reader.ReadBytes(4));
if (fourcc != FOURCC)
throw new FormatException();
rank = reader.ReadInt32();
shape = Enumerable.Range(0, rank)
.Select(_ => reader.ReadInt32())
.ToArray();
}
long totalLength = GetTotalLength(shape);
var array = Array.CreateInstance(typeof(T), shape);
using (var zip = new ZLibStream(stream, CompressionMode.Decompress))
using (var reader = new BinaryReader(zip))
for (long i = 0; i < totalLength; i++)
{
int[] indices = GetIndices(i, shape);
switch (Type.GetTypeCode(typeof(T)))
{
case TypeCode.Boolean: array.SetValue(reader.ReadBoolean(), indices); break;
case TypeCode.Byte: array.SetValue(reader.ReadByte(), indices); break;
case TypeCode.SByte: array.SetValue(reader.ReadSByte(), indices); break;
case TypeCode.Int16: array.SetValue(reader.ReadInt16(), indices); break;
case TypeCode.UInt16: array.SetValue(reader.ReadUInt16(), indices); break;
case TypeCode.Int32: array.SetValue(reader.ReadInt32(), indices); break;
case TypeCode.UInt32: array.SetValue(reader.ReadUInt32(), indices); break;
case TypeCode.Int64: array.SetValue(reader.ReadInt64(), indices); break;
case TypeCode.UInt64: array.SetValue(reader.ReadUInt64(), indices); break;
case TypeCode.Double: array.SetValue(reader.ReadDouble(), indices); break;
case TypeCode.Single: array.SetValue(reader.ReadSingle(), indices); break;
default: throw new NotSupportedException();
}
}
return array;
}
private static int[] GetIndices(long linearIndex, int[] lengths)
{
var indices = new int[lengths.Length];
for (int dim = lengths.Length - 1; dim >= 0; dim--)
{
indices[dim] = (int)(linearIndex % lengths[dim]);
linearIndex /= lengths[dim];
}
return indices;
}
private static long GetTotalLength(params int[] shape)
{
long totalLength = shape.First();
foreach (var length in shape.Skip(1))
totalLength *= length;
return totalLength;
}
private static int[] GetShape(Array array) =>
Enumerable.Range(0, array.Rank)
.Select(array.GetLength)
.ToArray();
}