diff --git a/Core/Collections/DataMap.cs b/Core/Collections/DataMap.cs index 8176cb0..cabf336 100644 --- a/Core/Collections/DataMap.cs +++ b/Core/Collections/DataMap.cs @@ -1,10 +1,14 @@ using System.Numerics; +using System.Runtime.InteropServices; +using Just.Core.Extensions; namespace Just.Core.Collections; public class DataMap : Map, IDataMap, ICloneable where T : IComparisonOperators, IEqualityOperators { + public static int ElementSize { get; } = Marshal.SizeOf(); + internal MapPoint MinCache = default; internal MapPoint MaxCache = default; @@ -85,3 +89,84 @@ public class DataMap : Map, IDataMap, ICloneable object ICloneable.Clone() => Clone(); } + +public static class DataMap +{ + public const int HeaderSize = sizeof(Int64) + 2 * sizeof(UInt32); + public struct Header + { + public Int64 BodySize; + public UInt32 Width; + public UInt32 Height; + } + + public static async ValueTask WriteToStreamAsync(this DataMap map, Stream stream, CancellationToken cancellationToken = default) + where T : unmanaged, IComparisonOperators, IEqualityOperators + { + var head = new Header + { + BodySize = DataMap.ElementSize * map.Width * map.Height, + Width = (uint)map.Width, + Height = (uint)map.Height + }; + + var headBytes = MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(ref head, 1)) + .ToArray(); + var bodyBytes = MemoryMarshal.AsBytes(map.AsSpan()) + .ToArray(); + + await stream.WriteAsync(headBytes, cancellationToken); + await stream.WriteAsync(bodyBytes, cancellationToken); + } + public static async ValueTask> ReadFromStreamAsync(Stream stream, CancellationToken cancellationToken = default) + where T : unmanaged, IComparisonOperators, IEqualityOperators + { + byte[] headBytes = new byte[HeaderSize]; + await stream.PopulateAsync(headBytes, cancellationToken); + var head = MemoryMarshal.Read
(headBytes); + + var bodySize = DataMap.ElementSize * head.Width * head.Height; + if (bodySize != head.BodySize) throw new InvalidOperationException("Can not read DataMap. Element size mismatch."); + + byte[] bodyBytes = new byte[bodySize]; + await stream.PopulateAsync(bodyBytes, cancellationToken); + + T[] body = MemoryMarshal.Cast(bodyBytes).ToArray(); + return new DataMap((int)head.Width, (int)head.Height, body); + } + + + public static void WriteToStream(this DataMap map, Stream stream) + where T : unmanaged, IComparisonOperators, IEqualityOperators + { + var head = new Header + { + BodySize = DataMap.ElementSize * map.Width * map.Height, + Width = (uint)map.Width, + Height = (uint)map.Height + }; + + var headBytes = MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(ref head, 1)); + var bodyBytes = MemoryMarshal.AsBytes(map.AsSpan()); + + stream.Write(headBytes); + stream.Write(bodyBytes); + } + public static DataMap ReadFromStream(Stream stream) + where T : unmanaged, IComparisonOperators, IEqualityOperators + { + Header head = default; + var headSpan = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref head, 1)); + stream.Populate(headSpan); + + var bodySize = DataMap.ElementSize * head.Width * head.Height; + if (bodySize != head.BodySize) throw new InvalidOperationException("Can not read DataMap. Element size mismatch."); + + T[] body = new T[bodySize]; + var bodySpan = MemoryMarshal.AsBytes(body.AsSpan()); + + stream.Populate(bodySpan); + + return new DataMap((int)head.Width, (int)head.Height, body); + } +}