From 57815e9072432c027b9d95baaaf38c17fa463d2b Mon Sep 17 00:00:00 2001 From: Ray Koopa Date: Fri, 26 Jun 2020 18:57:01 +0200 Subject: [PATCH] Get rid of DisposableGCHandle. Use Span functionality instead. This introduces some copies for .NET Standard 2.0 builds. --- .../Syroot.Worms/Core/DisposableGCHandle.cs | 44 -- .../Syroot.Worms/Core/Graphics/BitmapTools.cs | 15 +- .../Syroot.Worms/Core/Graphics/RawBitmap.cs | 14 +- .../Syroot.Worms/Core/IO/StreamExtensions.cs | 436 +++++++++--------- src/library/Syroot.Worms/Syroot.Worms.csproj | 1 + 5 files changed, 239 insertions(+), 271 deletions(-) delete mode 100644 src/library/Syroot.Worms/Core/DisposableGCHandle.cs diff --git a/src/library/Syroot.Worms/Core/DisposableGCHandle.cs b/src/library/Syroot.Worms/Core/DisposableGCHandle.cs deleted file mode 100644 index 922e65f..0000000 --- a/src/library/Syroot.Worms/Core/DisposableGCHandle.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -namespace Syroot.Worms.Core -{ - /// - /// Represents a disposable . - /// - internal class DisposableGCHandle : IDisposable - { - // ---- FIELDS ------------------------------------------------------------------------------------------------- - - private readonly GCHandle _handle; - private bool _disposed; - - // ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------ - - public DisposableGCHandle(object value, GCHandleType type) => _handle = GCHandle.Alloc(value, type); - - // ---- PROPERTIES --------------------------------------------------------------------------------------------- - - public IntPtr AddrOfPinnedObject => _handle.AddrOfPinnedObject(); - - // ---- METHODS (PUBLIC) --------------------------------------------------------------------------------------- - - public void Dispose() => Dispose(true); - - // ---- METHODS (PROTECTED) ------------------------------------------------------------------------------------ - - protected virtual void Dispose(bool disposing) - { - if (_disposed) - return; - - if (disposing) - { - if (_handle.IsAllocated) - _handle.Free(); - } - - _disposed = true; - } - } -} diff --git a/src/library/Syroot.Worms/Core/Graphics/BitmapTools.cs b/src/library/Syroot.Worms/Core/Graphics/BitmapTools.cs index 9b0ca94..f099939 100644 --- a/src/library/Syroot.Worms/Core/Graphics/BitmapTools.cs +++ b/src/library/Syroot.Worms/Core/Graphics/BitmapTools.cs @@ -1,7 +1,7 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Drawing; using System.Drawing.Imaging; -using System.Runtime.InteropServices; namespace Syroot.Worms.Core.Graphics { @@ -20,16 +20,15 @@ namespace Syroot.Worms.Core.Graphics /// The palette as a array. /// The data array storing bytes indexing the palette color array. /// The instance. - public static Bitmap CreateIndexed(Size size, IList palette, byte[] data) + public static unsafe Bitmap CreateIndexed(Size size, IList palette, byte[] data) { - using DisposableGCHandle dataPin = new DisposableGCHandle(data, GCHandleType.Pinned); - // Transfer the pixel data, respecting power-of-2 strides. Bitmap bitmap = new Bitmap(size.Width, size.Height, PixelFormat.Format8bppIndexed); BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, size.Width, size.Height), - ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed); - for (int y = 0; y < size.Height; y++) - Marshal.Copy(data, y * size.Width, bitmapData.Scan0 + y * bitmapData.Stride, size.Width); + ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed); + Span bitmapSpan = new Span(bitmapData.Scan0.ToPointer(), bitmapData.Stride * bitmapData.Height); + for (int y = 0; y < size.Height; y++) + data.AsSpan(y * size.Width, size.Width).CopyTo(bitmapSpan.Slice(y * bitmapData.Stride)); bitmap.UnlockBits(bitmapData); // Transfer the palette. diff --git a/src/library/Syroot.Worms/Core/Graphics/RawBitmap.cs b/src/library/Syroot.Worms/Core/Graphics/RawBitmap.cs index d33c483..821af55 100644 --- a/src/library/Syroot.Worms/Core/Graphics/RawBitmap.cs +++ b/src/library/Syroot.Worms/Core/Graphics/RawBitmap.cs @@ -1,8 +1,7 @@ +using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Imaging; -using System.Runtime.InteropServices; -using Syroot.Worms.Core; namespace Syroot.Worms { @@ -39,16 +38,15 @@ namespace Syroot.Worms /// Creates a from the raw data. /// /// The created from the raw data. - public Bitmap ToBitmap() + public unsafe Bitmap ToBitmap() { - using DisposableGCHandle dataPin = new DisposableGCHandle(Data, GCHandleType.Pinned); - // Transfer the pixel data, respecting power-of-2 strides. Bitmap bitmap = new Bitmap(Size.Width, Size.Height, PixelFormat.Format8bppIndexed); BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, Size.Width, Size.Height), - ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed); - for (int y = 0; y < Size.Height; y++) - Marshal.Copy(Data, y * Size.Width, bitmapData.Scan0 + y * bitmapData.Stride, Size.Width); + ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed); + Span bitmapSpan = new Span(bitmapData.Scan0.ToPointer(), bitmapData.Stride * bitmapData.Height); + for (int y = 0; y < Size.Height; y++) + Data.AsSpan(y * Size.Width, Size.Width).CopyTo(bitmapSpan.Slice(y * bitmapData.Stride)); bitmap.UnlockBits(bitmapData); // Transfer the palette. diff --git a/src/library/Syroot.Worms/Core/IO/StreamExtensions.cs b/src/library/Syroot.Worms/Core/IO/StreamExtensions.cs index d66abb7..928de1b 100644 --- a/src/library/Syroot.Worms/Core/IO/StreamExtensions.cs +++ b/src/library/Syroot.Worms/Core/IO/StreamExtensions.cs @@ -1,211 +1,225 @@ -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Runtime.InteropServices; -using Syroot.BinaryData; - -namespace Syroot.Worms.Core.IO -{ - /// - /// Represents extension methods for instances. - /// - [DebuggerStepThrough] - public static partial class BinaryStreamExtensions - { - // ---- METHODS (PUBLIC) --------------------------------------------------------------------------------------- - - // ---- Reading ---- - - /// - /// Reads an instance from the current stream. - /// - /// The type of the class to instantiate. - /// The extended . - /// The instance. - public static T Load(this BinaryStream self) where T : ILoadable, new() - { - T instance = new T(); - instance.Load(self.BaseStream); - return instance; - } - - /// - /// Reads instances from the current stream. - /// - /// The type of the class to instantiate. - /// The extended . - /// The number of instances to read. - /// The instances. - public static T[] Load(this BinaryStream self, int count) where T : ILoadable, new() - { - T[] instances = new T[count]; - for (int i = 0; i < count; i++) - instances[i] = Load(self); - return instances; - } - - /// - /// Reads a 0-terminated string which is stored in a fixed-size block of bytes. - /// - /// The extended . - /// The number of bytes the fixed-size blocks takes. - /// The read string. - public static string ReadString(this BinaryStream self, int length) - { - // TODO: This may not work with multi-byte encodings. - // Ensure to not try to decode any bytes after the 0 termination. - byte[] bytes = self.ReadBytes(length); - while (bytes[--length] == 0 && length > 0) { } - return self.Encoding.GetString(bytes, 0, length + 1); - } - - /// - /// Reads 0-terminated strings which are stored in a fixed-size block of - /// bytes. - /// - /// The extended . - /// The number of values to read. - /// The number of bytes the fixed-size blocks takes. - /// The read string. - public static string[] ReadStrings(this BinaryStream self, int count, int length) - { - string[] strings = new string[count]; - for (int i = 0; i < count; i++) - strings[i] = ReadString(self, length); - return strings; - } - - /// - /// Reads a raw byte structure from the current stream and returns it. - /// - /// The type of the structure to read. - /// The extended instance. - /// The structure of type . - public static T ReadStruct(this BinaryStream self) where T : struct - { - // Read the raw bytes of the structure. - byte[] bytes = self.ReadBytes(Marshal.SizeOf()); - - // Convert them to a structure instance and return it. - using DisposableGCHandle handle = new DisposableGCHandle(bytes, GCHandleType.Pinned); - T instance = Marshal.PtrToStructure(handle.AddrOfPinnedObject); - return instance; - } - - /// - /// Reads raw byte structures from the current stream and returns them. - /// - /// The type of the structure to read. - /// The extended instance. - /// The number of values to read. - /// The structures of type . - public static T[] ReadStructs(this BinaryStream self, int count) where T : struct - { - T[] values = new T[count]; - for (int i = 0; i < count; i++) - values[i] = ReadStruct(self); - return values; - } - - /// - /// Returns the current address of the stream to which a 4-byte placeholder has been written which can be filled - /// later. - /// - /// The extended instance. - /// The address at which a 4-byte placeholder has been written to. - public static uint ReserveOffset(this BinaryStream self) - { - uint offset = (uint)self.Position; - self.WriteUInt32(0); - return offset; - } - - // ---- Writing ---- - - /// - /// Writes the given instance into the current stream. - /// - /// The type of the class to write. - /// The extended . - /// The instance to write into the current stream. - public static void Save(this BinaryStream self, T value) where T : ISaveable - { - value.Save(self.BaseStream); - } - - /// - /// Writes the given instances into the current stream. - /// - /// The type of the class to write. - /// The extended . - /// The instances to write into the current stream. - public static void Save(this BinaryStream self, IList values) where T : ISaveable - { - foreach (T value in values) - Save(self, value); - } - - public static void SatisfyOffset(this BinaryStream self, uint offset, int value) - { - using (self.TemporarySeek(offset, SeekOrigin.Begin)) - self.WriteInt32(value); - } - - /// - /// Writes the given string into a fixed-size block of bytes and 0-terminates it. - /// - /// The extended instance. - /// The string to write. - /// The number of bytes the fixed-size block takes. - public static void WriteString(this BinaryStream self, string value, int length) - { - // TODO: This may not work with multi-byte encodings. - byte[] bytes = new byte[length]; - if (value != null) - self.Encoding.GetBytes(value, 0, value.Length, bytes, 0); - self.WriteBytes(bytes); - } - - /// - /// Writes the given strings into fixed-size blocks of bytes and 0-terminates them. - /// - /// The extended instance. - /// The strings to write. - /// The number of bytes a fixed-size block takes. - public static void WriteStrings(this BinaryStream self, IList values, int length) - { - foreach (string value in values) - WriteString(self, value, length); - } - - /// - /// Writes the bytes of a structure into the current stream. - /// - /// The type of the structure to read. - /// The extended instance. - /// The structure to write. - public static void WriteStruct(this BinaryStream self, T value) where T : struct - { - // Get the raw bytes of the structure instance. - byte[] bytes = new byte[Marshal.SizeOf()]; - - using (DisposableGCHandle handle = new DisposableGCHandle(bytes, GCHandleType.Pinned)) - Marshal.StructureToPtr(value, handle.AddrOfPinnedObject, false); - - // Write the bytes to the stream. - self.WriteStructs(bytes); - } - - /// - /// Writes the bytes of structures into the current stream. - /// - /// The type of the structure to read. - /// The extended instance. - /// The structures to write. - public static void WriteStructs(this BinaryStream self, IList values) where T : struct - { - foreach (T value in values) - WriteStruct(self, value); - } - } -} +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Syroot.BinaryData; + +namespace Syroot.Worms.Core.IO +{ + /// + /// Represents extension methods for instances. + /// + [DebuggerStepThrough] + public static partial class BinaryStreamExtensions + { + // ---- METHODS (PUBLIC) --------------------------------------------------------------------------------------- + + // ---- Reading ---- + + /// + /// Reads an instance from the current stream. + /// + /// The type of the class to instantiate. + /// The extended . + /// The instance. + public static T Load(this BinaryStream self) where T : ILoadable, new() + { + T instance = new T(); + instance.Load(self.BaseStream); + return instance; + } + + /// + /// Reads instances from the current stream. + /// + /// The type of the class to instantiate. + /// The extended . + /// The number of instances to read. + /// The instances. + public static T[] Load(this BinaryStream self, int count) where T : ILoadable, new() + { + T[] instances = new T[count]; + for (int i = 0; i < count; i++) + instances[i] = Load(self); + return instances; + } + + /// + /// Reads a 0-terminated string which is stored in a fixed-size block of bytes. + /// + /// The extended . + /// The number of bytes the fixed-size blocks takes. + /// The read string. + public static string ReadString(this BinaryStream self, int length) + { + // TODO: This may not work with multi-byte encodings. + // Ensure to not try to decode any bytes after the 0 termination. + byte[] bytes = self.ReadBytes(length); + while (bytes[--length] == 0 && length > 0) { } + return self.Encoding.GetString(bytes, 0, length + 1); + } + + /// + /// Reads 0-terminated strings which are stored in a fixed-size block of + /// bytes. + /// + /// The extended . + /// The number of values to read. + /// The number of bytes the fixed-size blocks takes. + /// The read string. + public static string[] ReadStrings(this BinaryStream self, int count, int length) + { + string[] strings = new string[count]; + for (int i = 0; i < count; i++) + strings[i] = ReadString(self, length); + return strings; + } + + /// + /// Reads a raw byte structure from the current stream and returns it. + /// + /// The type of the structure to read. + /// The extended instance. + /// The structure of type . + public static T ReadStruct(this BinaryStream self) where T : struct + { + // Read the raw bytes of the structure. +#if NETSTANDARD2_0 + byte[] bytes = new byte[Unsafe.SizeOf()]; + self.Read(bytes, 0, bytes.Length); +#else + Span bytes = stackalloc byte[Unsafe.SizeOf()]; + self.Read(bytes); +#endif + + // Convert them to a structure instance and return it. + ReadOnlySpan span = MemoryMarshal.Cast(bytes); + return span[0]; + } + + /// + /// Reads raw byte structures from the current stream and returns them. + /// + /// The type of the structure to read. + /// The extended instance. + /// The number of values to read. + /// The structures of type . + public static T[] ReadStructs(this BinaryStream self, int count) where T : struct + { + // Read the raw bytes of the structures. +#if NETSTANDARD2_0 + byte[] bytes = new byte[Unsafe.SizeOf()]; + self.Read(bytes, 0, bytes.Length); +#else + Span bytes = stackalloc byte[Unsafe.SizeOf() * count]; + self.Read(bytes); +#endif + + // Convert them to a structure array and return it. + ReadOnlySpan span = MemoryMarshal.Cast(bytes); + return span.ToArray(); + } + + /// + /// Returns the current address of the stream to which a 4-byte placeholder has been written which can be filled + /// later. + /// + /// The extended instance. + /// The address at which a 4-byte placeholder has been written to. + public static uint ReserveOffset(this BinaryStream self) + { + uint offset = (uint)self.Position; + self.WriteUInt32(0); + return offset; + } + + // ---- Writing ---- + + /// + /// Writes the given instance into the current stream. + /// + /// The type of the class to write. + /// The extended . + /// The instance to write into the current stream. + public static void Save(this BinaryStream self, T value) where T : ISaveable + { + value.Save(self.BaseStream); + } + + /// + /// Writes the given instances into the current stream. + /// + /// The type of the class to write. + /// The extended . + /// The instances to write into the current stream. + public static void Save(this BinaryStream self, IList values) where T : ISaveable + { + foreach (T value in values) + Save(self, value); + } + + public static void SatisfyOffset(this BinaryStream self, uint offset, int value) + { + using (self.TemporarySeek(offset, SeekOrigin.Begin)) + self.WriteInt32(value); + } + + /// + /// Writes the given string into a fixed-size block of bytes and 0-terminates it. + /// + /// The extended instance. + /// The string to write. + /// The number of bytes the fixed-size block takes. + public static void WriteString(this BinaryStream self, string value, int length) + { + // TODO: This may not work with multi-byte encodings. + byte[] bytes = new byte[length]; + if (value != null) + self.Encoding.GetBytes(value, 0, value.Length, bytes, 0); + self.WriteBytes(bytes); + } + + /// + /// Writes the given strings into fixed-size blocks of bytes and 0-terminates them. + /// + /// The extended instance. + /// The strings to write. + /// The number of bytes a fixed-size block takes. + public static void WriteStrings(this BinaryStream self, IList values, int length) + { + foreach (string value in values) + WriteString(self, value, length); + } + + /// + /// Writes the bytes of a structure into the current stream. + /// + /// The type of the structure to read. + /// The extended instance. + /// The structure to write. + public static void WriteStruct(this BinaryStream self, T value) where T : struct + { + ReadOnlySpan bytes; + unsafe { bytes = new ReadOnlySpan(Unsafe.AsPointer(ref value), Unsafe.SizeOf()); } +#if NETSTANDARD2_0 + self.Write(bytes.ToArray()); // cannot prevent copy in .NET Standard 2.0 +#else + self.Write(bytes); +#endif + } + + /// + /// Writes the bytes of structures into the current stream. + /// + /// The type of the structure to read. + /// The extended instance. + /// The structures to write. + public static void WriteStructs(this BinaryStream self, IEnumerable values) where T : struct + { + foreach (T value in values) + self.WriteStruct(value); + } + } +} diff --git a/src/library/Syroot.Worms/Syroot.Worms.csproj b/src/library/Syroot.Worms/Syroot.Worms.csproj index eccf2ab..6905b65 100644 --- a/src/library/Syroot.Worms/Syroot.Worms.csproj +++ b/src/library/Syroot.Worms/Syroot.Worms.csproj @@ -1,6 +1,7 @@  + true Syroot.Worms .NET library for loading and modifying files of Team17 Worms games. Split into game-related packages.