Skip to content

Instantly share code, notes, and snippets.

@rjstelling
Last active January 24, 2020 12:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rjstelling/0216a4f9bfaec87dec44d30ac65399b1 to your computer and use it in GitHub Desktop.
Save rjstelling/0216a4f9bfaec87dec44d30ac65399b1 to your computer and use it in GitHub Desktop.
Simple base encoding and decoding in Swift.
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
}
}
@rjstelling
Copy link
Author

I've updated this to prevent some bad use-cases and remove some bugs with large values.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment