Created
September 9, 2015 03:12
-
-
Save xtravar/6b52f59fb133229b360e to your computer and use it in GitHub Desktop.
This file contains 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
// | |
// Punycode.swift | |
// | |
// Created by Mike Kasianowicz on 9/7/15. | |
// Copyright © 2015 Mike Kasianowicz. All rights reserved. | |
// | |
import Foundation | |
public class Punycode { | |
//MARK: public static | |
// RFC 3492 implementation | |
public static let official = Punycode( | |
delimiter: "-", | |
encodeTable: "abcdefghijklmnopqrstuvwxyz0123456789" | |
) | |
// used for Swift name mangling - presumably to avoid digit interference | |
public static let swift = Punycode( | |
delimiter: "_", | |
encodeTable: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJ" | |
) | |
//MARK: variables | |
private let base = 36 | |
private let tMin = 1 | |
private let tMax = 26 | |
private let skew = 38 | |
private let damp = 700 | |
private let initialBias = 72 | |
private let initialN = 0x80 | |
private let delimiter : Character | |
private let encodeTable : [Character] | |
private let decodeTable : [Character : Int] | |
//MARK: initializers | |
public convenience init(delimiter: Character, encodeTable: String) { | |
self.init(delimiter: delimiter, encodeTable: [Character](encodeTable.characters)) | |
} | |
public required init(delimiter: Character, encodeTable: [Character]) { | |
self.delimiter = delimiter | |
self.encodeTable = encodeTable | |
var decodeTable = [Character : Int]() | |
encodeTable.enumerate().forEach { ( kvp: (Int, Character)) -> () in | |
decodeTable[kvp.1] = kvp.0 | |
} | |
self.decodeTable = decodeTable | |
} | |
//MARK: encode | |
public func encode(unicode: String) -> String { | |
var retval = "" | |
var extendedChars = [Int]() | |
for c in unicode.unicodeScalars { | |
let ci = Int(c.value) | |
if ci < initialN { | |
retval.append(c) | |
} else { | |
extendedChars.append(ci) | |
} | |
} | |
if extendedChars.count == 0 { | |
return retval | |
} | |
retval.append(delimiter) | |
extendedChars.sortInPlace() | |
var bias = initialBias | |
var delta = 0 | |
var n = initialN | |
var h = retval.unicodeScalars.count - 1 | |
let b = retval.unicodeScalars.count - 1 | |
for var i = 0; h < unicode.unicodeScalars.count; { | |
let char = extendedChars[i++] | |
delta = delta + (char - n) * (h + 1) | |
n = char | |
for c in unicode.unicodeScalars { | |
let ci = Int(c.value) | |
if ci < n || ci < initialN { | |
delta++ | |
} | |
if ci == n { | |
var q = delta | |
for var k = self.base; ; k += base { | |
let t = max(min(k - bias, self.tMax), self.tMin) | |
if q < t { | |
break | |
} | |
let code = t + ((q - t) % (self.base - t)) | |
retval.append(self.encodeTable[code]) | |
q = (q - t) / (self.base - t) | |
} | |
retval.append(self.encodeTable[q]) | |
bias = self.adapt(delta, h + 1, h == b) | |
delta = 0 | |
h++ | |
} | |
} | |
delta++ | |
n++ | |
} | |
return retval | |
} | |
private func adapt(var delta: Int, _ numPoints: Int, _ firstTime: Bool) -> Int { | |
delta = delta / (firstTime ? self.damp : 2) | |
delta += delta / numPoints | |
var k = 0 | |
while (delta > ((self.base - self.tMin) * self.tMax) / 2) { | |
delta = delta / (self.base - self.tMin) | |
k = k + self.base | |
} | |
k += ((self.base - self.tMin + 1) * delta) / (delta + self.skew) | |
return k | |
} | |
//MARK: decode | |
public func decode(punycode: String) -> String { | |
var input = [Character](punycode.characters) | |
var n = self.initialN | |
var i = 0 | |
var bias = self.initialBias | |
var output = [Character]() | |
var pos = 0 | |
if let ipos = input.indexOf(self.delimiter) { | |
pos = ipos | |
output.appendContentsOf(input[0 ..< pos++]) | |
} | |
var outputLength = output.count | |
let inputLength = input.count | |
while pos < inputLength { | |
let oldi = i | |
var w = 1 | |
for var k = self.base;; k += self.base { | |
let digit = self.decodeTable[input[pos++]]! | |
i = i + (digit * w) | |
let t = max(min(k - bias, self.tMax), self.tMin) | |
if (digit < t) { | |
break | |
} | |
w = w * (self.base - t) | |
} | |
bias = self.adapt(i - oldi, ++outputLength, (oldi == 0)) | |
n = n + i / outputLength | |
i = i % outputLength | |
output.insert(Character(UnicodeScalar(n)), atIndex: i) | |
i++ | |
} | |
return String(output) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
What's the license?
Thanks