13 Replies
      Latest reply: Sep 26, 2016 4:32 AM by eskimo RSS
      zaph Level 1 Level 1 (0 points)

        Is the following code the best that can be done, the nesting adds a lot of visual complexity.

        Is there a better way to handle the "C" parameters?

         

        Swift3:

        var macData = Data(count: Int(CC_SHA1_DIGEST_LENGTH))

        let _ = macData.withUnsafeMutableBytes {macBytes in

            messageData.withUnsafeBytes {messageBytes in

                keyData.withUnsafeBytes {keyBytes in

                    CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA1),

                           keyBytes, keyData.count,

                           messageBytes, messageData.count,

                           macBytes);

                }

            }

        }

        • Re: withUnsafeBytes
          QuinceyMorris Level 7 Level 7 (4,120 points)

          If you don't mind having an Obj-C file (.m) in your target, it might be clearer to move the above into a global function written in Obj-C, which takes 2 NSData parameters and returns a NSData result. The calling code and the function will then be pretty simple and clear.

          • Re: withUnsafeBytes
            OOPer Level 7 Level 7 (3,565 points)

            Seems the Standard Library designers of Swift think a few nested withUnsafeSomething should be acceptable.

             

            A global function `withUnsafePointers(_:_:_:_:)` has been removed from Swift Standard Library, and the error message of using it says:

            error: 'withUnsafePointers' is unavailable: use nested withUnsafePointer(to:_:) instead

             

            As you may already know, you still can use `NSMutableData` or `NSData`, if you prefer:

                let macData = NSMutableData(length: Int(CC_SHA1_DIGEST_LENGTH))!
                CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA1),
                       keyData.bytes, keyData.length,
                       messageData.bytes, messageData.length,
                       macData.mutableBytes)
            

             

            But, those changes -- pointer retrieving properties to `withUnsafeSomething` method -- are intentionally made, which may help Swift compiler generate better code or something like that.

             

            I expect more Swifty interface for CC functions will be provided in the near future (we should send a feature request?), like GCD has become Dispatch library in Swift 3.

            And until then, we may need to work with nested `withUnsafeSomething`s or to use good old `NSData` and `NSMutableData`.

              • Re: withUnsafeBytes
                QuinceyMorris Level 7 Level 7 (4,120 points)

                >> As you may already know, you still can use `NSMutableData` or `NSData`, if you prefer:

                 

                I'll point out that this approach is actually a bit dangerous, even in equivalent Obj-C code. Those pointers returned by the ".bytes" or ".mutableBytes" method calls are interior pointers, and hence not memory managed. It can be difficult to predict how long they will be valid, especially given that NSData has many possibile internal representations of its data, and some are more weird than others. On top of that, Swift's memory semantics are different from Obj-C's, which means that even things that were valid in Obj-C are not necessarily valid when re-coded into the Swift equivelent of the exact same code.

                 

                That's exactly why Swift has methods like "withUnsafeBytes". The compiler is able to take measures to ensure that the interior pointer is valid for the entire closure.

                 

                Chances are that the Swift NSData code you propose will work fine, but it may produce very occasional, hard-to-debug failures.

                 

                FWIW.

                  • Re: withUnsafeBytes
                    OOPer Level 7 Level 7 (3,565 points)

                    I'll point out that this approach is actually a bit dangerous, even in equivalent Obj-C code.

                     

                    So, you mean your proposal, move the above into a global function written in Obj-C , is as gangerous. Am I taking it right?

                      • Re: withUnsafeBytes
                        QuinceyMorris Level 7 Level 7 (4,120 points)

                        Potentially, but it's a better known issue in Obj-C and clang provides variable annotations that can be used to mitigate the problem.

                         

                        What concerns me more in this case is the mixing of memory models between Swift and Obj-C. It may be fine, but it's more of an unknown quantity.

                          • Re: withUnsafeBytes
                            OOPer Level 7 Level 7 (3,565 points)

                            Sorry, but I don't know the known issue. Can you tell what is it?

                             

                            And about the mixing of memory models between Swift and Obj-C, your proposal -- combining ObjC file will cause the same issue, no?

                              • Re: withUnsafeBytes
                                QuinceyMorris Level 7 Level 7 (4,120 points)

                                I'll give the simplest example, because that's the one I can remember. With code like this:

                                 

                                NSMutableData* data = [NSMutableData dataWithLength: 10 * sizeof (float)];
                                let floats = (float*) data.mutableBytes;
                                for (int i = 0; i < 10; i++)
                                     floats [i] = i; // imagine some useful calculation being done with "floats"
                                return;
                                

                                 

                                you'll likely crash in the "for" loop. That's because the compiler regards line 02 as the end of the lifetime of variable 'data' and releases the object before line 03, causing it to be deallocated, invalidating the "floats" pointer. There are more subtle examples of an interior pointer becoming invalidated — you usually find out about them the hard way, through a rare, unrepeatable bug or crash.

                                 

                                The different memory models have different rules for pointer aliasing. (Basically, in Swift, I believe you can't do it except with explicitly "raw" pointer types. Obj-C follows the C rules, which are more complicated.) If you're passing a couple of object pointers from Swift code to an Obj-C function, you're not going to run into an aliasing issue. If you're passing interior pointers across a Swift/C interface, as your version of the original code did, you might.

                                  • Re: withUnsafeBytes
                                    OOPer Level 7 Level 7 (3,565 points)

                                    Thanks for showing a good example.

                                     

                                    So, you are saying, with this code:

                                    let macData = NSMutableData(length: Int(CC_SHA1_DIGEST_LENGTH))!
                                    CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA1),
                                           keyData.bytes, keyData.length,
                                           messageData.bytes, messageData.length,
                                           macData.mutableBytes)
                                    

                                    Swift might release each `NSData` or `NSMutableData` instances before finishing `CCHMac`.

                          • Re: withUnsafeBytes
                            zaph Level 1 Level 1 (0 points)

                            I too expect native Swift3 interfaces as well as some updates such as AES-GCM soon.

                            My gloal is to use ionbly Swift3 data types such as Data.

                             

                            Thanks for letting me know I am not totally crazy.

                          • Re: withUnsafeBytes
                            eskimo Apple Staff Apple Staff (7,530 points)

                            Personally I’d wrap this code in a Swift-friendly function like this:

                            func sha1HMAC(message: Data, key: Data) -> Data
                            

                            At that point all the call sites for the code will be pleasant and all the ugliness is tightly isolated.  If you found yourself implementing this function a lot, it’d be worthwhile writing some glue to help with that, but for the odd Common Crypto function it’s easier to just wrap it and move on to something more interesting.

                            ps You probably want to get rid of that semicolon (-:

                            Share and Enjoy

                            Quinn “The Eskimo!”
                            Apple Developer Relations, Developer Technical Support, Core OS/Hardware
                            let myEmail = "eskimo" + "1" + "@apple.com"