Created
July 31, 2021 14:37
-
-
Save yspreen/95627d72bedcd47f1c0d29271b5600ea to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// URLDigest.swift | |
// | |
// Created by Yannick Spreen on 7/31/21. | |
// | |
import Foundation | |
import CommonCrypto | |
class URLDigest { | |
public static func hex(from fileURL: URL) -> String? { | |
let digest = URLDigest() | |
guard (try? digest.update(url: fileURL)) != nil else { | |
return nil | |
} | |
let result = digest.finalize() | |
return hex(for: result) | |
} | |
enum InputStreamError: Error { | |
case createFailed(URL) | |
case readFailed | |
} | |
private lazy var context: CC_SHA256_CTX = { | |
var shaContext = CC_SHA256_CTX() | |
CC_SHA256_Init(&shaContext) | |
return shaContext | |
}() | |
private var result: Data? | |
init() { | |
} | |
func update(url: URL) throws { | |
guard let inputStream = InputStream(url: url) else { | |
throw InputStreamError.createFailed(url) | |
} | |
return try update(inputStream: inputStream) | |
} | |
func update(inputStream: InputStream) throws { | |
guard result == nil else { | |
return | |
} | |
inputStream.open() | |
defer { | |
inputStream.close() | |
} | |
let bufferSize = 4096 | |
let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: bufferSize) | |
defer { | |
buffer.deallocate() | |
} | |
while true { | |
let bytesRead = inputStream.read(buffer, maxLength: bufferSize) | |
if bytesRead < 0 { | |
// Stream error occured | |
throw (inputStream.streamError ?? InputStreamError.readFailed) | |
} else if bytesRead == 0 { | |
// EOF | |
break | |
} | |
self.update(bytes: buffer, length: bytesRead) | |
} | |
} | |
func update(data: Data) { | |
guard result == nil else { | |
return | |
} | |
withUnsafeBytes(of: data) { | |
self.update(bytes: $0.baseAddress, length: data.count) | |
} | |
} | |
func update(bytes: UnsafeRawPointer?, length: Int) { | |
guard result == nil, let bytes = bytes else { | |
return | |
} | |
_ = CC_SHA256_Update(&self.context, bytes, CC_LONG(length)) | |
} | |
func finalize() -> Data { | |
if let calculatedResult = result { | |
return calculatedResult | |
} | |
var resultBuffer = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH)) | |
CC_SHA256_Final(&resultBuffer, &self.context) | |
let theResult = Data(resultBuffer) | |
result = theResult | |
return theResult | |
} | |
static func hex(for data: Data) -> String { | |
return data.reduce(into: String()) { result, byte in | |
let ch1: Character = hexCharacterLookupTable[Int(byte >> 4)] | |
let ch2: Character = hexCharacterLookupTable[Int(byte & 0x0F)] | |
result.append(ch1) | |
result.append(ch2) | |
} | |
} | |
static let hexCharacterLookupTable: [Character] = { | |
(0..<16).map { Character(String(format: "%x", arguments: [$0])) } | |
}() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment