Passing (possibly non-contiguous) array to a C API

Does passing an array (or the address of an array) to a C API pass the address of contiguous (mutable) element storage?

The Swift Blog article says Interacting with C Pointers says that

An immutable array value can be passed directly as a const pointer, and a mutable array can be passed as a non-const pointer argument using the & operator,

with the example

let a: [Float] = [1, 2, 3, 4]
let b: [Float] = [0.5, 0.25, 0.125, 0.0625]
var result: [Float] = [0, 0, 0, 0]
vDSP_vadd(a, 1, b, 1, &result, 1, 4)

On the other hand, Calling Functions With Pointer Parameters is less clear to me:

When you call a function that is declared as taking an UnsafePointer<Type> argument, you can pass any of the following:

  • A [Type] value, which is passed as a pointer to the start of the array.

The elements of an Array need not be stored contiguously in memory. Is “pointer to the start of the array” here guaranteed to be a “pointer to contiguous element storage” or only a “pointer to the initial array element”?

In other words, is it really safe to pass an array directly to a C API, or should withUnsafe(Mutable)BufferPointer(_:) be used?

I think it was said somewhere in this forum that passing an array (or the address of an array) to a C API passes the address of contiguous (mutable) element storage, but I cannot find that anymore. Some clarification would be appreciated.

3 Likes

For what it’s worth, the documentation for ContiguousArray tells us that Array will:

... store its elements in either a contiguous region of memory or an NSArray instance if its Element type is a class or @objc protocol.

So it would appear that the non-contiguous behavior of Array is limited to arrays of class instances or @objc protocols. So my apprehension (about the possible non-contiguous storage of arrays of Single, Double, etc., value types) would appear to be misplaced. If I’m reading this right, it looks like Swift arrays of those types will be contiguous. It would appear that only arrays of reference types can end up in non-contiguous storage.

I think I may have sent you down the wrong path with my prior comments. Sorry!

When emitting an array-to-pointer conversion, the compiler emits a call to _convert[Const/Mutable]ArrayToPointerArgument in order to get the pointer value. These call through to Array's _cPointerArgs method which creates contiguous storage if its storage isn't already continuous.

So yes, the resulting pointer is to a contiguous buffer of the array's elements. I can't find any documentation that makes this a guarantee, but this does seem to be a pretty important guarantee for the array-to-pointer conversion to provide (otherwise you'd run into very subtle cases of undefined behaviour).

5 Likes

Can confirm: even if the precise implementation changes, the array will always be forced into a contiguous form before being turned into a pointer; if it's being done so immutably, this may require a copy that gets thrown away.

(There is a scenario in which this is a performance concern, the same way as it can be for Strings! But it's a lot less likely to come up in practice. Still, maybe we'll want contiguity detection for Array at some point too.)

5 Likes