Simple base encoding and decoding in Swift.
import Foundation | |
enum StringSearchError: Swift.Error { | |
case didNotFind(Character) | |
} | |
extension String { | |
fileprivate static let DefaultDigits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" | |
fileprivate static let DefaultEmoji = "🤖❤️☠️🤓🦁🍆🧀😎🙄🤢🧠🧶🙈🙉🙊🔥🥕⚽️💊🎶🧶" | |
func character<T>(at index: T) -> String where T: FixedWidthInteger { | |
return String(self[self.index(self.startIndex, offsetBy: Int(index))]) | |
} | |
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) ) | |
} | |
} | |
enum BaseError: Swift.Error { | |
case outOfRange | |
} | |
extension FixedWidthInteger where Self.Magnitude : UnsignedInteger { | |
func base36() -> String { return try! self.base(36) } | |
func base62() -> String { return try! self.base(62) } | |
func baseEmoji() -> String { return try! self.base(String.DefaultEmoji.count, using: String.DefaultEmoji) } | |
func base<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 = Int(self) | |
let rdx = Int(redux) | |
repeat { | |
result = digits.character(at: value % rdx) + result | |
value /= rdx | |
} while (value > 0) | |
return result | |
} | |
} | |
extension String { | |
func decode36() -> Int { return try! self.decode(36) } | |
func decode62() -> Int { return try! self.decode(62) } | |
func decode(_ redux: Int, using digits: String = .DefaultDigits) throws -> Int { | |
let decoded = try self.reduce(0) { | |
let value = Double( try digits.position(of: $1) ) | |
return Int( Double($0) * Double(redux) + value ) | |
} | |
return decoded | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment