Last active
January 24, 2020 12:49
-
-
Save rjstelling/0216a4f9bfaec87dec44d30ac65399b1 to your computer and use it in GitHub Desktop.
Simple base encoding and decoding in Swift.
This file contains hidden or 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
import Foundation | |
public enum StringSearchError: Swift.Error { | |
case didNotFind(Character) | |
} | |
extension String { | |
static let ReducedDigits = "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ0123456789" | |
static let DefaultBase36 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" | |
static let DefaultDigits = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" | |
static let DefaultEmoji = "π€β€οΈβ οΈπ€π¦ππ§πππ€’π§ π§Άππππ₯π₯β½οΈππΆπ§Ά" | |
internal func character<T>(at index: T) -> String where T: FixedWidthInteger { | |
return String(self[self.index(self.startIndex, offsetBy: Int(index))]) | |
} | |
fileprivate func position(of char: Character) throws -> Int { | |
guard let idx = self.firstIndex(of: char) else { throw StringSearchError.didNotFind(char) } | |
return Int( self.distance(from: self.startIndex, to: idx) ) | |
} | |
} | |
extension String { | |
public func decode<T: FixedWidthInteger & UnsignedInteger>() throws -> T { return try self.decode(T(String.ReducedDigits.count), using: .ReducedDigits) } | |
private func decode<T>(_ redux: T, using digits: String = .DefaultDigits) throws -> T where T: FixedWidthInteger & UnsignedInteger { | |
let decoded = try self.reduce(T(0)) { | |
let value = T( try digits.position(of: $1) ) | |
return ($0 * redux) + value | |
} | |
return decoded | |
} | |
} | |
public enum BaseError: Swift.Error { | |
case outOfRange | |
} | |
extension FixedWidthInteger where Self.Magnitude : UnsignedInteger { | |
public func encode() throws -> String { return try self.base(String.ReducedDigits.count, using: .ReducedDigits) } | |
private func encode<T>(_ redux: T, using digits: String = .DefaultDigits) throws -> String where T: FixedWidthInteger { | |
guard 1...digits.count ~= Int(redux) else { throw BaseError.outOfRange } | |
var result = "" | |
var value = T(self) | |
let rdx = redux | |
repeat { | |
result = digits.character(at: value % rdx) + result | |
value /= rdx | |
} while (value > 0) | |
return result | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I've updated this to prevent some bad use-cases and remove some bugs with large values.