From b85fe974248e5063668615324082cfc8589038e6 Mon Sep 17 00:00:00 2001 From: iks-rain Date: Sat, 13 Dec 2025 21:22:20 +0800 Subject: [PATCH 01/10] =?UTF-8?q?ci:=20=E6=9C=AC=E5=9C=B0=E6=9E=84?= =?UTF-8?q?=E5=BB=BAnuget=E9=85=8D=E7=BD=AEPackNuget=E5=AE=8C=E5=96=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BinaryToolKit/BinaryToolKit.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BinaryToolKit/BinaryToolKit.csproj b/BinaryToolKit/BinaryToolKit.csproj index bfc99f9..6f7ff86 100644 --- a/BinaryToolKit/BinaryToolKit.csproj +++ b/BinaryToolKit/BinaryToolKit.csproj @@ -27,7 +27,7 @@ TRACE RELEASE true true - bin\PackNuget\BinaryToolKit.xml + true portable true snupkg From 301804ed9d04b287406b12d008a8fbde75ecc297 Mon Sep 17 00:00:00 2001 From: iks-rain Date: Sat, 13 Dec 2025 21:22:57 +0800 Subject: [PATCH 02/10] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0Pin=EF=BC=8C?= =?UTF-8?q?=E5=85=81=E8=AE=B8Pin=E4=BD=8F=E4=B8=80=E4=B8=AA=E5=AF=B9?= =?UTF-8?q?=E8=B1=A1=EF=BC=8C=E5=AE=9E=E7=8E=B0IDisposable=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BinaryToolKit/UnsafeToolkit/Pin.cs | 102 +++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 BinaryToolKit/UnsafeToolkit/Pin.cs diff --git a/BinaryToolKit/UnsafeToolkit/Pin.cs b/BinaryToolKit/UnsafeToolkit/Pin.cs new file mode 100644 index 0000000..4c54625 --- /dev/null +++ b/BinaryToolKit/UnsafeToolkit/Pin.cs @@ -0,0 +1,102 @@ + +using System.ComponentModel; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Iks.BinaryToolkit.UnsafeToolkit; + + +/// +/// Generics Supported and Auto-Release of . +/// +/// An object which you want to pin for sometime +[DebuggerDisplay("Address = {Address}, Target = {Target}")] +public struct Pin(TPinned objectNeededPinning) : IDisposable, IEquatable> where TPinned : class +{ + private GCHandle _handle = GCHandle.Alloc(objectNeededPinning ?? throw new ArgumentNullException(nameof(objectNeededPinning)), GCHandleType.Pinned); + + /// + /// Get pinned object + /// + public readonly TPinned Target => (TPinned)_handle.Target!; + + /// + /// Retrieves the address of object data in a Pinned handle. + /// + /// the address of the object + public readonly IntPtr Address => _handle.AddrOfPinnedObject(); + + /// Free the to unpin it + public void Dispose() => _handle.Free(); + + #region Wrapper GCHandle + + /// + /// Equals As + /// Returns an identifier for the current object. + /// + /// An identifier for the current object + public readonly override int GetHashCode() => _handle.GetHashCode(); + + /// Same as GCHandle.Equals. + /// Indicates whether the current instance is equal to another instance of the same type. + /// An instance to compare with this instance. + /// true if the current instance is equal to the other instance; otherwise, false. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly bool Equals(Pin other) => this._handle.Equals(other._handle); + + /// Same as GCHandle.Equals. + /// Indicates whether the current instance is equal to another instance of the same type. + /// An instance to compare with this instance. + /// true if the current instance is equal to the other instance; otherwise, false. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly bool Equals(GCHandle other) => _handle.Equals(other); + + /// Same as GCHandle.Equals. + /// Indicates whether the current instance is equal to another instance of the same type. + /// An instance to compare with this instance. + /// true if the current instance is equal to the other instance; otherwise, false. + public readonly override bool Equals(object? other) => other is GCHandle obj && _handle.Equals(obj); + + /// + /// Same As + /// + /// Equals + /// + /// + public static bool operator ==(Pin left, Pin right) => left.Equals(right); + + /// + /// Same As + /// + /// Equals + /// + /// + public static bool operator !=(Pin left, Pin right) => !(left == right); + + /// + /// Display Pin ,including Address and Target.ToString() + /// + public override string ToString() + { + return $"Pinned Address = {Address}, Target = {Target}"; + } + + #endregion +} +/// +/// Provide Pin extension ,such as PinIt, +/// +public static class PinExtension +{ + /// + extension(TPinned obj) where TPinned : class + { + /// + /// Make a Pin<TPinned> from obj + /// + /// + public Pin AsPinned() => new(obj); + } +} From 859cfb50a2e5084d412d8fdac24ef8bf492c6e61 Mon Sep 17 00:00:00 2001 From: iks-rain Date: Sat, 13 Dec 2025 21:23:18 +0800 Subject: [PATCH 03/10] =?UTF-8?q?test:=20=E5=A2=9E=E5=8A=A0=E5=AF=B9Pin?= =?UTF-8?q?=E7=9A=84=E9=9D=9ENull=E6=80=A7Test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BInaryKit-Test/UnsafeToolkit/Pin-Test.cs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 BInaryKit-Test/UnsafeToolkit/Pin-Test.cs diff --git a/BInaryKit-Test/UnsafeToolkit/Pin-Test.cs b/BInaryKit-Test/UnsafeToolkit/Pin-Test.cs new file mode 100644 index 0000000..4b287d9 --- /dev/null +++ b/BInaryKit-Test/UnsafeToolkit/Pin-Test.cs @@ -0,0 +1,23 @@ +using Iks.BinaryToolkit.UnsafeToolkit; +using Xunit.Abstractions; + +namespace BinaryKit_Test.UnsafeToolkit; + +public class PinTest(ITestOutputHelper output) +{ + [Fact] + public unsafe void NotNullPin() + { + try + { + // set a null ref + string str = null!; + using var pin = str.AsPinned(); + } + // throws is right,null cannot be pinned + catch (Exception e) + { + Assert.True(e is ArgumentNullException); + } + } +} \ No newline at end of file From c2bb76f624127a954327665729131964b2b9a0f8 Mon Sep 17 00:00:00 2001 From: iks-rain Date: Sat, 13 Dec 2025 21:58:09 +0800 Subject: [PATCH 04/10] =?UTF-8?q?test:=20=E6=9B=B4=E5=A4=9A=E6=B5=8B?= =?UTF-8?q?=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BInaryKit-Test/UnsafeToolkit/Pin-Test.cs | 53 ++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/BInaryKit-Test/UnsafeToolkit/Pin-Test.cs b/BInaryKit-Test/UnsafeToolkit/Pin-Test.cs index 4b287d9..48b11c5 100644 --- a/BInaryKit-Test/UnsafeToolkit/Pin-Test.cs +++ b/BInaryKit-Test/UnsafeToolkit/Pin-Test.cs @@ -1,12 +1,15 @@ -using Iks.BinaryToolkit.UnsafeToolkit; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Iks.BinaryToolkit.UnsafeToolkit; using Xunit.Abstractions; namespace BinaryKit_Test.UnsafeToolkit; public class PinTest(ITestOutputHelper output) { + // pin must be not null [Fact] - public unsafe void NotNullPin() + public void NotNullPin() { try { @@ -17,7 +20,51 @@ public unsafe void NotNullPin() // throws is right,null cannot be pinned catch (Exception e) { - Assert.True(e is ArgumentNullException); + return; } + Assert.Fail(); } + + [Fact] + public void NotUseAfterRelease() + { + var pin = new object().AsPinned(); + pin.Dispose(); + // use after release + try + { + var pinTarget = pin.Target; + } + catch + { + return; + } + Assert.Fail(); + } + [Fact] + public void NotMultipleRelease() + { + var pin = new object().AsPinned(); + pin.Dispose(); + // release again + try + { + pin.Dispose(); + } + catch + { + return; + } + Assert.Fail(); + } + + [Fact] + public void CopyRelease() + { + var pin_old = new object().AsPinned(); + var pin_new = pin_old; + pin_new.Dispose(); + } + + } \ No newline at end of file From 264bbf578332d76831eff69ed3ef217e0364331c Mon Sep 17 00:00:00 2001 From: iks-rain Date: Sat, 13 Dec 2025 22:11:43 +0800 Subject: [PATCH 05/10] =?UTF-8?q?feat:=20=E6=9A=B4=E9=9C=B2LocalEndianness?= =?UTF-8?q?Extension.LocalEndianness=E5=AD=97=E6=AE=B5=E4=BB=A5=E5=85=81?= =?UTF-8?q?=E8=AE=B8C#13=E4=BB=A5=E4=B8=8B=E4=BD=BF=E7=94=A8Local(?= =?UTF-8?q?=E4=BD=86=E5=8F=AF=E8=83=BD=E6=9C=89=E4=BA=9B=E4=B8=91=E9=99=8B?= =?UTF-8?q?=3F)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BinaryToolKit/EndianToolkit.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/BinaryToolKit/EndianToolkit.cs b/BinaryToolKit/EndianToolkit.cs index fd0c302..ab1efcc 100644 --- a/BinaryToolKit/EndianToolkit.cs +++ b/BinaryToolKit/EndianToolkit.cs @@ -187,9 +187,12 @@ public enum Endianness /// Extension of ,provide Local public static class EndiannessExtension { - private static readonly Endianness LocalEndianness = + /// + /// Same as Local,Just to support C# 13 and less + /// + public static readonly Endianness LocalEndianness = BitConverter.IsLittleEndian ? Endianness.Little : Endianness.Big; - + /// extension(Endianness obj) { /// From eccc5a8056bd353803753885feb4b9c4fae9f155 Mon Sep 17 00:00:00 2001 From: iks-rain Date: Sat, 13 Dec 2025 22:14:00 +0800 Subject: [PATCH 06/10] =?UTF-8?q?ci:=20=E6=98=BE=E7=A4=BA=E6=8C=87?= =?UTF-8?q?=E5=AE=9A=E6=B5=8B=E8=AF=95=E6=A1=86=E6=9E=B6SDK=E7=89=88?= =?UTF-8?q?=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BInaryKit-Test/BInaryKit-Test.csproj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/BInaryKit-Test/BInaryKit-Test.csproj b/BInaryKit-Test/BInaryKit-Test.csproj index 916fa9a..997e4a4 100644 --- a/BInaryKit-Test/BInaryKit-Test.csproj +++ b/BInaryKit-Test/BInaryKit-Test.csproj @@ -1,13 +1,14 @@  - net10.0 BinaryKit_Test enable enable false true BinaryKit-Test + 14 + net10.0 From c2ffea02a3907883fc5941e56f394f4e6edbf194 Mon Sep 17 00:00:00 2001 From: iks-rain Date: Sat, 13 Dec 2025 22:37:56 +0800 Subject: [PATCH 07/10] =?UTF-8?q?readme:=E6=B7=BB=E5=8A=A0pin=E7=9A=84read?= =?UTF-8?q?me=20Feature=E4=BB=8B=E7=BB=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Feature.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Feature.md b/Feature.md index 21c3306..9975b89 100644 --- a/Feature.md +++ b/Feature.md @@ -26,7 +26,17 @@ + 源码位置: 自己翻 + `ConvertMany(values,from,to)`同上,针对多个元素不会多次评估是否包含`指针(T*,ref)` + `ReverseMany`依旧是`Span`和`指针(T*)` - ++ ### `Pin` ,A `GCHandle` Wrapper + + 为NET10以下版本提供一个带Dispose方法的GCHandle + + NET10以上请使用标准库[PinnedGCHandle\](https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.pinnedgchandle-1?view=net-10.0) + + 行为与NET10的`PinnedGCHandle`有些不一致,`PinnedGCHandle`类型范围更大,它不检查对象是否是`可Pin`的 + + 我的实现`Pin`作为`GCHandle`的封装,并不能绕过`GCHandle`的API限制 + + 主要行为: struct Pin/static PinExtension + + 命名空间: Iks.BinaryToolkit.UnsafeToolkit.Pin + + 源码位置: 自己翻 + + 用途: 暂时pin住一个对象,用于与非托管代码交互,主要提供语法简洁性、额外的null检查、using自动释放 +> [!CAUTION] +> Remember To Dispose It! # Thinking + ## 还没想好(大声) + ### 欢迎贡献 From 303ea5dc0e981ac67420526588fdee7a6aacef55 Mon Sep 17 00:00:00 2001 From: iks-rain Date: Sat, 13 Dec 2025 22:47:47 +0800 Subject: [PATCH 08/10] =?UTF-8?q?chore:=20=E4=BF=AE=E6=94=B9=E5=86=85?= =?UTF-8?q?=E9=83=A8=E5=BC=82=E5=B8=B8=E6=B6=88=E6=81=AF=E8=B7=AF=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BinaryToolKit/BinaryStreamToolkit.cs | 1 + BinaryToolKit/{ => Message}/ErrorMessage.cs | 2 +- BinaryToolKit/Message/ObsoleteMessage.cs | 7 +++++++ 3 files changed, 9 insertions(+), 1 deletion(-) rename BinaryToolKit/{ => Message}/ErrorMessage.cs (86%) create mode 100644 BinaryToolKit/Message/ObsoleteMessage.cs diff --git a/BinaryToolKit/BinaryStreamToolkit.cs b/BinaryToolKit/BinaryStreamToolkit.cs index 939d89e..dec622d 100644 --- a/BinaryToolKit/BinaryStreamToolkit.cs +++ b/BinaryToolKit/BinaryStreamToolkit.cs @@ -1,5 +1,6 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using Iks.BinaryToolKit.Message; namespace Iks.BinaryToolkit; diff --git a/BinaryToolKit/ErrorMessage.cs b/BinaryToolKit/Message/ErrorMessage.cs similarity index 86% rename from BinaryToolKit/ErrorMessage.cs rename to BinaryToolKit/Message/ErrorMessage.cs index ae16abe..bc0ece0 100644 --- a/BinaryToolKit/ErrorMessage.cs +++ b/BinaryToolKit/Message/ErrorMessage.cs @@ -1,4 +1,4 @@ -namespace Iks.BinaryToolkit; +namespace Iks.BinaryToolKit.Message; internal static class ErrorMessage { diff --git a/BinaryToolKit/Message/ObsoleteMessage.cs b/BinaryToolKit/Message/ObsoleteMessage.cs new file mode 100644 index 0000000..47e4b3c --- /dev/null +++ b/BinaryToolKit/Message/ObsoleteMessage.cs @@ -0,0 +1,7 @@ +namespace Iks.BinaryToolkit.UnsafeToolkit; + + +internal static class ObsoleteMessage +{ + +} \ No newline at end of file From a2e581fe1fa1662c62c1774e1f863226bf960197 Mon Sep 17 00:00:00 2001 From: iks-rain Date: Sat, 13 Dec 2025 22:51:09 +0800 Subject: [PATCH 09/10] =?UTF-8?q?chore:=E5=9C=A8net10=E4=BB=A5=E4=B8=8A?= =?UTF-8?q?=E7=89=88=E6=9C=AC=E4=BD=BF=E8=87=AA=E5=AE=9A=E4=B9=89Pin?= =?UTF-8?q?=E8=BF=87=E6=97=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BinaryToolKit/Message/ObsoleteMessage.cs | 5 +++-- BinaryToolKit/UnsafeToolkit/Pin.cs | 12 +++++++++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/BinaryToolKit/Message/ObsoleteMessage.cs b/BinaryToolKit/Message/ObsoleteMessage.cs index 47e4b3c..0091087 100644 --- a/BinaryToolKit/Message/ObsoleteMessage.cs +++ b/BinaryToolKit/Message/ObsoleteMessage.cs @@ -1,7 +1,8 @@ -namespace Iks.BinaryToolkit.UnsafeToolkit; +namespace Iks.BinaryToolKit.Message; internal static class ObsoleteMessage { - + public const string Use_PinnedGCHandle_From_Standard = + "This Wrapper has already been obsoleted.Use standard Type PinnedGCHandle from ` System.Runtime.InteropServices.PinnedGCHandle `"; } \ No newline at end of file diff --git a/BinaryToolKit/UnsafeToolkit/Pin.cs b/BinaryToolKit/UnsafeToolkit/Pin.cs index 4c54625..95c20f9 100644 --- a/BinaryToolKit/UnsafeToolkit/Pin.cs +++ b/BinaryToolKit/UnsafeToolkit/Pin.cs @@ -1,16 +1,19 @@ - -using System.ComponentModel; -using System.Diagnostics; +using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using Iks.BinaryToolKit.Message; namespace Iks.BinaryToolkit.UnsafeToolkit; + /// /// Generics Supported and Auto-Release of . /// /// An object which you want to pin for sometime +#if NET10_0_OR_GREATER +[Obsolete(ObsoleteMessage.Use_PinnedGCHandle_From_Standard,false)] +#endif [DebuggerDisplay("Address = {Address}, Target = {Target}")] public struct Pin(TPinned objectNeededPinning) : IDisposable, IEquatable> where TPinned : class { @@ -88,6 +91,9 @@ public override string ToString() /// /// Provide Pin extension ,such as PinIt, /// +#if NET10_0_OR_GREATER +[Obsolete(ObsoleteMessage.Use_PinnedGCHandle_From_Standard,false)] +#endif public static class PinExtension { /// From efbea53e906d58b774972287a971ac99d8d0bd6a Mon Sep 17 00:00:00 2001 From: iks-rain Date: Sat, 13 Dec 2025 22:53:47 +0800 Subject: [PATCH 10/10] =?UTF-8?q?style:=20=E6=A0=BC=E5=BC=8F=E5=8C=96?= =?UTF-8?q?=E4=BB=A3=E7=A0=81,=E9=A1=B9=E7=9B=AE=E6=B8=85=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BInaryKit-Test/BInaryKit-Test.csproj | 2 +- BInaryKit-Test/EndianTest.cs | 18 ++-- BInaryKit-Test/StreamExTest.cs | 48 +++++----- BInaryKit-Test/UnsafeToolkit/Pin-Test.cs | 23 +++-- BinaryToolKit.slnx | 30 +++---- BinaryToolKit/BinaryStreamToolkit.cs | 12 +-- BinaryToolKit/EndianToolkit.cs | 44 ++++----- BinaryToolKit/Message/ObsoleteMessage.cs | 1 - BinaryToolKit/UnsafeToolkit/Pin.cs | 109 +++++++++++++++-------- Feature.md | 68 +++++++------- README.md | 39 ++++---- 11 files changed, 215 insertions(+), 179 deletions(-) diff --git a/BInaryKit-Test/BInaryKit-Test.csproj b/BInaryKit-Test/BInaryKit-Test.csproj index 997e4a4..4b695a0 100644 --- a/BInaryKit-Test/BInaryKit-Test.csproj +++ b/BInaryKit-Test/BInaryKit-Test.csproj @@ -23,7 +23,7 @@ - + \ No newline at end of file diff --git a/BInaryKit-Test/EndianTest.cs b/BInaryKit-Test/EndianTest.cs index f9815e9..b31082e 100644 --- a/BInaryKit-Test/EndianTest.cs +++ b/BInaryKit-Test/EndianTest.cs @@ -10,14 +10,14 @@ public class EndianTest(ITestOutputHelper output) public void Read_int() { const int value = 1000; - var expected = BitConverter.IsLittleEndian ? value : System.Buffers.Binary.BinaryPrimitives.ReverseEndianness(value); + var expected = BitConverter.IsLittleEndian ? value : BinaryPrimitives.ReverseEndianness(value); var actual = value; actual = EndianToolkit.Convert(actual, Endianness.Local, Endianness.Little); output.WriteLine("expected: " + expected); output.WriteLine("actual: " + actual); Assert.Equal(expected, actual); } - + [Fact] public unsafe void Read_Struct() { @@ -25,10 +25,7 @@ public unsafe void Read_Struct() var expected = value; var actual = value; // to big endian - if (BitConverter.IsLittleEndian) - { - new Span(&expected, sizeof(UnionStruct)).Reverse(); - } + if (BitConverter.IsLittleEndian) new Span(&expected, sizeof(UnionStruct)).Reverse(); // to big endian actual = EndianToolkit.Convert(actual, Endianness.Local, Endianness.Big); output.WriteLine("expected: " + expected); @@ -41,7 +38,7 @@ public unsafe void Read_Struct() [Fact] public void ReverseManyTest() { - Span old = [10,20,30,40,50,60,70,80]; + Span old = [10, 20, 30, 40, 50, 60, 70, 80]; Span expected = stackalloc int[old.Length]; for (var index = 0; index < old.Length; index++) { @@ -50,15 +47,10 @@ public void ReverseManyTest() } EndianToolkit.ReverseMany(old); - for (int i = 0; i < old.Length; i++) - { + for (var i = 0; i < old.Length; i++) if (old[i] != expected[i]) - { Assert.Fail(); - } - } } - #endregion } \ No newline at end of file diff --git a/BInaryKit-Test/StreamExTest.cs b/BInaryKit-Test/StreamExTest.cs index 518dbb9..8fe44ab 100644 --- a/BInaryKit-Test/StreamExTest.cs +++ b/BInaryKit-Test/StreamExTest.cs @@ -7,6 +7,24 @@ namespace BinaryKit_Test; public class StreamExTest(ITestOutputHelper output) { + #region WriteFrom Tests + + [Fact] + public unsafe void Write() + { + UnionStruct expected = new(); + UnionStruct value = default; + using var stream = new MemoryStream(); + stream.WriteFrom(expected); + stream.Position = 0; + stream.GetBuffer().AsSpan(0, sizeof(UnionStruct)).CopyTo(new Span(&expected, sizeof(UnionStruct))); + output.WriteLine("left: " + expected); + output.WriteLine("right: " + value); + Assert.Equal(expected, value); + } + + #endregion + #region ReadAs Tests [Fact] @@ -65,29 +83,12 @@ public unsafe void Read_UnionStruct() #endregion - #region WriteFrom Tests - - [Fact] - public unsafe void Write() - { - UnionStruct expected = new(); - UnionStruct value = default; - using var stream = new MemoryStream(); - stream.WriteFrom(expected); - stream.Position = 0; - stream.GetBuffer().AsSpan(0, sizeof(UnionStruct)).CopyTo(new Span(&expected, sizeof(UnionStruct))); - output.WriteLine("left: " + expected); - output.WriteLine("right: " + value); - Assert.Equal(expected, value); - } - #endregion - #region Multiple-Test [Fact] public unsafe void MultipleRead() { - byte[] ints = [10,20,30,40]; + byte[] ints = [10, 20, 30, 40]; Span span = stackalloc int[1]; var ptr = (int*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(ints)); var expected = *ptr; @@ -99,17 +100,16 @@ public unsafe void MultipleRead() stream.ReadManyAs(span2); if (span2[0] != *(short*)ptr) Assert.Fail(); if (span2[1] != ((short*)ptr)[1]) Assert.Fail(); - } - + } + [Fact] public void MultipleWrite() { - byte[] ints = [10,20,30,40]; + byte[] ints = [10, 20, 30, 40]; using var stream = new MemoryStream(); stream.WriteManyFrom(ints); - if (stream.GetBuffer() is not [10,20,30,40,..]) Assert.Fail(); - } - + if (stream.GetBuffer() is not [10, 20, 30, 40, ..]) Assert.Fail(); + } #endregion } \ No newline at end of file diff --git a/BInaryKit-Test/UnsafeToolkit/Pin-Test.cs b/BInaryKit-Test/UnsafeToolkit/Pin-Test.cs index 48b11c5..797689b 100644 --- a/BInaryKit-Test/UnsafeToolkit/Pin-Test.cs +++ b/BInaryKit-Test/UnsafeToolkit/Pin-Test.cs @@ -1,11 +1,8 @@ -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using Iks.BinaryToolkit.UnsafeToolkit; -using Xunit.Abstractions; +using Iks.BinaryToolkit.UnsafeToolkit; namespace BinaryKit_Test.UnsafeToolkit; -public class PinTest(ITestOutputHelper output) +public class PinTest { // pin must be not null [Fact] @@ -18,13 +15,14 @@ public void NotNullPin() using var pin = str.AsPinned(); } // throws is right,null cannot be pinned - catch (Exception e) + catch { return; } + Assert.Fail(); } - + [Fact] public void NotUseAfterRelease() { @@ -35,12 +33,14 @@ public void NotUseAfterRelease() { var pinTarget = pin.Target; } - catch + catch { return; } + Assert.Fail(); } + [Fact] public void NotMultipleRelease() { @@ -51,13 +51,14 @@ public void NotMultipleRelease() { pin.Dispose(); } - catch + catch { return; } + Assert.Fail(); } - + [Fact] public void CopyRelease() { @@ -65,6 +66,4 @@ public void CopyRelease() var pin_new = pin_old; pin_new.Dispose(); } - - } \ No newline at end of file diff --git a/BinaryToolKit.slnx b/BinaryToolKit.slnx index 7d9d028..988c9e0 100644 --- a/BinaryToolKit.slnx +++ b/BinaryToolKit.slnx @@ -1,17 +1,17 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + diff --git a/BinaryToolKit/BinaryStreamToolkit.cs b/BinaryToolKit/BinaryStreamToolkit.cs index dec622d..d402c66 100644 --- a/BinaryToolKit/BinaryStreamToolkit.cs +++ b/BinaryToolKit/BinaryStreamToolkit.cs @@ -63,7 +63,7 @@ allows ref struct } /// - /// write an unmanaged type(value) to the stream. + /// write an unmanaged type(value) to the stream. /// /// target type [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -76,9 +76,9 @@ allows ref struct { stream.Write(new ReadOnlySpan(&value, sizeof(T))); } - + /// - /// write an unmanaged type(value) to the stream. + /// write an unmanaged type(value) to the stream. /// /// target type [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -107,7 +107,7 @@ allows ref struct [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void ReadManyAs(Span targetSpan) where T : unmanaged { - fixed(T* ptrT = &MemoryMarshal.GetReference(targetSpan)) + fixed (T* ptrT = &MemoryMarshal.GetReference(targetSpan)) { ReadManyAs(stream, ptrT, targetSpan.Length); } @@ -155,9 +155,9 @@ allows ref struct /// target position public unsafe void WriteManyFrom(ReadOnlySpan values) where T : unmanaged { - fixed(T* ptrT = &MemoryMarshal.GetReference(values)) + fixed (T* ptrT = &MemoryMarshal.GetReference(values)) { - WriteManyFrom(stream,ptrT, values.Length); + WriteManyFrom(stream, ptrT, values.Length); } } diff --git a/BinaryToolKit/EndianToolkit.cs b/BinaryToolKit/EndianToolkit.cs index ab1efcc..0d71e00 100644 --- a/BinaryToolKit/EndianToolkit.cs +++ b/BinaryToolKit/EndianToolkit.cs @@ -5,7 +5,7 @@ namespace Iks.BinaryToolkit; /// -/// provides methods to process endianness of unmanaged types,including int,float. +/// provides methods to process endianness of unmanaged types,including int,float. /// public static class EndianToolkit { @@ -43,7 +43,7 @@ private static unsafe void ReverseNoCheck(void* value, int length) #region Single /// - /// reverses the endianness of an unmanaged type(value). + /// reverses the endianness of an unmanaged type(value). /// /// value wait to reverse /// type you want to reverse @@ -60,7 +60,7 @@ allows ref struct } /// - /// reverses the endianness of an unmanaged type(value). + /// reverses the endianness of an unmanaged type(value). /// /// target position you want to reverse /// The size of the element that this pointer points to @@ -72,8 +72,9 @@ public static unsafe void Reverse(void* value, int length) /// - /// converts the endianness of an unmanaged type(value) from one to another. - /// it cannot to make sure struct members are all in the same endian.it just reverses byte-endianness of the whole struct. + /// converts the endianness of an unmanaged type(value) from one to another. + /// it cannot to make sure struct members are all in the same endian.it just reverses byte-endianness of the whole + /// struct. /// /// target value /// source endian,can use local @@ -87,10 +88,7 @@ allows ref struct #endif { // differ, reverse - if (from != to) - { - ReverseNoCheck(&value, sizeof(T)); - } + if (from != to) ReverseNoCheck(&value, sizeof(T)); return value; } @@ -101,7 +99,7 @@ allows ref struct #region Multiple /// - /// reverses the endianness of multiple unmanaged type(values). + /// reverses the endianness of multiple unmanaged type(values). /// /// target enumerable wait to reverse [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -114,7 +112,7 @@ public static unsafe void ReverseMany(Span values) where T : unmanaged } /// - /// reverses the endianness of multiple unmanaged type(values). + /// reverses the endianness of multiple unmanaged type(values). /// /// target position to reverse endianness /// The size of the element that this pointer points to @@ -133,7 +131,7 @@ public static unsafe void ReverseMany(void* target, int elementSize, int element } /// - /// converts the endianness of multiple unmanaged type(value) from one to another. + /// converts the endianness of multiple unmanaged type(value) from one to another. /// /// target position to reverse endianness /// source endian @@ -148,7 +146,7 @@ public static unsafe void ConvertMany(Span target, Endianness from, Endian } /// - /// converts the endianness of multiple unmanaged type(value) from one to another. + /// converts the endianness of multiple unmanaged type(value) from one to another. /// /// target position to reverse endianness /// The size of the element that this pointer points to @@ -169,35 +167,39 @@ public static unsafe void ConvertMany(void* target, int elementSize, int element } /// -/// endianness type definition +/// endianness type definition /// public enum Endianness { /// - /// little-endian byte order + /// little-endian byte order /// Little, /// - /// big-endian byte order + /// big-endian byte order /// Big } -/// Extension of ,provide Local +/// Extension of +/// +/// ,provide +/// Local public static class EndiannessExtension { /// - /// Same as Local,Just to support C# 13 and less + /// Same as Local,Just to support C# 13 and less /// public static readonly Endianness LocalEndianness = BitConverter.IsLittleEndian ? Endianness.Little : Endianness.Big; - /// + + /// extension(Endianness obj) { /// - /// local machine byte order - /// same as BitConverter.IsLittleEndian + /// local machine byte order + /// same as BitConverter.IsLittleEndian /// public static Endianness Local => LocalEndianness; } diff --git a/BinaryToolKit/Message/ObsoleteMessage.cs b/BinaryToolKit/Message/ObsoleteMessage.cs index 0091087..caaf0a0 100644 --- a/BinaryToolKit/Message/ObsoleteMessage.cs +++ b/BinaryToolKit/Message/ObsoleteMessage.cs @@ -1,6 +1,5 @@ namespace Iks.BinaryToolKit.Message; - internal static class ObsoleteMessage { public const string Use_PinnedGCHandle_From_Standard = diff --git a/BinaryToolKit/UnsafeToolkit/Pin.cs b/BinaryToolKit/UnsafeToolkit/Pin.cs index 95c20f9..1c54ddc 100644 --- a/BinaryToolKit/UnsafeToolkit/Pin.cs +++ b/BinaryToolKit/UnsafeToolkit/Pin.cs @@ -5,81 +5,108 @@ namespace Iks.BinaryToolkit.UnsafeToolkit; - - /// -/// Generics Supported and Auto-Release of . +/// Generics Supported and Auto-Release of . /// /// An object which you want to pin for sometime #if NET10_0_OR_GREATER -[Obsolete(ObsoleteMessage.Use_PinnedGCHandle_From_Standard,false)] +[Obsolete(ObsoleteMessage.Use_PinnedGCHandle_From_Standard, false)] #endif [DebuggerDisplay("Address = {Address}, Target = {Target}")] public struct Pin(TPinned objectNeededPinning) : IDisposable, IEquatable> where TPinned : class { - private GCHandle _handle = GCHandle.Alloc(objectNeededPinning ?? throw new ArgumentNullException(nameof(objectNeededPinning)), GCHandleType.Pinned); - + private GCHandle _handle = + GCHandle.Alloc(objectNeededPinning ?? throw new ArgumentNullException(nameof(objectNeededPinning)), + GCHandleType.Pinned); + /// - /// Get pinned object + /// Get pinned object /// public readonly TPinned Target => (TPinned)_handle.Target!; /// - /// Retrieves the address of object data in a Pinned handle. + /// Retrieves the address of object data in a Pinned handle. /// /// the address of the object public readonly IntPtr Address => _handle.AddrOfPinnedObject(); - /// Free the to unpin it - public void Dispose() => _handle.Free(); + /// Free the to unpin it + public void Dispose() + { + _handle.Free(); + } #region Wrapper GCHandle /// - /// Equals As - /// Returns an identifier for the current object. + /// Equals As + /// Returns an identifier for the current object. /// - /// An identifier for the current object - public readonly override int GetHashCode() => _handle.GetHashCode(); + /// An identifier for the current object + public readonly override int GetHashCode() + { + return _handle.GetHashCode(); + } - /// Same as GCHandle.Equals. - /// Indicates whether the current instance is equal to another instance of the same type. + /// + /// Same as GCHandle.Equals. + /// Indicates whether the current instance is equal to another instance of the same type. + /// /// An instance to compare with this instance. /// true if the current instance is equal to the other instance; otherwise, false. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly bool Equals(Pin other) => this._handle.Equals(other._handle); + public readonly bool Equals(Pin other) + { + return _handle.Equals(other._handle); + } - /// Same as GCHandle.Equals. - /// Indicates whether the current instance is equal to another instance of the same type. + /// + /// Same as GCHandle.Equals. + /// Indicates whether the current instance is equal to another instance of the same type. + /// /// An instance to compare with this instance. /// true if the current instance is equal to the other instance; otherwise, false. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly bool Equals(GCHandle other) => _handle.Equals(other); + public readonly bool Equals(GCHandle other) + { + return _handle.Equals(other); + } - /// Same as GCHandle.Equals. - /// Indicates whether the current instance is equal to another instance of the same type. + /// + /// Same as GCHandle.Equals. + /// Indicates whether the current instance is equal to another instance of the same type. + /// /// An instance to compare with this instance. /// true if the current instance is equal to the other instance; otherwise, false. - public readonly override bool Equals(object? other) => other is GCHandle obj && _handle.Equals(obj); + public readonly override bool Equals(object? other) + { + return other is GCHandle obj && _handle.Equals(obj); + } /// - /// Same As - /// - /// Equals - /// + /// Same As + /// + /// Equals + /// /// - public static bool operator ==(Pin left, Pin right) => left.Equals(right); + public static bool operator ==(Pin left, Pin right) + { + return left.Equals(right); + } /// - /// Same As - /// - /// Equals - /// + /// Same As + /// + /// Equals + /// /// - public static bool operator !=(Pin left, Pin right) => !(left == right); + public static bool operator !=(Pin left, Pin right) + { + return !(left == right); + } /// - /// Display Pin ,including Address and Target.ToString() + /// Display Pin ,including Address and Target.ToString() /// public override string ToString() { @@ -88,11 +115,12 @@ public override string ToString() #endregion } + /// -/// Provide Pin extension ,such as PinIt, +/// Provide Pin extension ,such as PinIt, /// #if NET10_0_OR_GREATER -[Obsolete(ObsoleteMessage.Use_PinnedGCHandle_From_Standard,false)] +[Obsolete(ObsoleteMessage.Use_PinnedGCHandle_From_Standard, false)] #endif public static class PinExtension { @@ -100,9 +128,12 @@ public static class PinExtension extension(TPinned obj) where TPinned : class { /// - /// Make a Pin<TPinned> from obj + /// Make a Pin<TPinned> from obj /// /// - public Pin AsPinned() => new(obj); + public Pin AsPinned() + { + return new Pin(obj); + } } -} +} \ No newline at end of file diff --git a/Feature.md b/Feature.md index 9975b89..26ddebc 100644 --- a/Feature.md +++ b/Feature.md @@ -1,43 +1,49 @@ # Finished-Feature + + ### `BinaryStreamToolkit` - + 用于以泛型的方式读写Stream中的数据 - + 主要行为: Ex:Stream - + 命名空间: Iks.BinaryToolkit.BinaryStreamToolkit - + 源码位置: 自己翻 - + `ReadAs`(out与返回值模式两种出参方式)、`WriteFrom` + + 用于以泛型的方式读写Stream中的数据 + + 主要行为: Ex:Stream + + 命名空间: Iks.BinaryToolkit.BinaryStreamToolkit + + 源码位置: 自己翻 + + `ReadAs`(out与返回值模式两种出参方式)、`WriteFrom` + ### `Multiple-BinaryStreamToolkit` - + 用于以泛型的方式读取多个数据,提供`指针(T*)`与`Span`支持 - + 主要行为: Ex:Stream - + 命名空间: Iks.BinaryToolkit.BinaryStreamToolkit - + 源码位置: 自己翻 - + `ReadManyAs`、`WriteManyFrom` + + 用于以泛型的方式读取多个数据,提供`指针(T*)`与`Span`支持 + + 主要行为: Ex:Stream + + 命名空间: Iks.BinaryToolkit.BinaryStreamToolkit + + 源码位置: 自己翻 + + `ReadManyAs`、`WriteManyFrom` + ### `EndianToolkit` - + 用于以泛型的方式反转端序,算是`BinaryPrimitives`的增加与封装 - + 主要行为: static: EndianToolkit - + 命名空间: Iks.BinaryToolkit.EndianToolkit - + 源码位置: 自己翻 - + `Convert(value,from,to)`语义明确,支持填写Local: `from Local to Big` - + 能`allows ref struct`的尽力allow了,如果`ref struct`的内存布局里真有`指针(T*,ref)`不行(*`.NET 8因为不能allow也不行`*) - + `Reverse` 同样的`指针(T*)`与`Span`出参 + + 用于以泛型的方式反转端序,算是`BinaryPrimitives`的增加与封装 + + 主要行为: static: EndianToolkit + + 命名空间: Iks.BinaryToolkit.EndianToolkit + + 源码位置: 自己翻 + + `Convert(value,from,to)`语义明确,支持填写Local: `from Local to Big` + + 能`allows ref struct`的尽力allow了,如果`ref struct`的内存布局里真有`指针(T*,ref)`不行( + *`.NET 8因为不能allow也不行`*) + + `Reverse` 同样的`指针(T*)`与`Span`出参 + ### Multiple-EndianToolkit - + 用于以泛型的方式反转集合元素端序,与`Multiple-BinaryStreamToolkit`配合使用 - + 主要行为: static: EndianToolkit - + 命名空间: Iks.BinaryToolkit.EndianToolkit - + 源码位置: 自己翻 - + `ConvertMany(values,from,to)`同上,针对多个元素不会多次评估是否包含`指针(T*,ref)` - + `ReverseMany`依旧是`Span`和`指针(T*)` + + 用于以泛型的方式反转集合元素端序,与`Multiple-BinaryStreamToolkit`配合使用 + + 主要行为: static: EndianToolkit + + 命名空间: Iks.BinaryToolkit.EndianToolkit + + 源码位置: 自己翻 + + `ConvertMany(values,from,to)`同上,针对多个元素不会多次评估是否包含`指针(T*,ref)` + + `ReverseMany`依旧是`Span`和`指针(T*)` + ### `Pin` ,A `GCHandle` Wrapper - + 为NET10以下版本提供一个带Dispose方法的GCHandle - + NET10以上请使用标准库[PinnedGCHandle\](https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.pinnedgchandle-1?view=net-10.0) - + 行为与NET10的`PinnedGCHandle`有些不一致,`PinnedGCHandle`类型范围更大,它不检查对象是否是`可Pin`的 - + 我的实现`Pin`作为`GCHandle`的封装,并不能绕过`GCHandle`的API限制 - + 主要行为: struct Pin/static PinExtension - + 命名空间: Iks.BinaryToolkit.UnsafeToolkit.Pin - + 源码位置: 自己翻 - + 用途: 暂时pin住一个对象,用于与非托管代码交互,主要提供语法简洁性、额外的null检查、using自动释放 + + 为NET10以下版本提供一个带Dispose方法的GCHandle + + + NET10以上请使用标准库[PinnedGCHandle\](https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.pinnedgchandle-1?view=net-10.0) + + 行为与NET10的`PinnedGCHandle`有些不一致,`PinnedGCHandle`类型范围更大,它不检查对象是否是`可Pin`的 + + 我的实现`Pin`作为`GCHandle`的封装,并不能绕过`GCHandle`的API限制 + + 主要行为: struct Pin/static PinExtension + + 命名空间: Iks.BinaryToolkit.UnsafeToolkit.Pin + + 源码位置: 自己翻 + + 用途: 暂时pin住一个对象,用于与非托管代码交互,主要提供语法简洁性、额外的null检查、using自动释放 + > [!CAUTION] > Remember To Dispose It! + # Thinking + + ## 还没想好(大声) + ### 欢迎贡献 \ No newline at end of file diff --git a/README.md b/README.md index 3750c32..141a22e 100644 --- a/README.md +++ b/README.md @@ -1,38 +1,45 @@ # Feature: + #### [跳转到`Feature`](Feature.md) # Github-Actions + #### 可能我调的`actions`比我的库有用一点( + #### [跳转到我的`Actions`](.github/workflows/dotnet-build-publish.yml) + + 只需要为仓库添加`NUGET_API_KEY机密`并修改`env: project`即可使用 + **通过`打Tag`执行发布流程** - + `版本号` 就是 `Tag`名 - + `Preview版本`在`Tag`中后缀`preview` - + 自动生成符号包`snupkg`并推送 - + 您只需要关心`Icon`,`LICENSE`,`ReadMeFile`和`包描述`即可(都是IDE中很好指定的) + + `版本号` 就是 `Tag`名 + + `Preview版本`在`Tag`中后缀`preview` + + 自动生成符号包`snupkg`并推送 + + 您只需要关心`Icon`,`LICENSE`,`ReadMeFile`和`包描述`即可(都是IDE中很好指定的) + # 为什么我们需要泛型支持? -+ + ++ + `T Stream.ReadAs()` + 用于从Stream中读取一个非托管类型数据(如int,Int128) + `void Stream.Write(in T)` + 用于向Stream中写入一个非托管类型数据(如int,Int128) + ### 我们不需要将函数名当泛型使用的函数,请考虑以下场景 + 如果您需要实现一种二进制格式的数据的读写,如果您使用NET提供的蹩脚`BinaryReader`作为您的辅助 - ```csharp - class DataReader - { - BinaryReader _reader; - public int ReadInt(){ - return _reader.ReadInt(); - } - ... +```csharp +class DataReader +{ + BinaryReader _reader; + public int ReadInt(){ + return _reader.ReadInt(); } - ``` - 您会发现您被BinaryReader绑架了,要么你自己也各种`ReadInt`,`ReadShort`,要么你就得switch进行类型分发,现在,我替您完成了简单的类型读写操作 + ... +} +``` +您会发现您被BinaryReader绑架了,要么你自己也各种`ReadInt`,`ReadShort`,要么你就得switch进行类型分发,现在,我替您完成了简单的类型读写操作 + `Iks.Binary.EndianToolkit`理由同上,且有更严重的类型分发问题 - + 如果您采用了泛型,即使您将泛型参数类型限制到`INumber`,您也用不了`BinaryPrimitives`进行读写,如果您不希望使用非安全代码您可能会写出类似下面的东西 + + 如果您采用了泛型,即使您将泛型参数类型限制到`INumber`,您也用不了`BinaryPrimitives` + 进行读写,如果您不希望使用非安全代码您可能会写出类似下面的东西 ```csharp void Usage(T value) {