| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495 |
- /*
- * Copyright 2019 Google
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- import CommonCrypto
- import Foundation
- /// Hashing related utility functions. The enum type is used as a namespace here instead of having
- /// root functions, and no cases should be added to it. Note: this would be named `Hasher` but it
- /// collide's with Foundation's `Hasher` type.
- enum HashCalculator {}
- extension HashCalculator {
- enum HashError: Error {
- /// Real errors aren't thrown, so just give text what happened.
- case failed(String)
- }
- /// Hashes the contents of the directory recursively.
- static func sha256Contents(ofDir dir: URL) throws -> String {
- var hashes: [String] = []
- let allContents = try FileManager.default.recursivelySearch(for: .allFiles, in: dir)
- // Sort the contents to make it deterministic.
- let sortedContents = allContents.sorted { $0.absoluteString < $1.absoluteString }
- for file in sortedContents {
- // Hash the contents of the file.
- let contentsHash = try sha256(file)
- hashes.append(contentsHash)
- // Hash the file name as well.
- let nameHash = try sha256(file.path)
- hashes.append(nameHash)
- }
- if hashes.isEmpty {
- throw HashError.failed("Directory \(dir) does not contain any files.")
- }
- // Calculate the final hash by hashing all the hashes joined together.
- let hash = try sha256(hashes.joined())
- return hash
- }
- /// Calculates the SHA256 hash of the data given.
- static func sha256(_ data: Data) -> String {
- var digest = [UInt8].init(repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
- #if swift(>=5)
- _ = data.withUnsafeBytes {
- CC_SHA256($0.baseAddress, UInt32(data.count), &digest)
- }
- #else
- _ = data.withUnsafeBytes {
- CC_SHA256($0, UInt32(data.count), &digest)
- }
- #endif
- let characters = digest.map { String(format: "%02x", $0) }
- return characters.joined()
- }
- /// Calculates the SHA256 hash of the contents of the file at the given URL.
- static func sha256(_ file: URL) throws -> String {
- guard file.isFileURL else {
- throw HashError.failed("URL given is not a file URL. \(file)")
- }
- guard let data = FileManager.default.contents(atPath: file.path) else {
- throw HashError.failed("Could not get data from \(file.path).")
- }
- return sha256(data)
- }
- /// Calculates the SHA256 hash of the text given.
- static func sha256(_ text: String) throws -> String {
- // If we can't get UTF8 bytes out, return nil.
- guard let data = text.data(using: .utf8) else {
- throw HashError.failed("String used was not UTF8 compliant. \"\(text)\"")
- }
- return sha256(data)
- }
- }
|