将 Objective-C (#define) 宏转换为 Swift

2024-03-04

简而言之,我正在尝试转换#define宏转换为某种原生 Swift 数据结构。只是不确定如何或何种类型。

Details

我想尝试并复制以下内容#define从 Objective-C 到 Swift。资料来源:JoeKun/FileMD5Hash https://github.com/JoeKun/FileMD5Hash/blob/master/Library/FileHash.m#L48-54

#define FileHashComputationContextInitialize(context, hashAlgorithmName)                    \
    CC_##hashAlgorithmName##_CTX hashObjectFor##hashAlgorithmName;                          \
    context.initFunction      = (FileHashInitFunction)&CC_##hashAlgorithmName##_Init;       \
    context.updateFunction    = (FileHashUpdateFunction)&CC_##hashAlgorithmName##_Update;   \
    context.finalFunction     = (FileHashFinalFunction)&CC_##hashAlgorithmName##_Final;     \
    context.digestLength      = CC_##hashAlgorithmName##_DIGEST_LENGTH;                     \
    context.hashObjectPointer = (uint8_t **)&hashObjectFor##hashAlgorithmName

明显地#defineSwift 中不存在;因此我不是在寻找 1:1 端口。更一般地说,只是它的精神。

首先,我做了一个enum called CryptoAlgorithm。为了这个问题,我只关心支持两种加密算法;但应该没有什么可以阻止我进一步扩展它。

enum CryptoAlgorithm {
  case MD5, SHA1
}

到目前为止,一切都很好。现在要实施的是digestLength.

enum CryptoAlgorithm {
  case MD5, SHA1

  var digestLength: Int {
    switch self {
    case .MD5:
      return Int(CC_MD5_DIGEST_LENGTH)
    case .SHA1:
      return Int(CC_SHA1_DIGEST_LENGTH)
  }
}

再说一次,到目前为止一切都很好。现在要实施的是initFunction.

enum CryptoAlgorithm {
  case MD5, SHA1

  var digestLength: Int {
    switch self {
    case .MD5:
      return Int(CC_MD5_DIGEST_LENGTH)
    case .SHA1:
      return Int(CC_SHA1_DIGEST_LENGTH)
  }

  var initFunction: UnsafeMutablePointer<CC_MD5_CTX> -> Int32 {
    switch self {
    case .MD5:
      return CC_MD5_Init
    case .SHA1:
      return CC_SHA1_Init
    }
  }
}

崩溃和燃烧。'CC_MD5_CTX' is not identical to 'CC_SHA1_CTX'。问题是CC_SHA1_Init is a UnsafeMutablePointer<CC_SHA1_CTX> -> Int32。因此,两者的返回类型并不相同。

Is an enum错误的做法?我应该使用泛型吗?如果是的话,仿制药应该如何制作?我是否应该提供一个协议CC_MD5_CTX and CC_SHA1_CTX然后扩展并返回那个?

欢迎所有建议(除了使用 Objc 桥)。


我不知道我是否喜欢原始 ObjC 代码中的情况,因为它非常类型不安全。在 Swift 中,你只需要让所有类型的不安全性更加明确:

    var initFunction: UnsafeMutablePointer<Void> -> Int32 {
        switch self {
        case .MD5:
            return { CC_MD5_Init(UnsafeMutablePointer<CC_MD5_CTX>($0)) }
        case .SHA1:
            return { CC_SHA1_Init(UnsafeMutablePointer<CC_SHA1_CTX>($0)) }
        }
    }

解决这个问题的更“迅速”的方法是使用协议,例如:

protocol CryptoAlgorithm {
    typealias Context
    init(_ ctx: UnsafeMutablePointer<Context>)
    var digestLength: Int { get }
}

然后你会得到类似的东西(未经测试):

struct SHA1: CryptoAlgorithm {
    typealias Context = CC_SHA1_CONTEXT
    private let context: UnsafeMutablePointer<Context>
    init(_ ctx: UnsafeMutablePointer<Context>) {
        CC_SHA1_Init(ctx) // This can't actually fail
        self.context = ctx // This is pretty dangerous.... but matches above. (See below)
    }
    let digestLength = Int(CC_SHA1_DIGEST_LENGTH)
}

但我很想隐藏上下文,然后这样做:

protocol CryptoAlgorithm {
    init()
    var digestLength: Int { get }
}

struct SHA1: CryptoAlgorithm {
    private var context = CC_SHA1_CTX()
    init() {
        CC_SHA1_Init(&context) // This is very likely redundant.
    }
    let digestLength = Int(CC_SHA1_DIGEST_LENGTH)
}

为什么你需要揭露它是 CommonCrypto 的事实?为什么您要依靠调用者来为您保留上下文?如果超出范围,则后面的调用将会崩溃。我会保留里面的上下文。


更接近您原来的问题,请考虑这个(编译,但未经测试):

// Digests are reference types because they are stateful. Copying them may lead to confusing results.
protocol Digest: class {
    typealias Context
    var context: Context { get set }
    var length: Int { get }
    var digester: (UnsafePointer<Void>, CC_LONG, UnsafeMutablePointer<UInt8>) -> UnsafeMutablePointer<UInt8> { get }
    var updater: (UnsafeMutablePointer<Context>, UnsafePointer<Void>, CC_LONG) -> Int32 { get }
    var finalizer: (UnsafeMutablePointer<UInt8>, UnsafeMutablePointer<Context>) -> Int32 { get }
}

// Some helpers on all digests to make them act more Swiftly without having to deal with UnsafeMutablePointers.
extension Digest {
    func digest(data: [UInt8]) -> [UInt8] {
        return perform { digester(UnsafePointer<Void>(data), CC_LONG(data.count), $0) }
    }
    func update(data: [UInt8]) {
        updater(&context, UnsafePointer<Void>(data), CC_LONG(data.count))
    }
    func final() -> [UInt8] {
        return perform { finalizer($0, &context) }
    }
    // Helper that wraps up "create a buffer, update buffer, return buffer"
    private func perform(f: (UnsafeMutablePointer<UInt8>) -> ()) -> [UInt8] {
        var hash = [UInt8](count: length, repeatedValue: 0)
        f(&hash)
        return hash
    }
}

// Example of creating a new digest
final class SHA1: Digest {
    var context = CC_SHA1_CTX()
    let length = Int(CC_SHA1_DIGEST_LENGTH)
    let digester = CC_SHA1
    let updater = CC_SHA1_Update
    let finalizer = CC_SHA1_Final
}

// And here's what you change to make another one
final class SHA256: Digest {
    var context = CC_SHA256_CTX()
    let length = Int(CC_SHA256_DIGEST_LENGTH)
    let digester = CC_SHA256
    let updater = CC_SHA256_Update
    let finalizer = CC_SHA256_Final
}

// Type-eraser, so we can talk about arbitrary digests without worrying about the underlying associated type.
// See http://robnapier.net/erasure
// So now we can say things like `let digests = [AnyDigest(SHA1()), AnyDigest(SHA256())]`
// If this were the normal use-case, you could rename "Digest" as "DigestAlgorithm" and rename "AnyDigest" as "Digest"
// for convenience
final class AnyDigest: Digest {
    var context: Void = ()
    let length: Int
    let digester: (UnsafePointer<Void>, CC_LONG, UnsafeMutablePointer<UInt8>) -> UnsafeMutablePointer<UInt8>
    let updater: (UnsafeMutablePointer<Void>, UnsafePointer<Void>, CC_LONG) -> Int32
    let finalizer: (UnsafeMutablePointer<UInt8>, UnsafeMutablePointer<Void>) -> Int32

    init<D: Digest>(_ digest: D) {
        length = digest.length
        digester = digest.digester
        updater = { digest.updater(&digest.context, $1, $2) }
        finalizer = { (hash, _) in digest.finalizer(hash, &digest.context) }
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

将 Objective-C (#define) 宏转换为 Swift 的相关文章

随机推荐