有没有一种快速的预处理器方法来检测桥接头文件中包含的内容?

时间:2019-04-11 18:53:11

标签: swift string commoncrypto

我找到的唯一答案是在this中,我对此不满意。

我要添加一个标准的MD5转换器作为String扩展名:

/* ###################################################################################################################################### */
/**
 From here: https://stackoverflow.com/q/24123518/879365
 I am not making this public, because it requires the common crypto in the bridging header.
 */
fileprivate extension String {
    /* ################################################################## */
    /**
     - returns: the String, as an MD5 hash.
     */
    var md5: String {
        let str = self.cString(using: String.Encoding.utf8)
        let strLen = CUnsignedInt(self.lengthOfBytes(using: String.Encoding.utf8))
        let digestLen = Int(CC_MD5_DIGEST_LENGTH)
        let result = UnsafeMutablePointer<CUnsignedChar>.allocate(capacity: digestLen)
        CC_MD5(str!, strLen, result)

        let hash = NSMutableString()

        for i in 0..<digestLen {
            hash.appendFormat("%02x", result[i])
        }

        result.deallocate()
        return hash as String
    }
}

这要求我在桥接头中添加以下内容:

#import <CommonCrypto/CommonCrypto.h>

由于我想将其添加到一组可重用的工具中,因此我想看看是否有一种方法可以在编译时检测是否正在使用通用密码库。

我是否可以将其设置为条件编译?

如果不是的话,这没什么大不了的;只是意味着我需要将其设置为单独的源文件。

2 个答案:

答案 0 :(得分:2)

值得注意的是,如果您使用CC_MD5访问dlsym,而没有桥接标头,那么您可以 调用它。

import Foundation

typealias CC_MD5_Type = @convention(c) (UnsafeRawPointer, UInt32, UnsafeMutableRawPointer) -> UnsafeMutableRawPointer

let RTLD_DEFAULT = UnsafeMutableRawPointer(bitPattern: -2)
let CC_MD5 = unsafeBitCast(dlsym(RTLD_DEFAULT, "CC_MD5")!, to: CC_MD5_Type.self)

var md5 = Data(count: 16)
md5.withUnsafeMutableBytes {
    _ = CC_MD5("abc", 3, $0)
}

assert(md5 == Data(bytes: [0x90, 0x01, 0x50, 0x98, 0x3C, 0xD2, 0x4F, 0xB0, 0xD6, 0x96, 0x3F, 0x7D, 0x28, 0xE1, 0x7F, 0x72]))

答案 1 :(得分:0)

这是我遇到的解决方案。这是我原始版本的混搭,也是Rob的出色回答。它具有魅力(我用它来构建对RFC2617 Digest authentication的响应)。由于使用了内置的挂钩,因此我不再需要桥接头,可以将其添加到我的String扩展集中。

正确答案的相当经典的例子来自与我所见完全不同的地方。发生这种情况时,我会喜欢它。

在这里:

public extension String {
    /* ################################################################## */
    /**
     From here: https://stackoverflow.com/q/24123518/879365, but modified from here: https://stackoverflow.com/a/55639723/879365
     - returns: an MD5 hash of the String
     */
    var md5: String {
        var hash = ""

        // Start by getting a C-style string of our string as UTF-8.
        if let str = self.cString(using: .utf8) {
            // This is a cast for the MD5 function. The convention attribute just says that it's a "raw" C function.
            typealias CC_MD5_Type = @convention(c) (UnsafeRawPointer, UInt32, UnsafeMutableRawPointer) -> UnsafeMutableRawPointer

            // This is a flag, telling the name lookup to happen in the global scope. No dlopen required.
            let RTLD_DEFAULT = UnsafeMutableRawPointer(bitPattern: -2)

            // This loads a function pointer with the CommonCrypto MD5 function.
            let CC_MD5 = unsafeBitCast(dlsym(RTLD_DEFAULT, "CC_MD5")!, to: CC_MD5_Type.self)

            // This is the length of the hash
            let CC_MD5_DIGEST_LENGTH = 16

            // This is where our MD5 hash goes. It's a simple 16-byte buffer.
            let result = UnsafeMutablePointer<CUnsignedChar>.allocate(capacity: CC_MD5_DIGEST_LENGTH)

            // Execute the MD5 hash. Save the result in our buffer.
            _ = CC_MD5(str, CUnsignedInt(str.count), result)

            // Turn it into a normal Swift String of hex digits.
            for i in 0..<CC_MD5_DIGEST_LENGTH {
                hash.append(String(format: "%02x", result[i]))
            }

            // Don't need this anymore.
            result.deallocate()
        }

        return hash
    }
}