将 Objective-C (#define) 宏转换为 Swift
Convert Objective-C (#define) macro to Swift
简而言之,我正在尝试将 #define
宏转换为某种原生 Swift 数据结构。只是不确定如何或什么样。
详情
我想尝试将以下 #define
从 Objective-C 复制到 Swift。 Source: JoeKun/FileMD5Hash
#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
显然#define
不存在于Swift;因此我不是在寻找 1:1 端口。更一般地说只是它的精神。
首先,我制作了一个名为 CryptoAlgorithm
的 enum
。为了这个问题,我只关心支持两种加密算法;但应该没有什么能阻止我进一步扩展它。
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
是 UnsafeMutablePointer<CC_SHA1_CTX> -> Int32
。因此,这两个return类型是不一样的。
enum
是错误的方法吗?我应该使用泛型吗?如果是这样,应该如何制作仿制药?我是否应该提供一个同时 CC_MD5_CTX
和 CC_SHA1_CTX
然后由 return 扩展的协议?
欢迎所有建议(使用 Objc 桥除外)。
我不知道我是否喜欢原始 ObjC 代码的发展方向,因为它非常不安全。在 Swift 中,您只需要使所有类型的不安全性更加明确:
var initFunction: UnsafeMutablePointer<Void> -> Int32 {
switch self {
case .MD5:
return { CC_MD5_Init(UnsafeMutablePointer<CC_MD5_CTX>([=10=])) }
case .SHA1:
return { CC_SHA1_Init(UnsafeMutablePointer<CC_SHA1_CTX>([=10=])) }
}
}
更多"Swift"方法是使用协议,例如:
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), [=14=]) }
}
func update(data: [UInt8]) {
updater(&context, UnsafePointer<Void>(data), CC_LONG(data.count))
}
func final() -> [UInt8] {
return perform { finalizer([=14=], &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, , ) }
finalizer = { (hash, _) in digest.finalizer(hash, &digest.context) }
}
}
简而言之,我正在尝试将 #define
宏转换为某种原生 Swift 数据结构。只是不确定如何或什么样。
详情
我想尝试将以下 #define
从 Objective-C 复制到 Swift。 Source: JoeKun/FileMD5Hash
#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
显然#define
不存在于Swift;因此我不是在寻找 1:1 端口。更一般地说只是它的精神。
首先,我制作了一个名为 CryptoAlgorithm
的 enum
。为了这个问题,我只关心支持两种加密算法;但应该没有什么能阻止我进一步扩展它。
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
是 UnsafeMutablePointer<CC_SHA1_CTX> -> Int32
。因此,这两个return类型是不一样的。
enum
是错误的方法吗?我应该使用泛型吗?如果是这样,应该如何制作仿制药?我是否应该提供一个同时 CC_MD5_CTX
和 CC_SHA1_CTX
然后由 return 扩展的协议?
欢迎所有建议(使用 Objc 桥除外)。
我不知道我是否喜欢原始 ObjC 代码的发展方向,因为它非常不安全。在 Swift 中,您只需要使所有类型的不安全性更加明确:
var initFunction: UnsafeMutablePointer<Void> -> Int32 {
switch self {
case .MD5:
return { CC_MD5_Init(UnsafeMutablePointer<CC_MD5_CTX>([=10=])) }
case .SHA1:
return { CC_SHA1_Init(UnsafeMutablePointer<CC_SHA1_CTX>([=10=])) }
}
}
更多"Swift"方法是使用协议,例如:
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), [=14=]) }
}
func update(data: [UInt8]) {
updater(&context, UnsafePointer<Void>(data), CC_LONG(data.count))
}
func final() -> [UInt8] {
return perform { finalizer([=14=], &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, , ) }
finalizer = { (hash, _) in digest.finalizer(hash, &digest.context) }
}
}