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