├── .gitignore ├── LICENSE ├── README.md ├── SwiftDigest.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ ├── xcbaselines │ └── 91A577DF1F724B3A00A09D42.xcbaseline │ │ ├── 6AB74D39-F22F-445A-99D3-E216EF3911FD.plist │ │ └── Info.plist │ └── xcschemes │ └── SwiftDigest.xcscheme ├── SwiftDigest ├── Info.plist └── MD5Digest.swift └── SwiftDigestTests ├── Info.plist ├── MD5DigestPerformanceTests.swift └── MD5DigestTests.swift /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | ## Playgrounds 32 | timeline.xctimeline 33 | playground.xcworkspace 34 | 35 | # Swift Package Manager 36 | # 37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 38 | # Packages/ 39 | # Package.pins 40 | .build/ 41 | 42 | # CocoaPods 43 | # 44 | # We recommend against adding the Pods directory to your .gitignore. However 45 | # you should judge for yourself, the pros and cons are mentioned at: 46 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 47 | # 48 | # Pods/ 49 | 50 | # Carthage 51 | # 52 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 53 | # Carthage/Checkouts 54 | 55 | Carthage/Build 56 | 57 | # fastlane 58 | # 59 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 60 | # screenshots whenever they are needed. 61 | # For more information about the recommended setup visit: 62 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 63 | 64 | fastlane/report.xml 65 | fastlane/Preview.html 66 | fastlane/screenshots 67 | fastlane/test_output 68 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Nikolai Ruhe 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SwiftDigest 2 | 3 | Copyright (c) 2017 Nikolai Ruhe. 4 | 5 | SwiftDigest is released under the MIT License. 6 | 7 | ## Contents 8 | 9 | This is a pure Swift implementation of the MD5 algorithm. I might add more algorithms in the future. Or not. 10 | 11 | The main purpose is to provide hashing through a pure Swift framework without dependencies other than 12 | Swift Foundation. Currently no effort has been taken to optimze the performance. When hashing more than a 13 | couple of kilo bytes it might be better to use Apple's CommonCrypto implementation. 14 | 15 | ## Examples 16 | 17 | Hash some `Data`: 18 | 19 | let data = Data() 20 | let digest = data.md5 21 | print("md5: \(digest)") 22 | 23 | // prints: "md5: d41d8cd98f00b204e9800998ecf8427e" 24 | 25 | Hash `String` contents: 26 | 27 | let input = "The quick brown fox jumps over the lazy dog" 28 | let digest = input.utf8.md5 29 | print("md5: \(digest)") 30 | 31 | // prints: "md5: 9e107d9d372bb6826bd81d3542a419d6" 32 | 33 | Hash the main executable: 34 | 35 | let appID = try! Data(contentsOf: Bundle.main.executableURL!).md5 36 | // can be used to send a unique id of the app version to a server or so. 37 | 38 | ## Features 39 | 40 | The `MD5Digest` type is ... 41 | 42 | - `Hashable`, so it can be used as a key in dictionaries 43 | - `RawRepresentable` to convert to and from string representations 44 | - `CustomStringConvertible` to make printing easy 45 | - `Codable` to enable JSON and Plist coding of types containing a digest property 46 | 47 | ## Interface 48 | 49 | /// Represents a 16 byte digest value, created from hashing arbitrary data. 50 | public struct MD5Digest : Hashable, RawRepresentable, CustomStringConvertible, Codable { 51 | 52 | /// Perform hashing of the supplied data. 53 | public init(from input: Data) 54 | 55 | /// Create a digest from reading a hex representation from the supplied string. 56 | public init?(rawValue: String) 57 | 58 | /// The 32 digit hex representation. 59 | public var rawValue: String { get } 60 | 61 | /// The 32 digit hex representation. 62 | public var description: String { get } 63 | 64 | /// The raw bytes of the digest value, always exactly 16 bytes. 65 | public var data: Data { get } 66 | 67 | /// The raw bytes of the digest value as a tuple. 68 | public var bytes: (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8) { get } 69 | } 70 | 71 | 72 | public extension Data { 73 | 74 | /// Computes md5 digest value of the contained bytes. 75 | public var md5: MD5Digest { get } 76 | } 77 | 78 | public extension String.UTF8View { 79 | 80 | /// Computes md5 digest value of the string's UTF-8 representation. 81 | public var md5: MD5Digest { get } 82 | } 83 | -------------------------------------------------------------------------------- /SwiftDigest.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 48; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 91A577E11F724B3A00A09D42 /* SwiftDigest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 91A577D71F724B3A00A09D42 /* SwiftDigest.framework */; }; 11 | 91A577F31F724B8B00A09D42 /* MD5Digest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91A577F21F724B8B00A09D42 /* MD5Digest.swift */; }; 12 | 91A577F41F724F6400A09D42 /* MD5DigestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91A577F11F724B8300A09D42 /* MD5DigestTests.swift */; }; 13 | 91CADC561F7FB81D0045546F /* MD5DigestPerformanceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91CADC551F7FB81D0045546F /* MD5DigestPerformanceTests.swift */; }; 14 | /* End PBXBuildFile section */ 15 | 16 | /* Begin PBXContainerItemProxy section */ 17 | 91A577E21F724B3A00A09D42 /* PBXContainerItemProxy */ = { 18 | isa = PBXContainerItemProxy; 19 | containerPortal = 91A577CE1F724B3A00A09D42 /* Project object */; 20 | proxyType = 1; 21 | remoteGlobalIDString = 91A577D61F724B3A00A09D42; 22 | remoteInfo = SwiftDigest; 23 | }; 24 | /* End PBXContainerItemProxy section */ 25 | 26 | /* Begin PBXFileReference section */ 27 | 91A577D71F724B3A00A09D42 /* SwiftDigest.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SwiftDigest.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 28 | 91A577DB1F724B3A00A09D42 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 29 | 91A577E01F724B3A00A09D42 /* SwiftDigestTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwiftDigestTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 30 | 91A577E71F724B3A00A09D42 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 31 | 91A577F11F724B8300A09D42 /* MD5DigestTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MD5DigestTests.swift; sourceTree = ""; }; 32 | 91A577F21F724B8B00A09D42 /* MD5Digest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MD5Digest.swift; sourceTree = ""; }; 33 | 91ADDD4B1F72C21A00C2A8EC /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; 34 | 91ADDD4C1F72C22100C2A8EC /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 35 | 91CADC551F7FB81D0045546F /* MD5DigestPerformanceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MD5DigestPerformanceTests.swift; sourceTree = ""; }; 36 | /* End PBXFileReference section */ 37 | 38 | /* Begin PBXFrameworksBuildPhase section */ 39 | 91A577D31F724B3A00A09D42 /* Frameworks */ = { 40 | isa = PBXFrameworksBuildPhase; 41 | buildActionMask = 2147483647; 42 | files = ( 43 | ); 44 | runOnlyForDeploymentPostprocessing = 0; 45 | }; 46 | 91A577DD1F724B3A00A09D42 /* Frameworks */ = { 47 | isa = PBXFrameworksBuildPhase; 48 | buildActionMask = 2147483647; 49 | files = ( 50 | 91A577E11F724B3A00A09D42 /* SwiftDigest.framework in Frameworks */, 51 | ); 52 | runOnlyForDeploymentPostprocessing = 0; 53 | }; 54 | /* End PBXFrameworksBuildPhase section */ 55 | 56 | /* Begin PBXGroup section */ 57 | 91A577CD1F724B3A00A09D42 = { 58 | isa = PBXGroup; 59 | children = ( 60 | 91ADDD4B1F72C21A00C2A8EC /* LICENSE */, 61 | 91ADDD4C1F72C22100C2A8EC /* README.md */, 62 | 91A577D91F724B3A00A09D42 /* SwiftDigest */, 63 | 91A577E41F724B3A00A09D42 /* SwiftDigestTests */, 64 | 91A577D81F724B3A00A09D42 /* Products */, 65 | ); 66 | sourceTree = ""; 67 | }; 68 | 91A577D81F724B3A00A09D42 /* Products */ = { 69 | isa = PBXGroup; 70 | children = ( 71 | 91A577D71F724B3A00A09D42 /* SwiftDigest.framework */, 72 | 91A577E01F724B3A00A09D42 /* SwiftDigestTests.xctest */, 73 | ); 74 | name = Products; 75 | sourceTree = ""; 76 | }; 77 | 91A577D91F724B3A00A09D42 /* SwiftDigest */ = { 78 | isa = PBXGroup; 79 | children = ( 80 | 91A577DB1F724B3A00A09D42 /* Info.plist */, 81 | 91A577F21F724B8B00A09D42 /* MD5Digest.swift */, 82 | ); 83 | path = SwiftDigest; 84 | sourceTree = ""; 85 | }; 86 | 91A577E41F724B3A00A09D42 /* SwiftDigestTests */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | 91A577E71F724B3A00A09D42 /* Info.plist */, 90 | 91A577F11F724B8300A09D42 /* MD5DigestTests.swift */, 91 | 91CADC551F7FB81D0045546F /* MD5DigestPerformanceTests.swift */, 92 | ); 93 | path = SwiftDigestTests; 94 | sourceTree = ""; 95 | }; 96 | /* End PBXGroup section */ 97 | 98 | /* Begin PBXHeadersBuildPhase section */ 99 | 91A577D41F724B3A00A09D42 /* Headers */ = { 100 | isa = PBXHeadersBuildPhase; 101 | buildActionMask = 2147483647; 102 | files = ( 103 | ); 104 | runOnlyForDeploymentPostprocessing = 0; 105 | }; 106 | /* End PBXHeadersBuildPhase section */ 107 | 108 | /* Begin PBXNativeTarget section */ 109 | 91A577D61F724B3A00A09D42 /* SwiftDigest */ = { 110 | isa = PBXNativeTarget; 111 | buildConfigurationList = 91A577EB1F724B3A00A09D42 /* Build configuration list for PBXNativeTarget "SwiftDigest" */; 112 | buildPhases = ( 113 | 91A577D21F724B3A00A09D42 /* Sources */, 114 | 91A577D31F724B3A00A09D42 /* Frameworks */, 115 | 91A577D41F724B3A00A09D42 /* Headers */, 116 | 91A577D51F724B3A00A09D42 /* Resources */, 117 | ); 118 | buildRules = ( 119 | ); 120 | dependencies = ( 121 | ); 122 | name = SwiftDigest; 123 | productName = SwiftDigest; 124 | productReference = 91A577D71F724B3A00A09D42 /* SwiftDigest.framework */; 125 | productType = "com.apple.product-type.framework"; 126 | }; 127 | 91A577DF1F724B3A00A09D42 /* SwiftDigestTests */ = { 128 | isa = PBXNativeTarget; 129 | buildConfigurationList = 91A577EE1F724B3A00A09D42 /* Build configuration list for PBXNativeTarget "SwiftDigestTests" */; 130 | buildPhases = ( 131 | 91A577DC1F724B3A00A09D42 /* Sources */, 132 | 91A577DD1F724B3A00A09D42 /* Frameworks */, 133 | 91A577DE1F724B3A00A09D42 /* Resources */, 134 | ); 135 | buildRules = ( 136 | ); 137 | dependencies = ( 138 | 91A577E31F724B3A00A09D42 /* PBXTargetDependency */, 139 | ); 140 | name = SwiftDigestTests; 141 | productName = SwiftDigestTests; 142 | productReference = 91A577E01F724B3A00A09D42 /* SwiftDigestTests.xctest */; 143 | productType = "com.apple.product-type.bundle.unit-test"; 144 | }; 145 | /* End PBXNativeTarget section */ 146 | 147 | /* Begin PBXProject section */ 148 | 91A577CE1F724B3A00A09D42 /* Project object */ = { 149 | isa = PBXProject; 150 | attributes = { 151 | LastSwiftUpdateCheck = 0900; 152 | LastUpgradeCheck = 1000; 153 | ORGANIZATIONNAME = "Nikolai Ruhe"; 154 | TargetAttributes = { 155 | 91A577D61F724B3A00A09D42 = { 156 | CreatedOnToolsVersion = 9.0; 157 | LastSwiftMigration = 0900; 158 | ProvisioningStyle = Automatic; 159 | }; 160 | 91A577DF1F724B3A00A09D42 = { 161 | CreatedOnToolsVersion = 9.0; 162 | LastSwiftMigration = 0900; 163 | ProvisioningStyle = Automatic; 164 | }; 165 | }; 166 | }; 167 | buildConfigurationList = 91A577D11F724B3A00A09D42 /* Build configuration list for PBXProject "SwiftDigest" */; 168 | compatibilityVersion = "Xcode 8.0"; 169 | developmentRegion = en; 170 | hasScannedForEncodings = 0; 171 | knownRegions = ( 172 | en, 173 | ); 174 | mainGroup = 91A577CD1F724B3A00A09D42; 175 | productRefGroup = 91A577D81F724B3A00A09D42 /* Products */; 176 | projectDirPath = ""; 177 | projectRoot = ""; 178 | targets = ( 179 | 91A577D61F724B3A00A09D42 /* SwiftDigest */, 180 | 91A577DF1F724B3A00A09D42 /* SwiftDigestTests */, 181 | ); 182 | }; 183 | /* End PBXProject section */ 184 | 185 | /* Begin PBXResourcesBuildPhase section */ 186 | 91A577D51F724B3A00A09D42 /* Resources */ = { 187 | isa = PBXResourcesBuildPhase; 188 | buildActionMask = 2147483647; 189 | files = ( 190 | ); 191 | runOnlyForDeploymentPostprocessing = 0; 192 | }; 193 | 91A577DE1F724B3A00A09D42 /* Resources */ = { 194 | isa = PBXResourcesBuildPhase; 195 | buildActionMask = 2147483647; 196 | files = ( 197 | ); 198 | runOnlyForDeploymentPostprocessing = 0; 199 | }; 200 | /* End PBXResourcesBuildPhase section */ 201 | 202 | /* Begin PBXSourcesBuildPhase section */ 203 | 91A577D21F724B3A00A09D42 /* Sources */ = { 204 | isa = PBXSourcesBuildPhase; 205 | buildActionMask = 2147483647; 206 | files = ( 207 | 91A577F31F724B8B00A09D42 /* MD5Digest.swift in Sources */, 208 | ); 209 | runOnlyForDeploymentPostprocessing = 0; 210 | }; 211 | 91A577DC1F724B3A00A09D42 /* Sources */ = { 212 | isa = PBXSourcesBuildPhase; 213 | buildActionMask = 2147483647; 214 | files = ( 215 | 91A577F41F724F6400A09D42 /* MD5DigestTests.swift in Sources */, 216 | 91CADC561F7FB81D0045546F /* MD5DigestPerformanceTests.swift in Sources */, 217 | ); 218 | runOnlyForDeploymentPostprocessing = 0; 219 | }; 220 | /* End PBXSourcesBuildPhase section */ 221 | 222 | /* Begin PBXTargetDependency section */ 223 | 91A577E31F724B3A00A09D42 /* PBXTargetDependency */ = { 224 | isa = PBXTargetDependency; 225 | target = 91A577D61F724B3A00A09D42 /* SwiftDigest */; 226 | targetProxy = 91A577E21F724B3A00A09D42 /* PBXContainerItemProxy */; 227 | }; 228 | /* End PBXTargetDependency section */ 229 | 230 | /* Begin XCBuildConfiguration section */ 231 | 91A577E91F724B3A00A09D42 /* Debug */ = { 232 | isa = XCBuildConfiguration; 233 | buildSettings = { 234 | ALWAYS_SEARCH_USER_PATHS = NO; 235 | CLANG_ANALYZER_NONNULL = YES; 236 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 237 | CLANG_ENABLE_MODULES = YES; 238 | CLANG_ENABLE_OBJC_ARC = YES; 239 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 240 | CLANG_WARN_BOOL_CONVERSION = YES; 241 | CLANG_WARN_COMMA = YES; 242 | CLANG_WARN_CONSTANT_CONVERSION = YES; 243 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 244 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 245 | CLANG_WARN_EMPTY_BODY = YES; 246 | CLANG_WARN_ENUM_CONVERSION = YES; 247 | CLANG_WARN_INFINITE_RECURSION = YES; 248 | CLANG_WARN_INT_CONVERSION = YES; 249 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 250 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 251 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 252 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 253 | CLANG_WARN_STRICT_PROTOTYPES = YES; 254 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 255 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 256 | CLANG_WARN_UNREACHABLE_CODE = YES; 257 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 258 | CODE_SIGN_IDENTITY = "iPhone Developer"; 259 | COPY_PHASE_STRIP = NO; 260 | CURRENT_PROJECT_VERSION = 1; 261 | DEBUG_INFORMATION_FORMAT = dwarf; 262 | ENABLE_STRICT_OBJC_MSGSEND = YES; 263 | ENABLE_TESTABILITY = YES; 264 | GCC_C_LANGUAGE_STANDARD = gnu11; 265 | GCC_DYNAMIC_NO_PIC = NO; 266 | GCC_NO_COMMON_BLOCKS = YES; 267 | GCC_OPTIMIZATION_LEVEL = 0; 268 | GCC_PREPROCESSOR_DEFINITIONS = ( 269 | "DEBUG=1", 270 | "$(inherited)", 271 | ); 272 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 273 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 274 | GCC_WARN_UNDECLARED_SELECTOR = YES; 275 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 276 | GCC_WARN_UNUSED_FUNCTION = YES; 277 | GCC_WARN_UNUSED_VARIABLE = YES; 278 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 279 | ONLY_ACTIVE_ARCH = YES; 280 | OTHER_SWIFT_FLAGS = "-Xfrontend -warn-long-function-bodies=100 -Xfrontend -warn-long-expression-type-checking=100"; 281 | SDKROOT = iphoneos; 282 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 283 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 284 | VERSIONING_SYSTEM = "apple-generic"; 285 | VERSION_INFO_PREFIX = ""; 286 | }; 287 | name = Debug; 288 | }; 289 | 91A577EA1F724B3A00A09D42 /* Release */ = { 290 | isa = XCBuildConfiguration; 291 | buildSettings = { 292 | ALWAYS_SEARCH_USER_PATHS = NO; 293 | CLANG_ANALYZER_NONNULL = YES; 294 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 295 | CLANG_ENABLE_MODULES = YES; 296 | CLANG_ENABLE_OBJC_ARC = YES; 297 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 298 | CLANG_WARN_BOOL_CONVERSION = YES; 299 | CLANG_WARN_COMMA = YES; 300 | CLANG_WARN_CONSTANT_CONVERSION = YES; 301 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 302 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 303 | CLANG_WARN_EMPTY_BODY = YES; 304 | CLANG_WARN_ENUM_CONVERSION = YES; 305 | CLANG_WARN_INFINITE_RECURSION = YES; 306 | CLANG_WARN_INT_CONVERSION = YES; 307 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 308 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 309 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 310 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 311 | CLANG_WARN_STRICT_PROTOTYPES = YES; 312 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 313 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 314 | CLANG_WARN_UNREACHABLE_CODE = YES; 315 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 316 | CODE_SIGN_IDENTITY = "iPhone Developer"; 317 | COPY_PHASE_STRIP = NO; 318 | CURRENT_PROJECT_VERSION = 1; 319 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 320 | ENABLE_NS_ASSERTIONS = NO; 321 | ENABLE_STRICT_OBJC_MSGSEND = YES; 322 | GCC_C_LANGUAGE_STANDARD = gnu11; 323 | GCC_NO_COMMON_BLOCKS = YES; 324 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 325 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 326 | GCC_WARN_UNDECLARED_SELECTOR = YES; 327 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 328 | GCC_WARN_UNUSED_FUNCTION = YES; 329 | GCC_WARN_UNUSED_VARIABLE = YES; 330 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 331 | OTHER_SWIFT_FLAGS = "-Xfrontend -warn-long-function-bodies=100 -Xfrontend -warn-long-expression-type-checking=100"; 332 | SDKROOT = iphoneos; 333 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 334 | VERSIONING_SYSTEM = "apple-generic"; 335 | VERSION_INFO_PREFIX = ""; 336 | }; 337 | name = Release; 338 | }; 339 | 91A577EC1F724B3A00A09D42 /* Debug */ = { 340 | isa = XCBuildConfiguration; 341 | buildSettings = { 342 | CLANG_ENABLE_MODULES = YES; 343 | CODE_SIGN_IDENTITY = ""; 344 | CODE_SIGN_STYLE = Automatic; 345 | DEFINES_MODULE = YES; 346 | DEVELOPMENT_TEAM = GDDD8834V3; 347 | DYLIB_COMPATIBILITY_VERSION = 1; 348 | DYLIB_CURRENT_VERSION = 1; 349 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 350 | INFOPLIST_FILE = SwiftDigest/Info.plist; 351 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 352 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 353 | PRODUCT_BUNDLE_IDENTIFIER = "de.nikolai-ruhe.SwiftDigest"; 354 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 355 | SKIP_INSTALL = YES; 356 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 357 | SWIFT_VERSION = 4.0; 358 | TARGETED_DEVICE_FAMILY = "1,2"; 359 | }; 360 | name = Debug; 361 | }; 362 | 91A577ED1F724B3A00A09D42 /* Release */ = { 363 | isa = XCBuildConfiguration; 364 | buildSettings = { 365 | CLANG_ENABLE_MODULES = YES; 366 | CODE_SIGN_IDENTITY = ""; 367 | CODE_SIGN_STYLE = Automatic; 368 | DEFINES_MODULE = YES; 369 | DEVELOPMENT_TEAM = GDDD8834V3; 370 | DYLIB_COMPATIBILITY_VERSION = 1; 371 | DYLIB_CURRENT_VERSION = 1; 372 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 373 | INFOPLIST_FILE = SwiftDigest/Info.plist; 374 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 375 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 376 | PRODUCT_BUNDLE_IDENTIFIER = "de.nikolai-ruhe.SwiftDigest"; 377 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 378 | SKIP_INSTALL = YES; 379 | SWIFT_VERSION = 4.0; 380 | TARGETED_DEVICE_FAMILY = "1,2"; 381 | }; 382 | name = Release; 383 | }; 384 | 91A577EF1F724B3A00A09D42 /* Debug */ = { 385 | isa = XCBuildConfiguration; 386 | buildSettings = { 387 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 388 | CLANG_ENABLE_MODULES = YES; 389 | CODE_SIGN_STYLE = Automatic; 390 | DEVELOPMENT_TEAM = GDDD8834V3; 391 | INFOPLIST_FILE = SwiftDigestTests/Info.plist; 392 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 393 | PRODUCT_BUNDLE_IDENTIFIER = "de.nikolai-ruhe.SwiftDigestTests"; 394 | PRODUCT_NAME = "$(TARGET_NAME)"; 395 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 396 | SWIFT_VERSION = 4.0; 397 | TARGETED_DEVICE_FAMILY = "1,2"; 398 | }; 399 | name = Debug; 400 | }; 401 | 91A577F01F724B3A00A09D42 /* Release */ = { 402 | isa = XCBuildConfiguration; 403 | buildSettings = { 404 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 405 | CLANG_ENABLE_MODULES = YES; 406 | CODE_SIGN_STYLE = Automatic; 407 | DEVELOPMENT_TEAM = GDDD8834V3; 408 | INFOPLIST_FILE = SwiftDigestTests/Info.plist; 409 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 410 | PRODUCT_BUNDLE_IDENTIFIER = "de.nikolai-ruhe.SwiftDigestTests"; 411 | PRODUCT_NAME = "$(TARGET_NAME)"; 412 | SWIFT_VERSION = 4.0; 413 | TARGETED_DEVICE_FAMILY = "1,2"; 414 | }; 415 | name = Release; 416 | }; 417 | /* End XCBuildConfiguration section */ 418 | 419 | /* Begin XCConfigurationList section */ 420 | 91A577D11F724B3A00A09D42 /* Build configuration list for PBXProject "SwiftDigest" */ = { 421 | isa = XCConfigurationList; 422 | buildConfigurations = ( 423 | 91A577E91F724B3A00A09D42 /* Debug */, 424 | 91A577EA1F724B3A00A09D42 /* Release */, 425 | ); 426 | defaultConfigurationIsVisible = 0; 427 | defaultConfigurationName = Release; 428 | }; 429 | 91A577EB1F724B3A00A09D42 /* Build configuration list for PBXNativeTarget "SwiftDigest" */ = { 430 | isa = XCConfigurationList; 431 | buildConfigurations = ( 432 | 91A577EC1F724B3A00A09D42 /* Debug */, 433 | 91A577ED1F724B3A00A09D42 /* Release */, 434 | ); 435 | defaultConfigurationIsVisible = 0; 436 | defaultConfigurationName = Release; 437 | }; 438 | 91A577EE1F724B3A00A09D42 /* Build configuration list for PBXNativeTarget "SwiftDigestTests" */ = { 439 | isa = XCConfigurationList; 440 | buildConfigurations = ( 441 | 91A577EF1F724B3A00A09D42 /* Debug */, 442 | 91A577F01F724B3A00A09D42 /* Release */, 443 | ); 444 | defaultConfigurationIsVisible = 0; 445 | defaultConfigurationName = Release; 446 | }; 447 | /* End XCConfigurationList section */ 448 | }; 449 | rootObject = 91A577CE1F724B3A00A09D42 /* Project object */; 450 | } 451 | -------------------------------------------------------------------------------- /SwiftDigest.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SwiftDigest.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /SwiftDigest.xcodeproj/xcshareddata/xcbaselines/91A577DF1F724B3A00A09D42.xcbaseline/6AB74D39-F22F-445A-99D3-E216EF3911FD.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | classNames 6 | 7 | MD5DigestPerformanceTests 8 | 9 | testCommonCryptoShining() 10 | 11 | com.apple.XCTPerformanceMetric_WallClockTime 12 | 13 | baselineAverage 14 | 5.9301 15 | baselineIntegrationDisplayName 16 | 30.09.2017, 13:58:36 17 | 18 | 19 | testCommonCryptoSmallMessage() 20 | 21 | com.apple.XCTPerformanceMetric_WallClockTime 22 | 23 | baselineAverage 24 | 0.24309 25 | baselineIntegrationDisplayName 26 | 30.09.2017, 14:17:16 27 | 28 | 29 | testMD5DigestShining() 30 | 31 | com.apple.XCTPerformanceMetric_WallClockTime 32 | 33 | baselineAverage 34 | 5.3868 35 | baselineIntegrationDisplayName 36 | 30.09.2017, 13:58:36 37 | 38 | 39 | testMD5DigestSmallMessage() 40 | 41 | com.apple.XCTPerformanceMetric_WallClockTime 42 | 43 | baselineAverage 44 | 0.63867 45 | baselineIntegrationDisplayName 46 | 30.09.2017, 14:17:16 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /SwiftDigest.xcodeproj/xcshareddata/xcbaselines/91A577DF1F724B3A00A09D42.xcbaseline/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | runDestinationsByUUID 6 | 7 | 6AB74D39-F22F-445A-99D3-E216EF3911FD 8 | 9 | localComputer 10 | 11 | busSpeedInMHz 12 | 100 13 | cpuCount 14 | 1 15 | cpuKind 16 | 6-Core Intel Xeon E5 17 | cpuSpeedInMHz 18 | 3500 19 | logicalCPUCoresPerPackage 20 | 12 21 | modelCode 22 | MacPro6,1 23 | physicalCPUCoresPerPackage 24 | 6 25 | platformIdentifier 26 | com.apple.platform.macosx 27 | 28 | targetArchitecture 29 | x86_64 30 | targetDevice 31 | 32 | modelCode 33 | iPhone10,5 34 | platformIdentifier 35 | com.apple.platform.iphonesimulator 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /SwiftDigest.xcodeproj/xcshareddata/xcschemes/SwiftDigest.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 65 | 71 | 72 | 73 | 74 | 75 | 76 | 82 | 83 | 89 | 90 | 91 | 92 | 94 | 95 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /SwiftDigest/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /SwiftDigest/MD5Digest.swift: -------------------------------------------------------------------------------- 1 | // SwiftDigest | MD5Digest 2 | // Copyright (c) 2017, 2018 Nikolai Ruhe 3 | // SwiftDigest is released under the MIT License 4 | 5 | import Foundation 6 | 7 | 8 | public extension Sequence where Element == UInt8 { 9 | 10 | /// Computes md5 digest value of contained bytes. 11 | /// 12 | /// This extension on `Sequence` is the main API to create `MD5Digest` values. 13 | /// It is usable on all collection types that use bytes as elements, for instance 14 | /// `Data` or `String.UTF8View`: 15 | /// 16 | /// ## Example: 17 | /// 18 | /// Print the md5 of a string's UTF-8 representation 19 | /// 20 | /// let string = "The quick brown fox jumps over the lazy dog" 21 | /// print("md5: \(string.utf8.md5)") 22 | /// // prints "md5: 9e107d9d372bb6826bd81d3542a419d6" 23 | /// 24 | /// Check if a file's contents match a digest 25 | /// 26 | /// let expectedDigest = MD5Digest(rawValue: "9e107d9d372bb6826bd81d3542a419d6")! 27 | /// let data = try Data(contentsOf: someFileURL) 28 | /// if data.md5 != expectedDigest { 29 | /// throw .digestMismatchError 30 | /// } 31 | var md5: MD5Digest { 32 | return MD5Digest(from: Data(self)) 33 | } 34 | } 35 | 36 | 37 | /// MD5Digest represents a 16 byte digest value, created from hashing arbitrary data. 38 | /// 39 | /// MD5Digest is an immutable value type—just like the two `UInt64` values used for 40 | /// internal storage. 41 | /// 42 | /// It conforms to ... 43 | /// 44 | /// * `Equatable`, to make comparison to other values easy. 45 | /// * `Hashable`, so it can be used as a key in dictionaries or in sets. 46 | /// * `RawRepresentable`, to convert to and from string representations 47 | /// * `CustomStringConvertible`, to make printing easy 48 | /// * `Codable` to enable JSON and Plist coding of types containing a digest property 49 | /// 50 | /// - Copyright: Copyright (c) 2017 Nikolai Ruhe. 51 | 52 | public struct MD5Digest : Hashable, RawRepresentable, LosslessStringConvertible, Codable { 53 | 54 | private let _digest_0: UInt64 55 | private let _digest_1: UInt64 56 | 57 | /// Perform hashing of the supplied data. 58 | public init(from input: Data) { 59 | (_digest_0, _digest_1) = MD5State(input).digest 60 | } 61 | 62 | /// Create a digest from reading a hex representation from the supplied string. 63 | /// 64 | /// The string _must_ consist of exactly 32 hex digits. Otherwise the initializer 65 | /// returns `nil`. 66 | public init?(rawValue: String) { 67 | self.init(rawValue) 68 | } 69 | 70 | public init?(_ description: String) { 71 | guard description.count == 32 else { return nil } 72 | guard let high = UInt64(description.prefix(16), radix: 16) else { return nil } 73 | guard let low = UInt64(description.suffix(16), radix: 16) else { return nil } 74 | (_digest_0, _digest_1) = (high.byteSwapped, low.byteSwapped) 75 | } 76 | 77 | public var rawValue: String { return self.description } 78 | 79 | public var description: String { 80 | return String(format: "%016lx%016lx", 81 | _digest_0.byteSwapped, 82 | _digest_1.byteSwapped) 83 | } 84 | 85 | public var data: Data { 86 | var v = self 87 | return withUnsafeBytes(of: &v) { 88 | return Data(bytes: $0.baseAddress!, count: $0.count) 89 | } 90 | } 91 | 92 | public var bytes: (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8) { 93 | var v = self 94 | return withUnsafeBytes(of: &v) { 95 | (ptr: UnsafeRawBufferPointer) -> (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8) in 96 | return (ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5], ptr[6], ptr[7], 97 | ptr[8], ptr[9], ptr[10], ptr[11], ptr[12], ptr[13], ptr[14], ptr[15]) 98 | } 99 | } 100 | } 101 | 102 | 103 | /// Pure Swift implementation of the MD5 algorithm. 104 | fileprivate struct MD5State { 105 | 106 | var a = UInt32(0x67452301) 107 | var b = UInt32(0xefcdab89) 108 | var c = UInt32(0x98badcfe) 109 | var d = UInt32(0x10325476) 110 | 111 | static let chunkSize = 64 112 | static let endOfMessageMarker: UInt8 = 0x80 113 | 114 | /// Compute the md5 of the bytes in `message`. 115 | init(_ message: Data) { 116 | 117 | // NOTE: A static assert for little endian platform would be great here. 118 | // Not sure how to do this in Swift, though. 119 | assert(1.littleEndian == 1 && 2.bigEndian != 2) 120 | 121 | // Feed all complete 64 bytes chunks of the message. 122 | let remainingByteCount: Int = feedFullChunks(in: message) 123 | 124 | // Copy the remaining bytes into a new chunk sized buffer. 125 | var chunk = Data(count: MD5State.chunkSize) 126 | chunk.replaceSubrange(0 ..< remainingByteCount, 127 | with: message[(message.count - remainingByteCount)...]) 128 | 129 | // Mark the end of message with a single 1 bit. 130 | chunk[remainingByteCount] = MD5State.endOfMessageMarker 131 | 132 | // If the footer does not fit in the last chunk: feed and clear it. 133 | if remainingByteCount >= 56 { 134 | feedFullChunks(in: chunk) 135 | chunk.resetBytes(in: 0 ..< MD5State.chunkSize) 136 | } 137 | 138 | // Write the size of the message to the end of the last chunk. 139 | var len: UInt64 = UInt64(message.count) << 3 140 | withUnsafeBytes(of: &len) { chunk.replaceSubrange(56 ..< 64, with: $0) } 141 | 142 | // Feed the last chunk. 143 | feedFullChunks(in: chunk) 144 | } 145 | 146 | /// Feed all complete 64 byte chunks in the message and return the remaining number of bytes. 147 | @inline(__always) @discardableResult 148 | private mutating func feedFullChunks(in message: Data) -> Int { 149 | let chunkCount = message.count / MD5State.chunkSize 150 | message.withUnsafeBytes { (pointer: UnsafePointer) -> Void in 151 | var cursor = pointer 152 | for _ in 0 ..< chunkCount { 153 | feed(chunkPointer: &cursor) 154 | } 155 | } 156 | 157 | return message.count % MD5State.chunkSize 158 | } 159 | 160 | var digest: (UInt64, UInt64) { 161 | let high = UInt64(a) | UInt64(b) << 32 162 | let low = UInt64(c) | UInt64(d) << 32 163 | return (high, low) 164 | } 165 | 166 | private mutating func feed(chunkPointer ptr: inout UnsafePointer) { 167 | 168 | let old = self 169 | 170 | feed(f0, ptr[00], 0xd76aa478, 07); feed(f0, ptr[01], 0xe8c7b756, 12) 171 | feed(f0, ptr[02], 0x242070db, 17); feed(f0, ptr[03], 0xc1bdceee, 22) 172 | feed(f0, ptr[04], 0xf57c0faf, 07); feed(f0, ptr[05], 0x4787c62a, 12) 173 | feed(f0, ptr[06], 0xa8304613, 17); feed(f0, ptr[07], 0xfd469501, 22) 174 | feed(f0, ptr[08], 0x698098d8, 07); feed(f0, ptr[09], 0x8b44f7af, 12) 175 | feed(f0, ptr[10], 0xffff5bb1, 17); feed(f0, ptr[11], 0x895cd7be, 22) 176 | feed(f0, ptr[12], 0x6b901122, 07); feed(f0, ptr[13], 0xfd987193, 12) 177 | feed(f0, ptr[14], 0xa679438e, 17); feed(f0, ptr[15], 0x49b40821, 22) 178 | 179 | feed(f1, ptr[01], 0xf61e2562, 05); feed(f1, ptr[06], 0xc040b340, 09) 180 | feed(f1, ptr[11], 0x265e5a51, 14); feed(f1, ptr[00], 0xe9b6c7aa, 20) 181 | feed(f1, ptr[05], 0xd62f105d, 05); feed(f1, ptr[10], 0x02441453, 09) 182 | feed(f1, ptr[15], 0xd8a1e681, 14); feed(f1, ptr[04], 0xe7d3fbc8, 20) 183 | feed(f1, ptr[09], 0x21e1cde6, 05); feed(f1, ptr[14], 0xc33707d6, 09) 184 | feed(f1, ptr[03], 0xf4d50d87, 14); feed(f1, ptr[08], 0x455a14ed, 20) 185 | feed(f1, ptr[13], 0xa9e3e905, 05); feed(f1, ptr[02], 0xfcefa3f8, 09) 186 | feed(f1, ptr[07], 0x676f02d9, 14); feed(f1, ptr[12], 0x8d2a4c8a, 20) 187 | 188 | feed(f2, ptr[05], 0xfffa3942, 04); feed(f2, ptr[08], 0x8771f681, 11) 189 | feed(f2, ptr[11], 0x6d9d6122, 16); feed(f2, ptr[14], 0xfde5380c, 23) 190 | feed(f2, ptr[01], 0xa4beea44, 04); feed(f2, ptr[04], 0x4bdecfa9, 11) 191 | feed(f2, ptr[07], 0xf6bb4b60, 16); feed(f2, ptr[10], 0xbebfbc70, 23) 192 | feed(f2, ptr[13], 0x289b7ec6, 04); feed(f2, ptr[00], 0xeaa127fa, 11) 193 | feed(f2, ptr[03], 0xd4ef3085, 16); feed(f2, ptr[06], 0x04881d05, 23) 194 | feed(f2, ptr[09], 0xd9d4d039, 04); feed(f2, ptr[12], 0xe6db99e5, 11) 195 | feed(f2, ptr[15], 0x1fa27cf8, 16); feed(f2, ptr[02], 0xc4ac5665, 23) 196 | 197 | feed(f3, ptr[00], 0xf4292244, 06); feed(f3, ptr[07], 0x432aff97, 10) 198 | feed(f3, ptr[14], 0xab9423a7, 15); feed(f3, ptr[05], 0xfc93a039, 21) 199 | feed(f3, ptr[12], 0x655b59c3, 06); feed(f3, ptr[03], 0x8f0ccc92, 10) 200 | feed(f3, ptr[10], 0xffeff47d, 15); feed(f3, ptr[01], 0x85845dd1, 21) 201 | feed(f3, ptr[08], 0x6fa87e4f, 06); feed(f3, ptr[15], 0xfe2ce6e0, 10) 202 | feed(f3, ptr[06], 0xa3014314, 15); feed(f3, ptr[13], 0x4e0811a1, 21) 203 | feed(f3, ptr[04], 0xf7537e82, 06); feed(f3, ptr[11], 0xbd3af235, 10) 204 | feed(f3, ptr[02], 0x2ad7d2bb, 15); feed(f3, ptr[09], 0xeb86d391, 21) 205 | 206 | (a, b, c, d) = (a &+ old.a, b &+ old.b, c &+ old.c, d &+ old.d) 207 | 208 | ptr = ptr.advanced(by: 16) 209 | } 210 | 211 | private var f0: UInt32 { return (b & c) | (~b & d) } 212 | private var f1: UInt32 { return (d & b) | (~d & c) } 213 | private var f2: UInt32 { return b ^ c ^ d } 214 | private var f3: UInt32 { return c ^ (b | ~d) } 215 | 216 | @inline(__always) 217 | private mutating func feed(_ f: UInt32, _ input: UInt32, _ magic: UInt32, _ shift: Int) { 218 | let s = a &+ input &+ magic &+ f 219 | let r = (s << shift) | (s >> (32 - shift)) 220 | (a, b, c, d) = (d, b &+ r, b, c) 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /SwiftDigestTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /SwiftDigestTests/MD5DigestPerformanceTests.swift: -------------------------------------------------------------------------------- 1 | import SwiftDigest 2 | import XCTest 3 | import CommonCrypto 4 | 5 | 6 | class MD5DigestPerformanceTests: XCTestCase { 7 | 8 | static var hugeTestData: Data { 9 | var data = "All work and no play makes Jack a dull boy\n".data(using: .utf8)! 10 | data.reserveCapacity(2_885_681_152) // ~ 2.6 GB 11 | for _ in 1...26 { 12 | data.append(data) 13 | } 14 | return data 15 | } 16 | 17 | func testMD5DigestSmallMessage() { 18 | let input = "The quick brown fox jumps over the lazy dog".data(using: .utf8)! 19 | 20 | self.measure { 21 | var digest: MD5Digest? = nil 22 | for _ in 1 ... 1000000 { 23 | digest = input.md5 24 | } 25 | XCTAssertEqual( 26 | digest, 27 | MD5Digest(rawValue: "9e107d9d372bb6826bd81d3542a419d6") 28 | ) 29 | } 30 | 31 | } 32 | 33 | func testCommonCryptoSmallMessage() { 34 | let input = "The quick brown fox jumps over the lazy dog".data(using: .utf8)! 35 | 36 | self.measure { 37 | var digestData = Data(count: Int(CC_MD5_DIGEST_LENGTH)) 38 | 39 | for _ in 1 ... 1000000 { 40 | digestData.resetBytes(in: 0.. Void in 42 | input.withUnsafeBytes { (messageBytes) -> Void in 43 | CC_MD5(messageBytes, CC_LONG(input.count), digestBytes) 44 | } 45 | } 46 | } 47 | 48 | let md5Hex = digestData.map { String(format: "%02hhx", $0) }.joined() 49 | 50 | XCTAssertEqual( 51 | md5Hex, 52 | "9e107d9d372bb6826bd81d3542a419d6" 53 | ) 54 | } 55 | } 56 | 57 | func testMD5DigestShining() { 58 | let input = MD5DigestPerformanceTests.hugeTestData 59 | 60 | self.measure { 61 | let digest = input.md5 62 | XCTAssertEqual( 63 | digest, 64 | MD5Digest(rawValue: "91ad3b24f924e7999f10c1accd3cd510") 65 | ) 66 | } 67 | 68 | } 69 | 70 | func testCommonCryptoShining() { 71 | let input = MD5DigestPerformanceTests.hugeTestData 72 | 73 | self.measure { 74 | var digestData = Data(count: Int(CC_MD5_DIGEST_LENGTH)) 75 | 76 | digestData.withUnsafeMutableBytes { (digestBytes) -> Void in 77 | input.withUnsafeBytes { (messageBytes) -> Void in 78 | CC_MD5(messageBytes, CC_LONG(input.count), digestBytes) 79 | } 80 | } 81 | 82 | let md5Hex = digestData.map { String(format: "%02hhx", $0) }.joined() 83 | 84 | XCTAssertEqual( 85 | md5Hex, 86 | "91ad3b24f924e7999f10c1accd3cd510" 87 | ) 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /SwiftDigestTests/MD5DigestTests.swift: -------------------------------------------------------------------------------- 1 | import SwiftDigest 2 | import XCTest 3 | 4 | 5 | class MD5Tests: XCTestCase { 6 | 7 | func testEmpty() { 8 | XCTAssertEqual( 9 | Data().md5, 10 | MD5Digest(rawValue: "d41d8cd98f00b204e9800998ecf8427e") 11 | ) 12 | } 13 | 14 | func testData() { 15 | XCTAssertEqual( 16 | MD5Digest(rawValue: "d41d8cd98f00b204e9800998ecf8427e")?.data, 17 | Data([212, 29, 140, 217, 143, 0, 178, 4, 233, 128, 9, 152, 236, 248, 66, 126,]) 18 | ) 19 | } 20 | 21 | func testBytes() { 22 | XCTAssertEqual( 23 | "\(MD5Digest(rawValue: "d41d8cd98f00b204e9800998ecf8427e")!.bytes)", 24 | "(212, 29, 140, 217, 143, 0, 178, 4, 233, 128, 9, 152, 236, 248, 66, 126)" 25 | ) 26 | } 27 | 28 | func testFox1() { 29 | let input = "The quick brown fox jumps over the lazy dog" 30 | XCTAssertEqual( 31 | input.utf8.md5, 32 | MD5Digest(rawValue: "9e107d9d372bb6826bd81d3542a419d6") 33 | ) 34 | } 35 | 36 | func testFox2() { 37 | let input = "The quick brown fox jumps over the lazy dog." 38 | XCTAssertEqual( 39 | input.utf8.md5, 40 | MD5Digest(rawValue: "e4d909c290d0fb1ca068ffaddf22cbd0") 41 | ) 42 | } 43 | 44 | func testTwoFooterChunks() { 45 | let input = Data(count: 57) 46 | XCTAssertEqual( 47 | input.md5, 48 | MD5Digest(rawValue: "ab9d8ef2ffa9145d6c325cefa41d5d4e") 49 | ) 50 | } 51 | 52 | func test4KBytes() { 53 | var input = String(repeating: "The quick brown fox jumps over the lazy dog.", count: 100) 54 | XCTAssertEqual( 55 | input.utf8.md5, 56 | MD5Digest(rawValue: "7052292b1c02ae4b0b35fabca4fbd487") 57 | ) 58 | } 59 | 60 | func test4MBytes() { 61 | var input = String(repeating: "The quick brown fox jumps over the lazy dog.", count: 100000) 62 | XCTAssertEqual( 63 | input.utf8.md5, 64 | MD5Digest(rawValue: "f8a4ffa8b1c902f072338caa1e4482ce") 65 | ) 66 | } 67 | 68 | func testRecursive() { 69 | XCTAssertEqual( 70 | "".utf8.md5.description.utf8.md5.description.utf8.md5.description.utf8.md5, 71 | MD5Digest(rawValue: "5a8dccb220de5c6775c873ead6ff2e43") 72 | ) 73 | } 74 | 75 | func testEncoding() { 76 | let sut = MD5Digest(rawValue: "d41d8cd98f00b204e9800998ecf8427e")! 77 | let json = String(bytes: try! JSONEncoder().encode([sut]), encoding: .utf8)! 78 | XCTAssertEqual(json, "[\"\(sut)\"]") 79 | } 80 | 81 | func testDecoding() { 82 | let sut = MD5Digest(rawValue: "d41d8cd98f00b204e9800998ecf8427e")! 83 | let json = Data("[\"\(sut)\"]".utf8) 84 | let digest = try! JSONDecoder().decode(Array.self, from: json).first! 85 | XCTAssertEqual(digest, sut) 86 | } 87 | } 88 | --------------------------------------------------------------------------------