From d0ecc31da2817069121b98e1229b6a71bba577ab Mon Sep 17 00:00:00 2001 From: Alek-ban Date: Mon, 6 Apr 2026 14:49:25 +0300 Subject: [PATCH] feat: add MDA serializer utility for serialization and deserialization of multi-dimensional arrays --- .../Utils/MultiDimensionalArraySerializer.cs | 123 ++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 GSS2.Core/Utils/MultiDimensionalArraySerializer.cs diff --git a/GSS2.Core/Utils/MultiDimensionalArraySerializer.cs b/GSS2.Core/Utils/MultiDimensionalArraySerializer.cs new file mode 100644 index 0000000..2788cc6 --- /dev/null +++ b/GSS2.Core/Utils/MultiDimensionalArraySerializer.cs @@ -0,0 +1,123 @@ +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(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(); +}