Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UnsafeRawPointer assumingMemoryBound vs. bindMemory

Can anyone explain what is difference between UnsafeRawPointer.assumimgMemoryBound(to:) and UnsafeRawPointer.bindMemory(to:capacity:)?

A practical example of compile or run time difference would be more then welcome.

Swift Doc says about bindMemory(to:capacity:):

This API allows a region of memory to hold unrelated types at different points in the program. Binding uninitialized memory to a type prepares the memory to store values of that type. Binding memory that is already initialized reinterprets the in-memory values as the new type. If the old values are either nontrivial (require destruction) or if they are ever read from memory before being overwritten, then the new type must be mutually layout compatible with the old type.

What does it mean Binding uninitialized memory to a type prepares the memory to store values of that type? It's allocated bytes, right? So what's different after bindMemory(to:capacity:) completes?

like image 768
simpleone Avatar asked Dec 22 '17 10:12

simpleone


1 Answers

Allocated memory in Swift can either be:

  • Uninitialised raw memory
  • Uninitialised memory that's bound to a type
  • Initialised memory bound to a type

When you allocate memory with UnsafeMutableRawPointer.allocate(bytes:alignedTo:), you get uninitialised raw memory.

When you allocate memory with UnsafeMutablePointer<T>.allocate(capacity:), you get uninitialised memory that's bound to the type T.

  • bindMemory(to:capacity:) (re-)binds a pointer's memory to a new type, and gives you back a typed pointer to access it with. It can be called on a pointer to memory in any of the above states; though if the memory is initialised, the new bound type must be layout compatible with the old bound type, and both types should be trivial.

    Note that this method doesn't perform allocation or initialisation; it merely changes the bound type of the memory.

  • assumingMemoryBound(to:) is a method for getting a typed pointer from a raw pointer that you already know points to memory bound to a given type. If the memory is not bound to this type, accessing memory through the typed pointer you get back is undefined behaviour.

One important thing to note here is that memory can only be bound to one type at a given time. You are free to rebind to other types (with the above restriction for initialised memory); however attempting to access memory bound to a given type as an unrelated type violates strict aliasing and is therefore undefined behaviour.

Another thing to note is that related types and layout compatible types are independent concepts:

  • The type T is layout compatible with the type U if memory bound to type U can be bitwise reinterpreted as having type T. Note that this isn't necessarily a bidirectional relationship. For example, Int is layout compatible with (Int, Int) if one 'instance' of (Int, Int) can be reinterpreted as being 2 x Int. The reverse can't be true though; you can't form an (Int, Int) value from a single Int.

  • Two types are related if you can alias overlapping memory with those types. For example, if you have an UnsafePointer<T> and an UnsafePointer<U>, if T and U are unrelated types, then they cannot point to memory that overlaps with each other.

However, I don't believe Swift has officially defined any rules for these terms yet (I expect this'll come with ABI stability).

So what's different after bindMemory(to:capacity:) completes?

Currently, nothing. As Andrew Trick says in the mailing list discussion that Martin linked to:

Binding memory communicates to the compiler that the memory locations are safe for typed access. Nothing happens at runtime--until someone writes a type safety sanitizer. It affects the abstract state of the memory location, independent of the pointer variable used to access that memory. Binding memory returns a typed pointer for convenience and clarity, but there’s nothing special about that particular pointer value.

For further reading on this subject, see the memory model explanation section of SE-0107, as well as this unofficial guide to strict aliasing in Swift.

like image 131
Hamish Avatar answered Oct 12 '22 03:10

Hamish