From d719ef32cecf8f9f81e6b9027a292a3b94ff0e0e Mon Sep 17 00:00:00 2001 From: Jeremy Kuhne Date: Fri, 2 Feb 2024 19:06:16 -0800 Subject: [PATCH] Add a few more buffer related helpers and update StyleCop --- src/thirtytwo/Support/BufferScope.cs | 2 + src/thirtytwo/Support/SpanExtensions.cs | 19 ++++++ src/thirtytwo/Support/StackBufferScope16.cs | 46 +++++++++++++++ src/thirtytwo/Win32/Foundation/BstrBuffer.cs | 58 +++++++++++++++++++ src/thirtytwo/thirtytwo.csproj | 2 +- .../Win32/Foundation/BstrBufferTests.cs | 29 ++++++++++ 6 files changed, 155 insertions(+), 1 deletion(-) create mode 100644 src/thirtytwo/Support/StackBufferScope16.cs create mode 100644 src/thirtytwo/Win32/Foundation/BstrBuffer.cs create mode 100644 src/thirtytwo_tests/Win32/Foundation/BstrBufferTests.cs diff --git a/src/thirtytwo/Support/BufferScope.cs b/src/thirtytwo/Support/BufferScope.cs index c45a434..640feb5 100644 --- a/src/thirtytwo/Support/BufferScope.cs +++ b/src/thirtytwo/Support/BufferScope.cs @@ -107,6 +107,8 @@ public unsafe void EnsureCapacity(int capacity, bool copy = false) public static implicit operator ReadOnlySpan(BufferScope scope) => scope._span; + public readonly Span.Enumerator GetEnumerator() => _span.GetEnumerator(); + public void Dispose() { if (_array is not null) diff --git a/src/thirtytwo/Support/SpanExtensions.cs b/src/thirtytwo/Support/SpanExtensions.cs index e00593e..f735387 100644 --- a/src/thirtytwo/Support/SpanExtensions.cs +++ b/src/thirtytwo/Support/SpanExtensions.cs @@ -42,4 +42,23 @@ public static IEnumerable Split(this ReadOnlySpan span, char delim return strings; } + + /// + /// Converts a span of s to an array of ; + /// + public static string[] ToStringArray(this ReadOnlySpan bstrs) + { + if (bstrs.IsEmpty) + { + return []; + } + + string[] strings = new string[bstrs.Length]; + for (int i = 0; i < bstrs.Length; i++) + { + strings[i] = bstrs[i].ToString(); + } + + return strings; + } } \ No newline at end of file diff --git a/src/thirtytwo/Support/StackBufferScope16.cs b/src/thirtytwo/Support/StackBufferScope16.cs new file mode 100644 index 0000000..18270f2 --- /dev/null +++ b/src/thirtytwo/Support/StackBufferScope16.cs @@ -0,0 +1,46 @@ +// Copyright (c) Jeremy W. Kuhne. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Runtime.CompilerServices; + +namespace Windows.Support; + +/// +/// Simple buffer that has 16 s on the stack. If the buffer needs to be larger than 16 +/// elements it will be allocated on the heap. +/// +/// +/// +/// This type cannot be nested in another ref type as it will result in it getting initialized in a copy which will +/// make the data in the buffer point to random stack data. +/// +/// +public unsafe ref struct StackBufferScope16 where T : unmanaged +{ + private const int StackSpace = 16; + + private StackBuffer _stackBuffer; + private BufferScope _bufferScope; + + public StackBufferScope16(int length) + { +#pragma warning disable CS9084 // Struct member returns 'this' or other instance members by reference + _bufferScope = new BufferScope(_stackBuffer, length); +#pragma warning restore CS9084 + } + + public ref T this[int i] => ref _bufferScope[i]; + + public readonly Span this[Range range] => _bufferScope[range]; + public readonly int Length => _bufferScope.Length; + + public readonly Span.Enumerator GetEnumerator() => _bufferScope.GetEnumerator(); + public readonly ref T GetPinnableReference() => ref _bufferScope.GetPinnableReference(); + public void Dispose() => _bufferScope.Dispose(); + + [InlineArray(StackSpace)] + private struct StackBuffer + { + internal T _element0; + } +} \ No newline at end of file diff --git a/src/thirtytwo/Win32/Foundation/BstrBuffer.cs b/src/thirtytwo/Win32/Foundation/BstrBuffer.cs new file mode 100644 index 0000000..4ac8673 --- /dev/null +++ b/src/thirtytwo/Win32/Foundation/BstrBuffer.cs @@ -0,0 +1,58 @@ +// Copyright (c) Jeremy W. Kuhne. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Runtime.CompilerServices; +using Windows.Support; + +namespace Windows.Win32.Foundation; + +/// +/// Buffer specficially for allocating an array of s. Disposing the buffer will free all +/// of the contained s. +/// +/// +/// +/// This type cannot be nested in another ref type as it will result in it getting initialized in a copy which will +/// make the data in the buffer point to random stack data. +/// +/// +public unsafe ref struct BstrBuffer +{ + private const int StackSpace = 16; + + private StackBuffer _stackBuffer; + private BufferScope _bufferScope; + +#pragma warning disable CS9084 // Struct member returns 'this' or other instance members by reference + public BstrBuffer(int length) => _bufferScope = new(_stackBuffer, length); +#pragma warning restore CS9084 + + public readonly ref BSTR GetPinnableReference() => ref _bufferScope.GetPinnableReference(); + + public void Clear() + { + for (int i = 0; i < _bufferScope.Length; i++) + { + _bufferScope[i].Dispose(); + } + } + + public ref BSTR this[int i] => ref _bufferScope[i]; + + public readonly Span this[Range range] => _bufferScope[range]; + + public static implicit operator BSTR*(in BstrBuffer scope) => + (BSTR*)Unsafe.AsPointer(ref Unsafe.AsRef(in scope._stackBuffer._element0)); + + public void Dispose() + { + Clear(); + _bufferScope.Dispose(); + } + + [InlineArray(StackSpace)] + private struct StackBuffer + { + internal BSTR _element0; + } +} \ No newline at end of file diff --git a/src/thirtytwo/thirtytwo.csproj b/src/thirtytwo/thirtytwo.csproj index d8e229e..d471a4d 100644 --- a/src/thirtytwo/thirtytwo.csproj +++ b/src/thirtytwo/thirtytwo.csproj @@ -11,7 +11,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/thirtytwo_tests/Win32/Foundation/BstrBufferTests.cs b/src/thirtytwo_tests/Win32/Foundation/BstrBufferTests.cs new file mode 100644 index 0000000..904a1bc --- /dev/null +++ b/src/thirtytwo_tests/Win32/Foundation/BstrBufferTests.cs @@ -0,0 +1,29 @@ +// Copyright (c) Jeremy W. Kuhne. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Windows.Win32.Foundation; + +public class BstrBufferTests +{ + [Fact] + public unsafe void BstrBuffer_Basics() + { + BSTR bstr = new("Foo"); + using BstrBuffer buffer = new(2); + buffer[0] = bstr; + ((nint)buffer[0].Value).Should().Be((nint)bstr.Value); + buffer[0].Dispose(); + buffer[0].IsNull.Should().BeTrue(); + } + + [Fact] + public void BstrBuffer_Clear_FreesBstrs() + { + using BstrBuffer buffer = new(2); + buffer[0] = new("Foo"); + buffer[1] = new("Bar"); + buffer.Clear(); + buffer[0].IsNull.Should().BeTrue(); + buffer[1].IsNull.Should().BeTrue(); + } +}