From b102e364998e3522ab6ea9e5ba41ac6faf59a618 Mon Sep 17 00:00:00 2001 From: Leo Dion Date: Tue, 19 May 2020 17:18:20 -0400 Subject: [PATCH 1/6] adding equality test --- .../Base32CrockfordEncoding.swift | 21 ++++++++++- .../Base32CrockfordEncodingProtocol.swift | 10 ++++++ .../Base32PatternTests.swift | 36 +++++++++++++++++++ .../EncodeDecodeTests.swift | 29 ++++++++++----- .../XCTestManifests.swift | 10 ++++++ 5 files changed, 97 insertions(+), 9 deletions(-) create mode 100644 Tests/Base32CrockfordTests/Base32PatternTests.swift diff --git a/Sources/Base32Crockford/Base32CrockfordEncoding.swift b/Sources/Base32Crockford/Base32CrockfordEncoding.swift index 0bb8783..1812d9b 100644 --- a/Sources/Base32Crockford/Base32CrockfordEncoding.swift +++ b/Sources/Base32Crockford/Base32CrockfordEncoding.swift @@ -4,6 +4,7 @@ public struct Base32CrockfordEncoding: Base32CrockfordEncodingProtocol { public static let encoding: Base32CrockfordEncodingProtocol = Base32CrockfordEncoding() static let characters = "0123456789abcdefghjkmnpqrtuvwxyz".uppercased() + // static let checksum = [1, 1, 2, 4] struct ChecksumError: Error {} @@ -38,8 +39,26 @@ public struct Base32CrockfordEncoding: Base32CrockfordEncodingProtocol { return encodedString } + public func decodeWithoutChecksum(base32Encoded string: String) -> Data { + let standardized = standardize(string: string) + let strBitCount = string.count * 5 + let dataBitCount = Int(floor(Double(strBitCount) / 8)) * 8 + let checksumSize = strBitCount - dataBitCount + + let values = standardized.map { character -> String.IndexDistance in + let lastIndex = Base32CrockfordEncoding.characters.firstIndex(of: character)! + return Base32CrockfordEncoding.characters.distance(from: Base32CrockfordEncoding.characters.startIndex, to: lastIndex) + } + + let bitString = values.map { String($0, radix: 2).pad(toSize: 5) }.joined(separator: "") + + let bitStringWithoutChecksum = String(bitString[bitString.startIndex ... bitString.index(bitString.endIndex, offsetBy: -checksumSize - 1)]) + let dataBytes = bitStringWithoutChecksum.split(by: 8).compactMap { UInt8($0, radix: 2) } + return Data(dataBytes) + } + public func decode(base32Encoded string: String) throws -> Data { - let standardized = string.uppercased() + let standardized = standardize(string: string) let strBitCount = string.count * 5 let dataBitCount = Int(floor(Double(strBitCount) / 8)) * 8 let checksumSize = strBitCount - dataBitCount diff --git a/Sources/Base32Crockford/Base32CrockfordEncodingProtocol.swift b/Sources/Base32Crockford/Base32CrockfordEncodingProtocol.swift index 3cdd83b..b06220a 100644 --- a/Sources/Base32Crockford/Base32CrockfordEncodingProtocol.swift +++ b/Sources/Base32Crockford/Base32CrockfordEncodingProtocol.swift @@ -5,3 +5,13 @@ public protocol Base32CrockfordEncodingProtocol: Base32CrockfordGenerator { func decode(base32Encoded string: String) throws -> Data static var encoding: Base32CrockfordEncodingProtocol { get } } + +public extension Base32CrockfordEncodingProtocol { + func standardize(string: String) -> String { + return string + .uppercased() + .replacingOccurrences(of: "O", with: "0") + .replacingOccurrences(of: "I", with: "1") + .replacingOccurrences(of: "L", with: "1") + } +} diff --git a/Tests/Base32CrockfordTests/Base32PatternTests.swift b/Tests/Base32CrockfordTests/Base32PatternTests.swift new file mode 100644 index 0000000..7de79a8 --- /dev/null +++ b/Tests/Base32CrockfordTests/Base32PatternTests.swift @@ -0,0 +1,36 @@ +@testable import Base32Crockford +import XCTest + +final class Base32EqualityTests: XCTestCase { + func testExample() { + let values = ["0": "O", "1": "I", "I": "L"] + let encoding = Base32CrockfordEncoding() + var checks = 0 + for _ in 0 ... 2000 { + let id = UUID() + let data = Data(Array(uuid: id)) + let fullId = encoding.encode(data: data) + let shortId = String(fullId[fullId.startIndex ... fullId.index(fullId.startIndex, offsetBy: 4)]) + var shortValues = values.reduce([shortId]) { (shortValues, arg1) -> [String] in + + let (key, value) = arg1 + let current = shortValues.last ?? shortId + + return shortValues + [current.replacingOccurrences(of: key, with: value)] + } + shortValues.append((shortValues.last ?? shortId).lowercased()) + shortValues = [String](Set(shortValues)) + for aShortId in shortValues { + debugPrint(aShortId, fullId) + checks += 1 + let result = encoding.decodeWithoutChecksum(base32Encoded: aShortId) + for (lhs, rhs) in zip(data, result) { + XCTAssertEqual(lhs, rhs) + } + } + if checks > 500 { + return + } + } + } +} diff --git a/Tests/Base32CrockfordTests/EncodeDecodeTests.swift b/Tests/Base32CrockfordTests/EncodeDecodeTests.swift index da6db68..7b7008a 100644 --- a/Tests/Base32CrockfordTests/EncodeDecodeTests.swift +++ b/Tests/Base32CrockfordTests/EncodeDecodeTests.swift @@ -31,20 +31,33 @@ final class EncodeDecodeTests: XCTestCase { } } + func decode(value: String, withExpected expected: Data) { + let actual: Data + do { + actual = try Base32CrockfordEncoding.encoding.decode(base32Encoded: value) + } catch { + XCTFail(error.localizedDescription) + return + } + XCTAssertEqual(actual, expected) + } + func testDecoding() { for (expectedString, value) in data { - let actual: Data guard let expected = expectedString.data(using: .utf8) else { XCTFail("Unable to create data from string") continue } - do { - actual = try Base32CrockfordEncoding.encoding.decode(base32Encoded: value) - } catch { - XCTFail(error.localizedDescription) - continue - } - XCTAssertEqual(actual, expected) + var newValue = value + decode(value: value, withExpected: expected) + newValue = newValue.replacingOccurrences(of: "0", with: "O") + decode(value: newValue, withExpected: expected) + newValue = newValue.replacingOccurrences(of: "1", with: "L") + decode(value: newValue, withExpected: expected) + newValue = newValue.replacingOccurrences(of: "L", with: "I") + decode(value: newValue, withExpected: expected) + newValue = newValue.lowercased() + decode(value: newValue, withExpected: expected) } } } diff --git a/Tests/Base32CrockfordTests/XCTestManifests.swift b/Tests/Base32CrockfordTests/XCTestManifests.swift index da7aef1..cd7592a 100644 --- a/Tests/Base32CrockfordTests/XCTestManifests.swift +++ b/Tests/Base32CrockfordTests/XCTestManifests.swift @@ -25,6 +25,15 @@ ] } + extension Base32EqualityTests { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__Base32EqualityTests = [ + ("testExample", testExample) + ] + } + extension EncodeDecodeTests { // DO NOT MODIFY: This is autogenerated, use: // `swift test --generate-linuxmain` @@ -39,6 +48,7 @@ return [ testCase(ArrayTests.__allTests__ArrayTests), testCase(Base32CrockfordTests.__allTests__Base32CrockfordTests), + testCase(Base32EqualityTests.__allTests__Base32EqualityTests), testCase(EncodeDecodeTests.__allTests__EncodeDecodeTests) ] } From f064177209c754166706a705783f3cd75d1f4f0f Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Tue, 19 May 2020 21:22:53 +0000 Subject: [PATCH 2/6] [github action] Update Docs --- docs/extensions/Base32CrockfordEncodingProtocol.md | 6 ++++++ docs/structs/Base32CrockfordEncoding.md | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/docs/extensions/Base32CrockfordEncodingProtocol.md b/docs/extensions/Base32CrockfordEncodingProtocol.md index 87a1f91..c045086 100644 --- a/docs/extensions/Base32CrockfordEncodingProtocol.md +++ b/docs/extensions/Base32CrockfordEncodingProtocol.md @@ -3,6 +3,12 @@ # `Base32CrockfordEncodingProtocol` ## Methods +### `standardize(string:)` + +```swift +func standardize(string: String) -> String +``` + ### `generateIdentifier(from:)` ```swift diff --git a/docs/structs/Base32CrockfordEncoding.md b/docs/structs/Base32CrockfordEncoding.md index e93f6de..2811907 100644 --- a/docs/structs/Base32CrockfordEncoding.md +++ b/docs/structs/Base32CrockfordEncoding.md @@ -13,6 +13,12 @@ public struct Base32CrockfordEncoding: Base32CrockfordEncodingProtocol public func encode(data: Data) -> String ``` +### `decodeWithoutChecksum(base32Encoded:)` + +```swift +public func decodeWithoutChecksum(base32Encoded string: String) -> Data +``` + ### `decode(base32Encoded:)` ```swift From af5f9ec50a2ad9d1478fb630795fbc8c410bf430 Mon Sep 17 00:00:00 2001 From: Leo Dion Date: Tue, 19 May 2020 21:00:40 -0400 Subject: [PATCH 3/6] added comparer --- .../Base32CrockfordComparer.swift | 5 + .../Base32CrockfordEncoding.swift | 109 ++++++++++-------- .../Base32PatternTests.swift | 6 +- 3 files changed, 66 insertions(+), 54 deletions(-) create mode 100644 Sources/Base32Crockford/Base32CrockfordComparer.swift diff --git a/Sources/Base32Crockford/Base32CrockfordComparer.swift b/Sources/Base32Crockford/Base32CrockfordComparer.swift new file mode 100644 index 0000000..46a0b56 --- /dev/null +++ b/Sources/Base32Crockford/Base32CrockfordComparer.swift @@ -0,0 +1,5 @@ +import Foundation + +public protocol Base32CrockfordComparer { + func data(_ data: Data, hasEncodedPrefix prefix: String) -> Bool +} diff --git a/Sources/Base32Crockford/Base32CrockfordEncoding.swift b/Sources/Base32Crockford/Base32CrockfordEncoding.swift index 1812d9b..eba0a10 100644 --- a/Sources/Base32Crockford/Base32CrockfordEncoding.swift +++ b/Sources/Base32Crockford/Base32CrockfordEncoding.swift @@ -1,13 +1,61 @@ import Foundation -public struct Base32CrockfordEncoding: Base32CrockfordEncodingProtocol { - public static let encoding: Base32CrockfordEncodingProtocol = Base32CrockfordEncoding() +public struct Base32CrockfordEncoding: Base32CrockfordEncodingProtocol, Base32CrockfordComparer { + fileprivate static let _encoding = Base32CrockfordEncoding() - static let characters = "0123456789abcdefghjkmnpqrtuvwxyz".uppercased() + public static var encoding: Base32CrockfordEncodingProtocol { + return _encoding + } + + public static var comparer: Base32CrockfordComparer { + return _encoding + } + + fileprivate static let characters = "0123456789abcdefghjkmnpqrtuvwxyz".uppercased() + + fileprivate struct ChecksumError: Error {} + fileprivate func sizeOf(checksumFrom string: String) -> Int { + let strBitCount = string.count * 5 + let dataBitCount = Int(floor(Double(strBitCount) / 8)) * 8 + return strBitCount - dataBitCount + } + + fileprivate func decodeWithoutChecksum(base32Encoded string: String) -> Data { + let standardized = standardize(string: string) + let checksumSize = sizeOf(checksumFrom: standardized) + + return decode(standardizedString: standardized, withChecksumSize: checksumSize) + } + + fileprivate func verifyChecksum(_ checksumSize: Int, _ standardized: String) throws { + let lastValue: UInt8? + if checksumSize != 0 { + let lastIndex = Base32CrockfordEncoding.characters.firstIndex(of: standardized.last!)! + lastValue = UInt8(Base32CrockfordEncoding.characters.distance(from: Base32CrockfordEncoding.characters.startIndex, to: lastIndex)) + } else { + lastValue = nil + } - // static let checksum = [1, 1, 2, 4] + if let lastValue = lastValue { + let checksumValue = (lastValue << (8 - checksumSize)) >> (8 - checksumSize) + guard checksumValue == 0 else { + throw ChecksumError() + } + } + } + + fileprivate func decode(standardizedString standardized: String, withChecksumSize checksumSize: Int) -> Data { + let values = standardized.map { character -> String.IndexDistance in + let lastIndex = Base32CrockfordEncoding.characters.firstIndex(of: character)! + return Base32CrockfordEncoding.characters.distance(from: Base32CrockfordEncoding.characters.startIndex, to: lastIndex) + } - struct ChecksumError: Error {} + let bitString = values.map { String($0, radix: 2).pad(toSize: 5) }.joined(separator: "") + + let bitStringWithoutChecksum = String(bitString[bitString.startIndex ... bitString.index(bitString.endIndex, offsetBy: -checksumSize - 1)]) + let dataBytes = bitStringWithoutChecksum.split(by: 8).compactMap { UInt8($0, radix: 2) } + return Data(dataBytes) + } public func encode(data: Data) -> String { let dataBitCount = data.count * 8 @@ -39,53 +87,16 @@ public struct Base32CrockfordEncoding: Base32CrockfordEncodingProtocol { return encodedString } - public func decodeWithoutChecksum(base32Encoded string: String) -> Data { - let standardized = standardize(string: string) - let strBitCount = string.count * 5 - let dataBitCount = Int(floor(Double(strBitCount) / 8)) * 8 - let checksumSize = strBitCount - dataBitCount - - let values = standardized.map { character -> String.IndexDistance in - let lastIndex = Base32CrockfordEncoding.characters.firstIndex(of: character)! - return Base32CrockfordEncoding.characters.distance(from: Base32CrockfordEncoding.characters.startIndex, to: lastIndex) - } - - let bitString = values.map { String($0, radix: 2).pad(toSize: 5) }.joined(separator: "") - - let bitStringWithoutChecksum = String(bitString[bitString.startIndex ... bitString.index(bitString.endIndex, offsetBy: -checksumSize - 1)]) - let dataBytes = bitStringWithoutChecksum.split(by: 8).compactMap { UInt8($0, radix: 2) } - return Data(dataBytes) - } - public func decode(base32Encoded string: String) throws -> Data { let standardized = standardize(string: string) - let strBitCount = string.count * 5 - let dataBitCount = Int(floor(Double(strBitCount) / 8)) * 8 - let checksumSize = strBitCount - dataBitCount - let lastValue: UInt8? - if checksumSize != 0 { - let lastIndex = Base32CrockfordEncoding.characters.firstIndex(of: standardized.last!)! - lastValue = UInt8(Base32CrockfordEncoding.characters.distance(from: Base32CrockfordEncoding.characters.startIndex, to: lastIndex)) - } else { - lastValue = nil - } - - if let lastValue = lastValue { - let checksumValue = (lastValue << (8 - checksumSize)) >> (8 - checksumSize) - guard checksumValue == 0 else { - throw ChecksumError() - } - } + let checksumSize = sizeOf(checksumFrom: standardized) + try verifyChecksum(checksumSize, standardized) - let values = standardized.map { character -> String.IndexDistance in - let lastIndex = Base32CrockfordEncoding.characters.firstIndex(of: character)! - return Base32CrockfordEncoding.characters.distance(from: Base32CrockfordEncoding.characters.startIndex, to: lastIndex) - } - - let bitString = values.map { String($0, radix: 2).pad(toSize: 5) }.joined(separator: "") + return decode(standardizedString: standardized, withChecksumSize: checksumSize) + } - let bitStringWithoutChecksum = String(bitString[bitString.startIndex ... bitString.index(bitString.endIndex, offsetBy: -checksumSize - 1)]) - let dataBytes = bitStringWithoutChecksum.split(by: 8).compactMap { UInt8($0, radix: 2) } - return Data(dataBytes) + public func data(_ data: Data, hasEncodedPrefix prefix: String) -> Bool { + let prefixData = decodeWithoutChecksum(base32Encoded: prefix) + return zip(data, prefixData).allSatisfy { $0 == $1 } } } diff --git a/Tests/Base32CrockfordTests/Base32PatternTests.swift b/Tests/Base32CrockfordTests/Base32PatternTests.swift index 7de79a8..bd2308b 100644 --- a/Tests/Base32CrockfordTests/Base32PatternTests.swift +++ b/Tests/Base32CrockfordTests/Base32PatternTests.swift @@ -21,12 +21,8 @@ final class Base32EqualityTests: XCTestCase { shortValues.append((shortValues.last ?? shortId).lowercased()) shortValues = [String](Set(shortValues)) for aShortId in shortValues { - debugPrint(aShortId, fullId) + XCTAssert(Base32CrockfordEncoding.comparer.data(data, hasEncodedPrefix: aShortId)) checks += 1 - let result = encoding.decodeWithoutChecksum(base32Encoded: aShortId) - for (lhs, rhs) in zip(data, result) { - XCTAssertEqual(lhs, rhs) - } } if checks > 500 { return From 84f17dd5e2be59b93c4cb70363c7a657ec5fc7d4 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 20 May 2020 01:06:59 +0000 Subject: [PATCH 4/6] [github action] Update Docs --- docs/README.md | 1 + docs/protocols/Base32CrockfordComparer.md | 14 ++++++++++++++ docs/structs/Base32CrockfordEncoding.md | 10 +++++----- 3 files changed, 20 insertions(+), 5 deletions(-) create mode 100644 docs/protocols/Base32CrockfordComparer.md diff --git a/docs/README.md b/docs/README.md index 08c3d60..c14c3c9 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,5 +1,6 @@ ## Protocols +- [Base32CrockfordComparer](protocols/Base32CrockfordComparer.md) - [Base32CrockfordEncodingProtocol](protocols/Base32CrockfordEncodingProtocol.md) - [Base32CrockfordGenerator](protocols/Base32CrockfordGenerator.md) diff --git a/docs/protocols/Base32CrockfordComparer.md b/docs/protocols/Base32CrockfordComparer.md new file mode 100644 index 0000000..217de0a --- /dev/null +++ b/docs/protocols/Base32CrockfordComparer.md @@ -0,0 +1,14 @@ +**PROTOCOL** + +# `Base32CrockfordComparer` + +```swift +public protocol Base32CrockfordComparer +``` + +## Methods +### `data(_:hasEncodedPrefix:)` + +```swift +func data(_ data: Data, hasEncodedPrefix prefix: String) -> Bool +``` diff --git a/docs/structs/Base32CrockfordEncoding.md b/docs/structs/Base32CrockfordEncoding.md index 2811907..5e448ee 100644 --- a/docs/structs/Base32CrockfordEncoding.md +++ b/docs/structs/Base32CrockfordEncoding.md @@ -3,7 +3,7 @@ # `Base32CrockfordEncoding` ```swift -public struct Base32CrockfordEncoding: Base32CrockfordEncodingProtocol +public struct Base32CrockfordEncoding: Base32CrockfordEncodingProtocol, Base32CrockfordComparer ``` ## Methods @@ -13,14 +13,14 @@ public struct Base32CrockfordEncoding: Base32CrockfordEncodingProtocol public func encode(data: Data) -> String ``` -### `decodeWithoutChecksum(base32Encoded:)` +### `decode(base32Encoded:)` ```swift -public func decodeWithoutChecksum(base32Encoded string: String) -> Data +public func decode(base32Encoded string: String) throws -> Data ``` -### `decode(base32Encoded:)` +### `data(_:hasEncodedPrefix:)` ```swift -public func decode(base32Encoded string: String) throws -> Data +public func data(_ data: Data, hasEncodedPrefix prefix: String) -> Bool ``` From 77bf5fd1f202bf489a05f0e6937e89a2551053b3 Mon Sep 17 00:00:00 2001 From: leogdion Date: Wed, 20 May 2020 08:54:07 -0400 Subject: [PATCH 5/6] remove parallel Xcode build --- Scripts/script.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Scripts/script.sh b/Scripts/script.sh index 4148fcd..f4d241c 100755 --- a/Scripts/script.sh +++ b/Scripts/script.sh @@ -27,8 +27,8 @@ if [[ $TRAVIS_OS_NAME = 'osx' ]]; then pod lib lint swift package generate-xcodeproj pod install --silent --project-directory=Example - xcodebuild -quiet -workspace Example/Example.xcworkspace -scheme "iOS Example" ONLY_ACTIVE_ARCH=NO CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO & - xcodebuild -quiet -workspace Example/Example.xcworkspace -scheme "tvOS Example" ONLY_ACTIVE_ARCH=NO CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO & - xcodebuild -quiet -workspace Example/Example.xcworkspace -scheme "macOS Example" ONLY_ACTIVE_ARCH=NO CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO & + xcodebuild -quiet -workspace Example/Example.xcworkspace -scheme "iOS Example" ONLY_ACTIVE_ARCH=NO CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO + xcodebuild -quiet -workspace Example/Example.xcworkspace -scheme "tvOS Example" ONLY_ACTIVE_ARCH=NO CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO + xcodebuild -quiet -workspace Example/Example.xcworkspace -scheme "macOS Example" ONLY_ACTIVE_ARCH=NO CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO wait fi From 1927177b5c27a0218862c0582a555f9a2400fe77 Mon Sep 17 00:00:00 2001 From: leogdion Date: Wed, 20 May 2020 08:54:19 -0400 Subject: [PATCH 6/6] Update script.sh --- Scripts/script.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/Scripts/script.sh b/Scripts/script.sh index f4d241c..6783e0b 100755 --- a/Scripts/script.sh +++ b/Scripts/script.sh @@ -30,5 +30,4 @@ if [[ $TRAVIS_OS_NAME = 'osx' ]]; then xcodebuild -quiet -workspace Example/Example.xcworkspace -scheme "iOS Example" ONLY_ACTIVE_ARCH=NO CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO xcodebuild -quiet -workspace Example/Example.xcworkspace -scheme "tvOS Example" ONLY_ACTIVE_ARCH=NO CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO xcodebuild -quiet -workspace Example/Example.xcworkspace -scheme "macOS Example" ONLY_ACTIVE_ARCH=NO CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO - wait fi