I suggest we ...

Additional intrinsics for the NativePtr module

When interoperating with native code, it would be handy if the NativePtr module included some additional "intrinsic" functions for taking advantage of low-level IL instructions; specifically, I'd like to be able to use 'cpblk', 'initblk', 'initobj', and 'copyobj'.

It would also be nice to have an easy way of checking for null pointer values.

Example implementation of these functions:

[<RequireQualifiedAccess>]
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module NativePtr =
[<GeneralizableValue>]
[<NoDynamicInvocation>]
[<CompiledName("Zero")>]
let inline zero<'T when 'T : unmanaged> : nativeptr<'T> =
(# "ldnull" : nativeptr<'T> #)

[<NoDynamicInvocation>]
[<CompiledName("IsNull")>]
let inline isNull<'T when 'T : unmanaged> (ptr : nativeptr<'T>) =
(# "ceq" zero<'T> ptr : bool #)

[<Unverifiable>]
[<NoDynamicInvocation>]
[<CompiledName("InitBlockInlined")>]
let inline initBlock (p : nativeptr<'T>) (value : byte) (size : uint32) =
(# "initblk" p value size #)

[<Unverifiable>]
[<NoDynamicInvocation>]
[<CompiledName("CopyBlockInlined")>]
let inline memcpy (destPtr : nativeptr<'T>) (srcPtr : nativeptr<'T>) (count : int) =
(# "cpblk" destPtr srcPtr (count * sizeof<'T>) #)

[<Unverifiable>]
[<NoDynamicInvocation>]
[<CompiledName("InitPointerInlined")>]
let inline clear (p : nativeptr<'T>) =
(# "initobj !0" type ('T) p #)

[<Unverifiable>]
[<NoDynamicInvocation>]
[<CompiledName("CopyPointerInlined")>]
let inline copy (destPtr : nativeptr<'T>) (srcPtr : nativeptr<'T>) =
(# "copyobj !0" type ('T) destPtr srcPtr #)

10 votes
Vote
Sign in
Check!
(thinking…)
Reset
or sign in with
  • facebook
  • google
    Password icon
    I agree to the terms of service
    Signed in as (Sign out)
    You have left! (?) (thinking…)
    Jack PappasJack Pappas shared this idea  ·   ·  Admin →

    8 comments

    Sign in
    Check!
    (thinking…)
    Reset
    or sign in with
    • facebook
    • google
      Password icon
      I agree to the terms of service
      Signed in as (Sign out)
      Submitting...
      • Frank NiemeyerFrank Niemeyer commented  · 

        @Sebastian: The only thing that F# is missing for this is the "fixed" statement (you'd have to pin the byte[] by allocating a GCHandle). The dereferencing (i.e. copy the contents of the byte array to a struct of type T) can achieved today using p |> NativePtr.toNativeInt |> NativePtr.ofNativeInt<'T> |> NativePtr.read.

      • Anonymous commented  · 

        In this vein, it would be useful to be able to easily use what amounts to a binary cast from a byte array, as in C# (fixed byte* p = &b[offset]) { return *(SomeUsefulStruct*)p; }

      • Will SmithWill Smith commented  · 

        Yea, it was only recently did I found out that nativeptr<'T> is really just a nativeint. Thanks for giving more info on it.

        You make a good point on the type safety, but as we are already in an unsafe context and potentially messing with unmanaged memory, a NativePtr.cast would only benefit handling memory. Is there a better way of going about it? I would interested in an alternative.

      • Jack PappasJack Pappas commented  · 

        Will -- nativeptr<'T> is just nativeint with a generic type annotation grafted onto it, and it's erased to nativeint at compile-time. Implementing a cast function like you described should be straightforward if you use the F# proto-compiler. IMO it wouldn't be a good addition to FSharp.Core though, because it would totally bypass any and all type-safety. In any case, the difference between your cast function and the intrinsics I listed above -- you can implement your cast with the current version of F#, but the above intrinsics can't be compiled due to some changes that were made to the ILX layer and metadata picker in F# 3.0.

      F# Language

      Feedback and Knowledge Base