Skip to content

Instantly share code, notes, and snippets.

@rnapier
Last active August 29, 2015 14:26
Show Gist options
  • Save rnapier/969cee3cafcc4d1e4a47 to your computer and use it in GitHub Desktop.
Save rnapier/969cee3cafcc4d1e4a47 to your computer and use it in GitHub Desktop.
Associated types and type erasure for crypto digests http://stackoverflow.com/a/31748616/97337
// Just showing how to hide the underlying functions if you wanted to.
// Also makes some pieces a little less noisy
public struct DigestFunctions<Context> {
private var digester: (UnsafePointer<Void>, CC_LONG, UnsafeMutablePointer<UInt8>) -> UnsafeMutablePointer<UInt8>
private var updater: (UnsafeMutablePointer<Context>, UnsafePointer<Void>, CC_LONG) -> Int32
private var finalizer: (UnsafeMutablePointer<UInt8>, UnsafeMutablePointer<Context>) -> Int32
}
// Digests are reference types because they are stateful. Copying them may lead to confusing results.
public protocol Digest: class {
typealias Context
var context: Context { get set }
var length: Int { get }
var functions: DigestFunctions<Context> { get } // functions itself is public, but data in it is private
}
// Some helpers on all digests to make them act more Swiftly without having to deal with UnsafeMutablePointers.
public extension Digest {
public func digest(data: [UInt8]) -> [UInt8] {
return perform { functions.digester(UnsafePointer<Void>(data), CC_LONG(data.count), $0) }
}
public func update(data: [UInt8]) {
functions.updater(&context, UnsafePointer<Void>(data), CC_LONG(data.count))
}
public func final() -> [UInt8] {
return perform { functions.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
public final class SHA1: Digest {
public var context = CC_SHA1_CTX()
public let length = Int(CC_SHA1_DIGEST_LENGTH)
public let functions = DigestFunctions(digester: CC_SHA1, updater: CC_SHA1_Update, finalizer: CC_SHA1_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
public final class AnyDigest: Digest {
public var context: Void = ()
public let length: Int
public let functions: DigestFunctions<Void>
public init<D: Digest>(_ digest: D) {
length = digest.length
functions = DigestFunctions(
digester: digest.functions.digester,
updater: { digest.functions.updater(&digest.context, $1, $2) },
finalizer: { (hash, _) in digest.functions.finalizer(hash, &digest.context) })
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment