Ver Fonte

Add Base16 functions and extensions

Norio Nomura há 10 anos atrás
pai
commit
49da3b079a
3 ficheiros alterados com 243 adições e 0 exclusões
  1. 12 0
      Base32.xcodeproj/project.pbxproj
  2. 122 0
      Base32/Base16.swift
  3. 109 0
      Base32Tests/Base16Tests.swift

+ 12 - 0
Base32.xcodeproj/project.pbxproj

@@ -9,8 +9,12 @@
 /* Begin PBXBuildFile section */
 		6C10E90F1A753A6C006EED90 /* Base32.h in Headers */ = {isa = PBXBuildFile; fileRef = 6CA0A79D1A74E80600AC539F /* Base32.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		6C10E91A1A754346006EED90 /* TTTDataTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 6C10E9141A7540B5006EED90 /* TTTDataTransformer.m */; };
+		6C122F7A1A85DC1E004FD458 /* Base16.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C122F791A85DC1E004FD458 /* Base16.swift */; };
+		6C122F7B1A85DC1E004FD458 /* Base16.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C122F791A85DC1E004FD458 /* Base16.swift */; };
 		6C122F7D1A85E52C004FD458 /* StringExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C122F7C1A85E52C004FD458 /* StringExtension.swift */; };
 		6C122F7E1A85E52C004FD458 /* StringExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C122F7C1A85E52C004FD458 /* StringExtension.swift */; };
+		6C122F801A85E885004FD458 /* Base16Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C122F7F1A85E885004FD458 /* Base16Tests.swift */; };
+		6C122F811A85E885004FD458 /* Base16Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C122F7F1A85E885004FD458 /* Base16Tests.swift */; };
 		6C122F821A85EA0A004FD458 /* StringExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C122F7C1A85E52C004FD458 /* StringExtension.swift */; };
 		6C122F831A85EA0B004FD458 /* StringExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C122F7C1A85E52C004FD458 /* StringExtension.swift */; };
 		6C122F851A85EA52004FD458 /* StringExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C122F841A85EA52004FD458 /* StringExtensionTests.swift */; };
@@ -49,7 +53,9 @@
 		6C10E9121A7540B4006EED90 /* SecEncodeTransformTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SecEncodeTransformTests-Bridging-Header.h"; sourceTree = "<group>"; };
 		6C10E9131A7540B5006EED90 /* TTTDataTransformer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TTTDataTransformer.h; sourceTree = "<group>"; };
 		6C10E9141A7540B5006EED90 /* TTTDataTransformer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TTTDataTransformer.m; sourceTree = "<group>"; };
+		6C122F791A85DC1E004FD458 /* Base16.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Base16.swift; sourceTree = "<group>"; };
 		6C122F7C1A85E52C004FD458 /* StringExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StringExtension.swift; sourceTree = "<group>"; };
+		6C122F7F1A85E885004FD458 /* Base16Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Base16Tests.swift; sourceTree = "<group>"; };
 		6C122F841A85EA52004FD458 /* StringExtensionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StringExtensionTests.swift; sourceTree = "<group>"; };
 		6CA0A7981A74E80600AC539F /* Base32.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Base32.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		6CA0A79C1A74E80600AC539F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@@ -161,6 +167,7 @@
 			children = (
 				6CA0A79D1A74E80600AC539F /* Base32.h */,
 				6CA0A7EF1A74ECEE00AC539F /* Base32.swift */,
+				6C122F791A85DC1E004FD458 /* Base16.swift */,
 				6C122F7C1A85E52C004FD458 /* StringExtension.swift */,
 				6CA0A79B1A74E80600AC539F /* Supporting Files */,
 			);
@@ -179,6 +186,7 @@
 			isa = PBXGroup;
 			children = (
 				6CA0A7AA1A74E80600AC539F /* Base32Tests.swift */,
+				6C122F7F1A85E885004FD458 /* Base16Tests.swift */,
 				6C122F841A85EA52004FD458 /* StringExtensionTests.swift */,
 				6CA0A7A81A74E80600AC539F /* Supporting Files */,
 			);
@@ -492,6 +500,7 @@
 			files = (
 				6C122F7D1A85E52C004FD458 /* StringExtension.swift in Sources */,
 				6CA0A7F01A74ECEE00AC539F /* Base32.swift in Sources */,
+				6C122F7A1A85DC1E004FD458 /* Base16.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -501,6 +510,7 @@
 			files = (
 				6C122F851A85EA52004FD458 /* StringExtensionTests.swift in Sources */,
 				6CA0A7AB1A74E80600AC539F /* Base32Tests.swift in Sources */,
+				6C122F801A85E885004FD458 /* Base16Tests.swift in Sources */,
 				6C122F821A85EA0A004FD458 /* StringExtension.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
@@ -511,6 +521,7 @@
 			files = (
 				6C122F7E1A85E52C004FD458 /* StringExtension.swift in Sources */,
 				6CA0A7F11A74ECEE00AC539F /* Base32.swift in Sources */,
+				6C122F7B1A85DC1E004FD458 /* Base16.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -520,6 +531,7 @@
 			files = (
 				6C122F861A85EA52004FD458 /* StringExtensionTests.swift in Sources */,
 				6CA0A7EE1A74ECD700AC539F /* Base32Tests.swift in Sources */,
+				6C122F811A85E885004FD458 /* Base16Tests.swift in Sources */,
 				6C122F831A85EA0B004FD458 /* StringExtension.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;

+ 122 - 0
Base32/Base16.swift

@@ -0,0 +1,122 @@
+//
+//  Base16.swift
+//  Base32
+//
+//  Created by 野村 憲男 on 2/7/15.
+//  Copyright (c) 2015 Norio Nomura. All rights reserved.
+//
+
+import Foundation
+
+// MARK: - Base16 NSData <-> String
+
+public func base16Encode(data: NSData) -> String {
+    return base16encode(data.bytes, data.length)
+}
+
+public func base16DecodeToData(string: String) -> NSData? {
+    if let array = base16decode(string) {
+        return NSData(bytes: array, length: array.count)
+    } else {
+        return nil
+    }
+}
+
+// MARK: - Base16 [UInt8] <-> String
+
+public func base16Encode(array: [UInt8]) -> String {
+    return base16encode(array, array.count)
+}
+
+public func base16Decode(string: String) -> [UInt8]? {
+    return base16decode(string)
+}
+
+// MARK: extensions
+
+extension String {
+    // base16
+    public var base16DecodedData: NSData? {
+        return base16DecodeToData(self)
+    }
+    
+    public var base16EncodedString: String {
+        let length = lengthOfBytesUsingEncoding(NSUTF8StringEncoding)
+        return nulTerminatedUTF8.withUnsafeBufferPointer {
+            (p: UnsafeBufferPointer<UInt8>) -> String in
+            return base16encode(p.baseAddress, length)
+        }
+    }
+    
+    public func base16DecodedString(encoding: NSStringEncoding = NSUTF8StringEncoding) -> String? {
+        if let data = self.base16DecodedData {
+            return NSString(data: data, encoding: NSUTF8StringEncoding) as? String
+        } else {
+            return nil
+        }
+    }
+}
+
+extension NSData {
+    // base16
+    public var base16EncodedString: String {
+        return base16Encode(self)
+    }
+    
+    public var base16EncodedData: NSData {
+        return base16EncodedString.dataUsingUTF8StringEncoding
+    }
+    
+    public var base16DecodedData: NSData? {
+        if let string = NSString(data: self, encoding: NSUTF8StringEncoding) as? String {
+            return base16DecodeToData(string)
+        } else {
+            return nil
+        }
+    }
+}
+
+// MARK: encode
+private func base16encode(data: UnsafePointer<Void>, var length: Int, uppercase: Bool = true) -> String {
+    let array = UnsafeBufferPointer<UInt8>(start: UnsafePointer<UInt8>(data), count: length)
+    return map(array) { String(format: uppercase ? "%02X" : "%02x", $0) }.reduce("", +)
+}
+
+// MARK: decode
+extension UnicodeScalar {
+    private var hexToUInt8: UInt8? {
+        switch self {
+        case "0"..."9": return UInt8(value - UnicodeScalar("0").value)
+        case "a"..."f": return UInt8(value - UnicodeScalar("a").value + 0xa)
+        case "A"..."F": return UInt8(value - UnicodeScalar("A").value + 0xa)
+        default:
+            println("base16decode: Invalid hex character \(self)")
+            return nil
+        }
+    }
+}
+
+private func base16decode(string: String) -> [UInt8]? {
+    // validate length
+    let lenght = countElements(string)
+    if lenght % 2 != 0 {
+        println("base16decode: String must contain even number of characters")
+        return nil
+    }
+    var g = string.unicodeScalars.generate()
+    var buffer = Array<UInt8>(count: lenght / 2, repeatedValue: 0)
+    var index = 0
+    while let msn = g.next() {
+        if let msn = msn.hexToUInt8 {
+            if let lsn = g.next()?.hexToUInt8 {
+                buffer[index] = msn << 4 | lsn
+            } else {
+                return nil
+            }
+        } else {
+            return nil
+        }
+        index++
+    }
+    return buffer
+}

+ 109 - 0
Base32Tests/Base16Tests.swift

@@ -0,0 +1,109 @@
+//
+//  Base16Tests.swift
+//  Base32
+//
+//  Created by 野村 憲男 on 2/7/15.
+//  Copyright (c) 2015 Norio Nomura. All rights reserved.
+//
+
+import Foundation
+import XCTest
+import Base32
+
+class Base16Tests: XCTestCase {
+
+    let vectors: [(String, String)] = [
+        ("", ""),
+        ("f", "66"),
+        ("fo", "666F"),
+        ("foo", "666F6F"),
+        ("foob", "666F6F62"),
+        ("fooba", "666F6F6261"),
+        ("foobar", "666F6F626172"),
+    ]
+    
+
+    override func setUp() {
+        super.setUp()
+        // Put setup code here. This method is called before the invocation of each test method in the class.
+    }
+    
+    override func tearDown() {
+        // Put teardown code here. This method is called after the invocation of each test method in the class.
+        super.tearDown()
+    }
+
+    // MARK: https://tools.ietf.org/html/rfc4648
+
+    func test_RFC4648_base16Encode() {
+        let convertedVectors = self.vectors.map {($0.dataUsingUTF8StringEncoding, $1)}
+        self.measureBlock{
+            for _ in 0...100 {
+                for (test, expect) in convertedVectors {
+                    let result = base16Encode(test)
+                    XCTAssertEqual(result, expect, "base16Encode for \(test)")
+                }
+            }
+        }
+    }
+    
+    func test_RFC4648_base16Decode() {
+        let convertedVectors = self.vectors.map {($0.dataUsingUTF8StringEncoding, $1)}
+        self.measureBlock{
+            for _ in 0...100 {
+                for (expect, test) in convertedVectors {
+                    let result = base16DecodeToData(test)
+                    XCTAssertEqual(result!, expect, "base16Decode for \(test)")
+                }
+            }
+        }
+    }
+    
+    // MARK: -
+    
+    func test_Base16ExtensionString() {
+        self.measureBlock{
+            for _ in 0...100 {
+                for (test, expect) in self.vectors {
+                    let result = test.base16EncodedString
+                    XCTAssertEqual(result, expect, "\(test).base16EncodedString")
+                    let decoded = result.base16DecodedString()
+                    XCTAssertEqual(decoded!, test, "\(result).base16DecodedString()")
+                }
+            }
+        }
+    }
+    
+    func test_Base16ExtensionData() {
+        let dataVectors = vectors.map {
+            (
+                $0.dataUsingUTF8StringEncoding,
+                $1.dataUsingUTF8StringEncoding
+            )
+        }
+        self.measureBlock{
+            for _ in 0...100 {
+                for (test, expect) in dataVectors {
+                    let result = test.base16EncodedData
+                    XCTAssertEqual(result, expect, "\(test).base16EncodedData")
+                    let decoded = result.base16DecodedData
+                    XCTAssertEqual(decoded!, test, "\(result).base16DecodedData")
+                }
+            }
+        }
+    }
+    
+    func test_Base16ExtensionDataAndString() {
+        let dataAndStringVectors = vectors.map {($0.dataUsingUTF8StringEncoding, $1)}
+        self.measureBlock{
+            for _ in 0...100 {
+                for (test, expect) in dataAndStringVectors {
+                    let result = test.base16EncodedString
+                    XCTAssertEqual(result, expect, "\(test).base16EncodedString")
+                    let decoded = result.base16DecodedData
+                    XCTAssertEqual(decoded!, test, "\(result).base16DecodedData")
+                }
+            }
+        }
+    }
+}