-
Notifications
You must be signed in to change notification settings - Fork 189
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
Review UnsafeRawPointer projections in C# for CryptoKit dev templates #2529
Comments
Is this supposed to wrap https://developer.apple.com/documentation/cryptokit/chachapoly ? I do not see the mapping between this cryptokit API and what you have above. |
Yes, but in the next iterations. This should review and implement the existing bindings in https://github.com/dotnet/runtime/blob/main/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.swift. |
I think
This will fail to compile with CS0575: Only class types can contain destructors. Also, according to https://developer.apple.com/documentation/swift/unsaferawbufferpointer, |
What if a Swift method has two overloads, one with More generally, this can happen if we map any two or more Swift types into the same .NET type. We had this problem in our Objective-C bindings since we bound the Objective-C type public byte* SwiftData { get; set; }
public Unsafe[Mutable]RawPointer(void* p)
{
SwiftData = (byte*)p;
} why is the ctor taking a Maybe we should also add explicit operators to/from // <summary>
// Represents init(start: UnsafeRawPointer?, count: Int)
// https://developer.apple.com/documentation/swift/unsaferawbufferpointer/init(start:count:)
// </summary>
[DllImport("libSwiftCore.dylib", EntryPoint = "$sSR5start5countSRyxGSPyxGSg_Sitcfc")]
internal static extern void* _Initialize (void* start, int count); The Swift |
I agree that we should have a specific reason for mapping
Thanks, you are right! Here are details:
The // <summary>
// Represents Swift Unsafe[Mutable]RawBufferPointer in C#.
// </summary>
public unsafe struct Unsafe[Mutable]RawBufferPointer : ISwiftStruct
{
public void* SwiftData { get; set; }
public Unsafe[Mutable]RawBufferPointer(void* start, nint count)
{
SwiftData = init(start, count);
}
// Explicit conversion from Unsafe[Mutable]RawBufferPointer to void*
public static explicit operator void*(Unsafe[Mutable]RawBufferPointer buffer)
{
return buffer.SwiftData;
}
// <summary>
// Represents init(start: UnsafeRawPointer?, count: Int)
// https://developer.apple.com/documentation/swift/unsaferawbufferpointer/init(start:count:)
// </summary>
[DllImport("libSwiftCore.dylib", EntryPoint = "$sSR5start5countSRyxGSPyxGSg_Sitcfc")]
internal static extern void* init(void* start, nint count);
} |
Ok, we can have a thin struct wrapper with default conversion to/from
Is |
The init function does not allocate any new memory itself; it creates a buffer and provides a view into the memory referenced by
I agree. If it utilizes |
I am not following. This function takes two pointer-sized pieces of information and creates one pointer-sized piece of information from those. How can it do that without allocating new memory? |
I may be mistaken, but here is my understanding of the flow: .NET allocates memory and passes its pointer and count to the Swift _position = start
_end = start.map { $0 + _assumeNonNegative(count) } The constructor should return an instance of BTW I added support for |
Yes, that makes sense. |
Thank you for navigating the discussion. It appears that projecting buffer pointers might be simpler than initially prototyped. Consider the following Swift example: public func testBuffer(buffer: UnsafeRawBufferPointer) {
print("testBuffer.baseAddress: \(buffer.baseAddress)");
print("testBuffer.count: \(buffer.count)");
} And the corresponding LLVM-IR: define protected swiftcc void @"$s6output10testBuffer6bufferySW_tF"(i64 %0, i64 %1) #0 !dbg !37 {
entry:
%buffer.debug = alloca %TSW, align 8
%buffer.debug._position = getelementptr inbounds %TSW, ptr %buffer.debug, i32 0, i32 0, !dbg !49
store i64 %0, ptr %buffer.debug._position, align 8, !dbg !49
%buffer.debug._end = getelementptr inbounds %TSW, ptr %buffer.debug, i32 0, i32 1, !dbg !49
store i64 %1, ptr %buffer.debug._end, align 8, !dbg !49 The function accepts two // Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System.Runtime.InteropServices;
namespace Swift.Runtime
{
// <summary>
// Represents Swift Unsafe[Mutable]RawBufferPointer in C#.
// </summary>
public unsafe struct UnsafeRawBufferPointer
{
public void* Position { get; set; }
public void* End { get; set; }
public UnsafeRawBufferPointer(void* start, nint count)
{
Position = start;
End = (byte*)start + count;
}
// Explicit conversion from Unsafe[Mutable]RawBufferPointer to void*
public static explicit operator void*(UnsafeRawBufferPointer buffer)
{
return buffer.Position;
}
}
} I managed to confirm it through manual lowering: unsafe {
byte[] key = RandomNumberGenerator.GetBytes(32);
fixed (void* pKey = key){
var testBuffer = new UnsafeRawBufferPointer(pKey, key.Length);
HelloLibrary.EncryptBuffer(testBuffer.Position, testBuffer.End);
}
}
[DllImport("libHelloLibrary.dylib", EntryPoint = "$s12HelloLibrary13EncryptBuffer04testD05countySW_s5Int32VtF")]
internal static extern void PIfunc_EncryptBuffer(void* position, void* end);
public static void EncryptBuffer(void* position, void* end)
{
PIfunc_EncryptBuffer(position, end);
}
// Swift output
// testBuffer.baseAddress: Optional(0x000000013800d500)
// testBuffer.count: 32 |
I do not think we want to have explicit conversion operator for this. People should just call the Pointer property to get the buffer pointer. |
Are types like |
This should not be called |
I recommend using the same private fields as those in the Swift, and surface them as Based on the current design and usage expectations, I don't think it is necessary to implement iterator support for "typed" buffers ( Feel free to adjust the code snippet if needed. // Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System.Runtime.InteropServices;
namespace Swift.Runtime
{
// <summary>
// Represents Swift UnsafeRawBufferPointer in C#.
// </summary>
public unsafe readonly struct UnsafeRawBufferPointer
{
private readonly void* _Position;
private readonly void* _End;
public UnsafeRawBufferPointer(void* start, nint count)
{
_Position = start;
_End = (byte*)start + count;
}
public void* BaseAddress => _Position;
public nint Count => (nint)((byte*)_End - (byte*)_Position);
}
// <summary>
// Represents Swift UnsafeMutableRawBufferPointer in C#.
// </summary>
public unsafe readonly struct UnsafeMutableRawBufferPointer
{
private readonly void* _Position;
private readonly void* _End;
public UnsafeMutableRawBufferPointer(void* start, nint count)
{
_Position = start;
_End = (byte*)start + count;
}
public void* BaseAddress => _Position;
public nint Count => (nint)((byte*)_End - (byte*)_Position);
}
// <summary>
// Represents Swift UnsafeBufferPointer in C#.
// </summary>
public unsafe readonly struct UnsafeBufferPointer
{
private readonly void* _Position;
public readonly nint Count;
public UnsafeBufferPointer(void* start, nint count)
{
_Position = start;
Count = count;
}
public void* BaseAddress => _Position;
}
// <summary>
// Represents Swift UnsafeMutableBufferPointer in C#.
// </summary>
public unsafe readonly struct UnsafeMutableBufferPointer
{
private readonly void* _Position;
public readonly nint Count;
public UnsafeMutableBufferPointer(void* start, nint count)
{
_Position = start;
Count = count;
}
public void* BaseAddress => _Position;
}
} |
@kotlarmilos can we make all of the buffer types define |
Thanks! |
I think all these structs should be readonly struct. readonly applies only to the struct itself, not to the memory that it points to. The difference between UnsafeBufferPointer and UnsafeMutableBufferPointer is whether the memory that it points to is mutable, not whether the pointer itself is mutable.
These are generic over TElement in Swift. (https://developer.apple.com/documentation/swift/unsafebufferpointer/). How are we going to deal with the genericness of this type? |
Makes sense, updated.
What are the implications here? Do we need to implement them as generics? Maybe we can reduce the scope and consider adding |
If you map multiple Swift types to a single .NET type, you will run into the potential problems with overloading that were mentioned earlier. Consider a |
Thanks, we can implement thin wrappers to handle overload cases. |
Support for Surfacing https://developer.apple.com/documentation/cryptokit/chachapoly enum and https://developer.apple.com/documentation/cryptokit/symmetrickey struct will be discussed in a subsequent issue. Thank you all for valuable feedback! |
Description
This issue is intended to discuss projections of UnsafeRawPointers in C# and their marshalling, with a focus on evaluating correctness and scalability. The goal is to review and confirm the design of the projections and use them to create CryptoKit dev templates.
Projections of Swift unsafe raw pointers in C#:
Generated C# bindings:
User's C# code:
/cc: @stephen-hawley @rolfbjarne @AaronRobinsonMSFT @jkoritzinsky @vitek-karas
The text was updated successfully, but these errors were encountered: