[swift-users] Swift-C array interop with Swift 4/Xcode 9

Braden Scothern bradenscothern at gmail.com
Thu Sep 21 10:46:55 CDT 2017


Hey Chris,

So I work with a C library and Swift as well and have a couple of ideas for
you depending on how you work with the array. Here are some tricks I have
used when passing around arrays from C to Swift to ObjC and never had
issues with it. Also, while passing in the first element of the tuple is
fine since the first element has the same address as the entire array in C,
I would just use the base tuple instead as it is more Swifty.

You will have to specify the result type of the functions but it is better
than using several closures to get to where you want to work with stuff.
Removing closures will also help you debug it and see if something else is
going on because if you are passing in the first element all the way down
and not modifying the pointer your logic should be correct.

If you don't mind making a deep copy into a native Swift array you can use
a function like this to work with any C array:


func tupleValuesToArray<T, U>(_ tuple: T) -> U {
    var toReturn: [U] = []
    for (_, value) in Mirror(reflecting: tuple).children {
        if let value = value as? U {
            toReturn.append(value)
        } else {
            fatalError("Failed to convert \(tuple) into an array")
        }
    }
    return toReturn
}

let swiftArray: [Float] = tupleValuesToArray(comeCArray)


If you need to mutate the array or if you just don't want to copy it and/or
want to access it as a sequence you should use an
Unsafe[Mutable]BufferPointer. You can convert to one with this function:


