# # # Nim's Runtime Library # (c) Copyright 2016 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # ## .. note:: In order to use this module, run `nimble install punycode`. ## ## Implements a representation of Unicode with the limited ## ASCII character subset. import strutils import unicode # issue #3045 const Base = 36 TMin = 1 TMax = 26 Skew = 38 Damp = 700 InitialBias = 72 InitialN = 128 Delimiter = '-' type PunyError* = object of ValueError func decodeDigit(x: char): int {.raises: [PunyError].} = if '0' <= x and x <= '9': result = ord(x) - (ord('0') - 26) elif 'A' <= x and x <= 'Z': result = ord(x) - ord('A') elif 'a' <= x and x <= 'z': result = ord(x) - ord('a') else: raise newException(PunyError, "Bad input") func encodeDigit(digit: int): Rune {.raises: [PunyError].} = if 0 <= digit and digit < 26: result = Rune(digit + ord('a')) elif 26 <= digit and digit < 36: result = Rune(digit + (ord('0') - 26)) else: raise newException(PunyError, "internal error in punycode encoding") func isBasic(c: char): bool = ord(c) < 0x80 func isBasic(r: Rune): bool = int(r) < 0x80 func adapt(delta, numPoints: int, first: bool): int = var d = if first: delta div Damp else: delta div 2 d += d div numPoints var k = 0 while d > ((Base-TMin)*TMax) div 2: d = d div (Base - TMin) k += Base result = k + (Base - TMin + 1) * d div (d + Skew) func encode*(prefix, s: string): string {.raises: [PunyError].} = ## Encode a string that may contain Unicode. ## Prepend `prefix` to the result result = prefix var (d, n, bias) = (0, InitialN, InitialBias) var (b, remaining) = (0, 0) for r in s.runes: if r.isBasic: # basic Ascii character inc b result.add($r) else: # special character inc remaining var h = b if b > 0: result.add(Delimiter) # we have some Ascii chars while remaining != 0: var m: int = high(int32) for r in s.runes: if m > int(r) and int(r) >= n: m = int(r) d += (m - n) * (h + 1) if d < 0: raise newException(PunyError, "invalid label " & s) n = m for r in s.runes: if int(r) < n: inc d if d < 0: raise newException(PunyError, "invalid label " & s) continue if int(r) > n: continue var q = d var k = Base while true: var t = k - bias if t < TMin: t = TMin elif t > TMax: t = TMax if q < t: break result.add($encodeDigit(t + (q - t) mod (Base - t))) q = (q - t) div (Base - t) k += Base result.add($encodeDigit(q)) bias = adapt(d, h + 1, h == b) d = 0 inc h dec remaining inc d inc n func encode*(s: string): string {.raises: [PunyError].} = ## Encode a string that may contain Unicode. Prefix is empty. result = encode("", s) func decode*(encoded: string): string {.raises: [PunyError].} = ## Decode a Punycode-encoded string var n = InitialN i = 0 bias = InitialBias var d = rfind(encoded, Delimiter) var output: seq[Rune] if d > 0: # found Delimiter for j in 0..pre { line-height: 125%; } td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } .highlight .hll { background-color: #ffffcc } .highlight .c { color: #888888 } /* Comment */ .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ .highlight .k { color: #008800; font-weight: bold } /* Keyword */ .highlight .ch { color: #888888 } /* Comment.Hashbang */ .highlight .cm { color: #888888 } /* Comment.Multiline */ .highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */ .highlight .cpf { color: #888888 } /* Comment.PreprocFile */ .highlight .c1 { color: #888888 } /* Comment.Single */ .highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */ .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */ .highlight .gr { color: #aa0000 } /* Generic.Error */ .highlight .gh { color: #333333 } /* Generic.Heading */ .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ .highlight .go { color: #888888 } /* Generic.Output */ .highlight .gp { color: #555555 } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #666666 } /* Generic.Subheading */ .highlight .gt { color: #aa0000 } /* Generic.Traceback */ .highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #008800 } /* Keyword.Pseudo */ .highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #888888; font-weight: bold } /* Keyw