Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pool or otherwise remove some buffers in StringAsPinnedUTF8 on NET6+ #2208

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
40 changes: 37 additions & 3 deletions src/Confluent.Kafka/Internal/Util.cs
Expand Up @@ -19,6 +19,8 @@
using SystemMarshal = System.Runtime.InteropServices.Marshal;
using SystemGCHandle = System.Runtime.InteropServices.GCHandle;
using SystemGCHandleType = System.Runtime.InteropServices.GCHandleType;
using System.Buffers;
using System.Runtime.InteropServices;


namespace Confluent.Kafka.Internal
Expand All @@ -31,23 +33,55 @@ internal static class Marshal
/// Convenience class for generating and pinning the UTF8
/// representation of a string.
/// </summary>
public class StringAsPinnedUTF8 : IDisposable
public sealed class StringAsPinnedUTF8 : IDisposable
{
private SystemGCHandle gch;
private readonly GCHandle gch;

#if NET6_0_OR_GREATER
private readonly byte[] strBytesNulTerminated;
#endif

public StringAsPinnedUTF8(string str)
{
#if NET6_0_OR_GREATER
var size = Encoding.UTF8.GetMaxByteCount(str.Length);

// Ask for one extra for the null byte
strBytesNulTerminated = ArrayPool<byte>.Shared.Rent(size + 1);

try
{
Span<byte> slice = strBytesNulTerminated.AsSpan().Slice(0, size);
int bytesWritten = Encoding.UTF8.GetBytes(str, slice);

// 0-init the remainder
Array.Clear(strBytesNulTerminated, bytesWritten, strBytesNulTerminated.Length - bytesWritten);
}
catch
{
// An exception above means the object never creates, potentially leaving a
// memory leak as there would be no object to Dispose() of later
ArrayPool<byte>.Shared.Return(strBytesNulTerminated);
throw;
}

this.gch = GCHandle.Alloc(strBytesNulTerminated, GCHandleType.Pinned);
#else
byte[] strBytes = System.Text.UTF8Encoding.UTF8.GetBytes(str);
byte[] strBytesNulTerminated = new byte[strBytes.Length + 1]; // initialized to all 0's.
Array.Copy(strBytes, strBytesNulTerminated, strBytes.Length);
this.gch = SystemGCHandle.Alloc(strBytesNulTerminated, SystemGCHandleType.Pinned);
this.gch = GCHandle.Alloc(strBytesNulTerminated, GCHandleType.Pinned);
#endif
}

public IntPtr Ptr { get => this.gch.AddrOfPinnedObject(); }

public void Dispose()
{
gch.Free();
#if NET6_0_OR_GREATER
ArrayPool<byte>.Shared.Return(strBytesNulTerminated);
#endif
}
}

Expand Down