func tupleToUnsafe[Mutable]Buffer<T, U>(_ tuple: Unsafe[Mutable]Pointer<T>
-> Unsafe[Mutable]BufferPointer<U> {
    let mirror = Mirror(reflecting: tuple.poinee)
    return Unsafe[Mutable]BufferPointer<U>(start:
Unsafe[Mutable]Pointer(OpaquePointer(tuple)), count:
Int(mirror.children.count))
}

let buffer: UnsafeMutableBufferPointer<Float> =
tupleToUnsafeMutableBuffer(&cArrayAsTuple)


So there are a few things to point out with this function. You have to get
around type checking by converting to an OpaquePointer and back in order to
cast from T -> U.. There are other solutions to get around the type checks
but this is the one I often use even though I dislike it...

Anyways, once you have a buffer you should then be able to pass in the
baseAddress (and count) of the buffer to ObjC and have it work fine so long
as no pointer math is messing it up down inside the other parts of your
code.



On Thu, Sep 21, 2017 at 7:33 AM, Johannes Weiß via swift-users <
swift-users at swift.org> wrote:

> Hi Chris,
>
> Yes, he'll need to do
>
> withUnsafeMutablePointer(to: &inReq.imageInfo.transformation) {
> transformationTuplePtr in
>    withUnsafeMutablePointer(to: &inReq.imageInfo.projection) {
> projectionTuplePtr in
>        transformationTuplePtr.withMemoryRebound(to: Float.self, capacity:
> 16) { transformationArrayPtr in
>            projectionTuplePtr.withMemoryRebound(to: Float.self, capacity:
> 16) { projectionArrayPtr in
>                ....imageReceived(..., transform: transformationArrayPtr,
> projection: projectionArrayPtr, ...)
>            }
>        }
>    }
> }
>
> if you need access to 5 of them, you'll need to nest deeper, apologies.
>
> However, there's talk on swift-evolution about getting fixed-size arrays
> into Swift which would help here. But there's no full proposal yet AFAIK.
>
>
> -- Johannes
>
> > On 21 Sep 2017, at 2:27 pm, Chris McIntyre <
> nothingwasdelivered at gmail.com> wrote:
> >
> > Sorry to jump in here, and maybe I’m missing something obvious, but in
> your example Rick is two levels of closure deep and only has access to one
> of the arrays in his struct. In this case there is another array of floats
> he needs as an argument to his method call. Will he need to go through the
> same steps, going two levels of closure deeper, before he has access to
> both struct members?
> >
> > This doesn’t seem to scale very well. What about a struct with 5 arrays?
> > --
> > Chris McIntyre
> >
> >
> >
> >
> >> On Sep 21, 2017, at 6:05 AM, Johannes Weiß via swift-users <
> swift-users at swift.org> wrote:
> >>
> >> Hi Rick,
> >>
> >>> On 21 Sep 2017, at 1:03 am, Rick Mann via swift-users <
> swift-users at swift.org> wrote:
> >>>
> >>> I've got Swift code wrapping a C API. One of the C structs the API
> uses looks like this:
> >>>
> >>> typedef struct {
> >>>   size_t size;
> >>>   float transformation[16];
> >>>   float projection[16];
> >>>   uint32_t width;
> >>>   uint32_t height;
> >>> } image_info_t;
> >>>
> >>> In Swift, the two array members are 16-tuples of floats. Eventually,
> the Swift code calls an Objective-C delegate, passing a tuple to an array:
> >>>
> >>> self.delegate?
> >>>   .imageReceived(self,
> >>>                   index: inReq.imageIndex,
> >>>                   data: data,
> >>>                   width: width,
> >>>                   height: height,
> >>>                   transform: &inReq.imageInfo.transformation.0,
> >>>                   projection: &inReq.imageInfo.projection.0)
> >>
> >> these are illegal. You're getting a pointer to _only_ the first element
> (&inReq.imageInfo.transformation.0) but the library will then read 16
> elements from there.
> >>
> >> Something like this should work (and is legal):
> >>
> >> withUnsafeMutablePointer(to: &inReq.imageInfo.transformation) {
> tupleOfFloatsPtr in
> >>    tupleOfFloatsPtr.withMemoryRebound(to: Float.self, capacity: 16) {
> arrayOfFloatsPtr in
> >>        ....imageReceived(..., transform: arrayOfFloatsPtr, ...)
> >>    }
> >> }
> >>
> >> that's legal because now we're capturing a pointer to the whole tuple
> and therefore Swift guarantees the memory to be in C standard layout.
> >>
> >>
> >>
> >>>
> >>> This seems to have been working fine for a long time, but we're
> running into a crash in some cases, and so I've been digging into it. I
> turned on the address sanitizer, and I consistently get it to trip in some
> C++ code that eventually gets called with a float* to that transform tuple.
> >>>
> >>> Now, in Swift, the values of the tuple look great:
> >>>
> >>> ▿ 16 elements
> >>> - .0 : 0.548282921
> >>> - .1 : 0.821425974
> >>> - .2 : -0.156990349
> >>> - .3 : 0.0
> >>> - .4 : -0.277842015
> >>> - .5 : 0.00185899995
> >>> - .6 : -0.960625231
> >>> - .7 : 0.0
> >>> - .8 : -0.788789928
> >>> - .9 : 0.570312977
> >>> - .10 : 0.229245573
> >>> - .11 : 0.0
> >>> - .12 : 0.0475889929
> >>> - .13 : -0.0323969983
> >>> - .14 : -0.0113521963
> >>> - .15 : 1.0
> >>>
> >>> I can see this by clicking up the stack a couple frames to the Swift
> code. But the Objective-C(++) delegate code, that gets passed a float*,
> sees this (BTW, I can't figure out how to get the debugger, Xcode or
> command line, to display inTransform as an array of 16 floats). It's
> clearly not the same array.
> >>>
> >>>
> >>> (lldb) po inTransform[0]
> >>> 0.548282921
> >>>
> >>> (lldb) po inTransform[1]
> >>> 0.0343905278
> >>>
> >>> (lldb) po inTransform[2]
> >>> -0
> >>>
> >>> (lldb) po inTransform[3]
> >>> 0
> >>>
> >>> (lldb) po inTransform[4]
> >>> 0
> >>>
> >>> (lldb) po inTransform[5]
> >>> 0
> >>>
> >>> (lldb) po inTransform[6]
> >>> 95488384
> >>>
> >>> (lldb) po inTransform[14]
> >>> 0.00000000000000000000000000000000000000000000560519386
> >>>
> >>> (lldb) po inTransform[15]
> >>> 0
> >>>
> >>> The address sanitizer spits out this:
> >>>
> >>> =================================================================
> >>> ==1358==ERROR: AddressSanitizer: stack-buffer-overflow on address
> 0x00014d3ea224 at pc 0x000100eb9a84 bp 0x00016fd84bd0 sp 0x00016fd84bc8
> >>> READ of size 4 at 0x00014d3ea224 thread T0
> >>>   #0 0x100eb9a83 in <redacted>::addImage(void const*, int, int, int,
> float const*, float const*) (<redacted>:arm64+0x100e41a83)
> >>>   #1 0x10040b72b in -[<redacted> imageReceived:index:data:
> width:height:transform:projection:] (<redacted>:arm64+0x10039372b)
> >>>   #2 0x10045d037 in function signature specialization <Arg[0] = Owned
> To Guaranteed, Arg[1] = Owned To Guaranteed, Arg[2] = Owned To Guaranteed
> and Exploded> of closure #1 () -> () in <redacted>.imageDownloaded(<
> redacted>.DownloadImageRequest, index: Swift.Int) -> ()
> (<redacted>:arm64+0x1003e5037)
> >>>   #3 0x100442bbb in closure #1 () -> () in <redacted>.imageDownloaded(<
> redacted>.DownloadImageRequest, index: Swift.Int) -> ()
> (<redacted>:arm64+0x1003cabbb)
> >>>   #4 0x10043e7fb in reabstraction thunk helper from @callee_owned ()
> -> () to @callee_unowned @convention(block) () -> ()
> (<redacted>:arm64+0x1003c67fb)
> >>>   #5 0x103ba9d5b in __wrap_dispatch_async_block_invoke
> (/var/containers/Bundle/Application/B3E557CA-5ED9-4673-8B07-37A0F2DA472B/<
> redacted>.app/Frameworks/libclang_rt.asan_ios_dynamic.dylib:arm64+0x4dd5b)
> >>>   #6 0x105f1549b in _dispatch_call_block_and_release (/usr/lib/system/
> introspection/libdispatch.dylib:arm64+0x149b)
> >>>   #7 0x105f1545b in _dispatch_client_callout (/usr/lib/system/
> introspection/libdispatch.dylib:arm64+0x145b)
> >>>   #8 0x105f1a04f in _dispatch_main_queue_callback_4CF
> (/usr/lib/system/introspection/libdispatch.dylib:arm64+0x604f)
> >>>   #9 0x181d43f1f in <redacted> (/System/Library/Frameworks/
> CoreFoundation.framework/CoreFoundation:arm64+0xe9f1f)
> >>>   #10 0x181d41afb in <redacted> (/System/Library/Frameworks/
> CoreFoundation.framework/CoreFoundation:arm64+0xe7afb)
> >>>   #11 0x181c622d7 in CFRunLoopRunSpecific (/System/Library/Frameworks/
> CoreFoundation.framework/CoreFoundation:arm64+0x82d7)
> >>>   #12 0x183af3f83 in GSEventRunModal (/System/Library/
> PrivateFrameworks/GraphicsServices.framework/
> GraphicsServices:arm64+0xaf83)
> >>>   #13 0x18b20e87f in UIApplicationMain (/System/Library/Frameworks/
> UIKit.framework/UIKit:arm64+0x7387f)
> >>>   #14 0x100122dc3 in main (<redacted>:arm64+0x1000aadc3)
> >>>   #15 0x18178656b in <redacted> (/usr/lib/system/libdyld.
> dylib:arm64+0x156b)
> >>>
> >>> Address 0x00014d3ea224 is located in stack of thread T0 at offset 36
> in frame
> >>>   #0 0x10045cd57 in function signature specialization <Arg[0] = Owned
> To Guaranteed, Arg[1] = Owned To Guaranteed, Arg[2] = Owned To Guaranteed
> and Exploded> of closure #1 () -> () in <redacted>.imageDownloaded(<
> redacted>.DownloadImageRequest, index: Swift.Int) -> ()
> (<redacted>:arm64+0x1003e4d57)
> >>>
> >>> This frame has 4 object(s):
> >>>   [32, 36) '' <== Memory access at offset 36 overflows this variable
> >>>   [48, 208) ''
> >>>   [272, 276) ''
> >>>   [288, 448) ''
> >>> HINT: this may be a false positive if your program uses some custom
> stack unwind mechanism or swapcontext
> >>>     (longjmp and C++ exceptions *are* supported)
> >>> SUMMARY: AddressSanitizer: stack-buffer-overflow
> (<redacted>:arm64+0x100e41a83) in <redacted>::addImage(void const*, int,
> int, int, float const*, float const*)
> >>> Shadow bytes around the buggy address:
> >>> 0x0001302dd3f0: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
> >>> 0x0001302dd400: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
> >>> 0x0001302dd410: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
> >>> 0x0001302dd420: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
> >>> 0x0001302dd430: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
> >>> =>0x0001302dd440: f1 f1 f1 f1[04]f2 00 00 00 00 00 00 00 00 00 00
> >>> 0x0001302dd450: 00 00 00 00 00 00 00 00 00 00 f2 f2 f2 f2 f2 f2
> >>> 0x0001302dd460: f2 f2 04 f2 00 00 00 00 00 00 00 00 00 00 00 00
> >>> 0x0001302dd470: 00 00 00 00 00 00 00 00 f3 f3 f3 f3 f3 f3 f3 f3
> >>> 0x0001302dd480: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
> >>> 0x0001302dd490: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
> >>> Shadow byte legend (one shadow byte represents 8 application bytes):
> >>> Addressable:           00
> >>> Partially addressable: 01 02 03 04 05 06 07
> >>> Heap left redzone:       fa
> >>> Freed heap region:       fd
> >>> Stack left redzone:      f1
> >>> Stack mid redzone:       f2
> >>> Stack right redzone:     f3
> >>> Stack after return:      f5
> >>> Stack use after scope:   f8
> >>> Global redzone:          f9
> >>> Global init order:       f6
> >>> Poisoned by user:        f7
> >>> Container overflow:      fc
> >>> Array cookie:            ac
> >>> Intra object redzone:    bb
> >>> ASan internal:           fe
> >>> Left alloca redzone:     ca
> >>> Right alloca redzone:    cb
> >>>
> >>>
> >>>
> >>> So, how do I properly go back and forth between C and Swift code with
> pointers to arrays of floats?
> >>>
> >>> Thanks,
> >>>
> >>> --
> >>> Rick Mann
> >>> rmann at latencyzero.com
> >>>
> >>>
> >>> _______________________________________________
> >>> swift-users mailing list
> >>> swift-users at swift.org
> >>> https://lists.swift.org/mailman/listinfo/swift-users
> >>
> >> _______________________________________________
> >> swift-users mailing list
> >> swift-users at swift.org
> >> https://lists.swift.org/mailman/listinfo/swift-users
> >
>
> _______________________________________________
> swift-users mailing list
> swift-users at swift.org
> https://lists.swift.org/mailman/listinfo/swift-users
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-users/attachments/20170921/cb77fb0d/attachment.html>


More information about the swift-users mailing list