-
Notifications
You must be signed in to change notification settings - Fork 7
/
punycode.ml
173 lines (159 loc) · 5.07 KB
/
punycode.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
(* RFC 3492 *)
exception Error
exception Overflow
let base = 36
let tmin = 1
let tmax = 26
let skew = 38
let damp = 700
let initial_bias = 72
let initial_n = 128
let delimiter = 0x2D
let is_basic cp = cp < 0x80
let decode_digit cp =
if cp - 48 < 10 then
cp - 22
else if cp - 65 < 26 then
cp - 65
else if cp - 97 < 26 then
cp - 97
else
raise Error
let encode_digit d =
d + 22 + 75 * (if d < 26 then 1 else 0)
let adapt_bias delta numpoints firsttime =
let delta = if firsttime then delta / damp else delta asr 1 in
let delta = delta + (delta / numpoints) in
let rec aux_while k delta =
if delta > ((base - tmin) * tmax) asr 1 then
aux_while (k+base) (delta / (base - tmin))
else
k, delta
in
let k, delta = aux_while 0 delta in
k + (((base - tmin + 1) * delta) / (delta + skew))
let insert i n outstr =
let rec aux_insert j = function
| [] -> [n]
| x :: xs ->
if j = i then
n ::x::xs
else
x::aux_insert (succ j) xs
in
aux_insert 0 outstr
let decode instr =
let instr_len = String.length instr in
let n = initial_n in
let i = 0 in
let bias = initial_bias in
let idx =
try String.rindex instr '-'
with _ -> 0 in
let idx, outstr =
if idx = 0 then
idx, []
else
let rec aux_copy j outstr =
if j < idx then
if is_basic (Char.code instr.[j]) then
aux_copy (succ j) ((Char.code instr.[j]) :: outstr)
else
raise Error
else
List.rev outstr
in
succ idx, aux_copy 0 [] in
let rec aux_decode idx i bias n outstr outstr_len =
if idx < instr_len then (
let oldi = i in
let rec aux k i w idx =
if idx >= instr_len then raise Error;
let digit = decode_digit (Char.code instr.[idx]) in
let i = i + digit * w in (* TODO: check overflow *)
let t =
if k <= bias then tmin
else if k >= bias + tmax then tmax else k - bias in
if digit < t then
(oldi, i, idx)
else
let w = w * (base - t) in (* TODO: check overflow *)
aux (k + base) i w (succ idx)
in
let oldi, i, idx = aux base i 1 idx in
let bias = adapt_bias (i - oldi) (outstr_len + 1) (oldi = 0) in
let n = n + i / (outstr_len + 1) in (* TODO: check overflow *)
let i = i mod (List.length outstr + 1) in
if is_basic n then raise Error;
let outstr = insert i n outstr in
aux_decode (succ idx) (succ i) bias n outstr (succ outstr_len)
) else
outstr
in
aux_decode idx i bias n outstr (List.length outstr)
let encode instr =
let instr_len = Array.length instr in
let n = initial_n in
let delta = 0 in
let bias = initial_bias in
let rec aux_collect_bcp j outstr =
if j < instr_len then
if is_basic instr.(j) then
aux_collect_bcp (succ j) (instr.(j) :: outstr)
else
aux_collect_bcp (succ j) outstr
else
outstr
in
let outstr = aux_collect_bcp 0 [] in
let b = List.length outstr in
let h = b in
let outstr = if b > 0 then delimiter :: outstr else outstr in
let rec aux_encode h n bias delta outstr =
if h < instr_len then
let rec get_min j m =
if j < instr_len then
if instr.(j) >= n && instr.(j) < m then get_min (succ j) instr.(j)
else get_min (succ j) m
else
m
in
let m = get_min 0 0x10FFFF in
let delta = delta + (m - n) * (h + 1) in (* TODO: check overflow *)
let n = m in
let rec aux_foreach idx h delta bias outstr =
if idx < instr_len then
if instr.(idx) < n then
(* TODO: check overflow for delta + 1 *)
aux_foreach (succ idx) h (succ delta) bias outstr
else if instr.(idx) = n then
let rec aux_for k q outstr =
let t = if k <= bias then tmin
else if k >= bias + tmax then tmax else k - bias in
if q < t then
q, outstr
else
let o = encode_digit (t + ((q - t) mod (base - t))) in
let q = (q - t) / (base - t) in
aux_for (k + base) q (o::outstr)
in
let q, outstr = aux_for base delta outstr in
let outstr = encode_digit q :: outstr in
let bias = adapt_bias delta (succ h) (h = b) in
let delta = 0 in
aux_foreach (succ idx) (succ h) delta bias outstr
else
aux_foreach (succ idx) h delta bias outstr
else
h, delta, bias, outstr
in
let h, delta, bias, outstr = aux_foreach 0 h delta bias outstr in
aux_encode h (succ n) bias (succ delta) outstr
else
List.rev outstr
in
let outstr = aux_encode h n bias delta outstr in
let len = List.length outstr in
let str = String.create len in
let _ = List.fold_left (fun i c -> str.[i] <- Char.chr c; succ i) 0 outstr in
str