b64.bend
cswap-bend-cli@0.1.0.0 · 223 lines · 33 definitions
1import Base
2
3
4
5
6
7
8def utf8.cat.pick(a: Bool, b: Bool, d: Bool, +c: U32, rest: List<&2, U32>) -> List<&2, U32>:
9 match a b d:
10 case True{} _ _:
11 c <> rest
12 case False{} True{} _:
13 (192 .|. (c >> 6n) : U32) <> (128 .|. (c .&. 63) : U32) <> rest
14 case False{} False{} True{}:
15 (224 .|. (c >> 12n) : U32) <> (128 .|. ((c >> 6n) .&. 63) : U32) <> (128 .|. (c .&. 63) : U32) <> rest
16 case False{} False{} False{}:
17 (240 .|. (c >> 18n) : U32) <> (128 .|. ((c >> 12n) .&. 63) : U32) <> (128 .|. ((c >> 6n) .&. 63) : U32) <> (128 .|. (c .&. 63) : U32) <> rest
18
19def utf8.cat(+c: U32, rest: List<&2, U32>) -> List<&2, U32>:
20 utf8.cat.pick(U32.is_lt(c, 128), U32.is_lt(c, 2048), U32.is_lt(c, 65536), c, rest)
21
22
23def utf8.encode(s: String) -> List<&2, U32>:
24 match s:
25 case SNil{}:
26 []
27 case SCon{Chr{c}, t}:
28 utf8.cat(c, utf8.encode(t))
29
30def sel(c: Bool, x: U32, y: U32) -> U32:
31 match c:
32 case True{}:
33 x
34 case False{}:
35 y
36
37def sel.b(c: Bool, x: Bool, y: Bool) -> Bool:
38 match c:
39 case True{}:
40 x
41 case False{}:
42 y
43
44
45def utf8.len(+b: U32) -> U32:
46 sel(U32.is_lt(b, 128), 0, sel(U32.is_lt(b, 224), 1, sel(U32.is_lt(b, 240), 2, 3)))
47
48def utf8.mask(+n: U32) -> U32:
49 sel(U32.is_eq(n, 0), 255, sel(U32.is_eq(n, 1), 31, sel(U32.is_eq(n, 2), 15, 7)))
50
51
52
53
54def utf8.need(+b: U32, +need: U32) -> U32:
55 sel(U32.is_eq(need, 0), utf8.len(b), (need - 1 : U32))
56
57def utf8.cp(+b: U32, +need: U32, +cp: U32) -> U32:
58 sel(U32.is_eq(need, 0), (b .&. utf8.mask(utf8.len(b)) : U32), ((cp << 6n) .|. (b .&. 63) : U32))
59
60def utf8.emit(+b: U32, +need: U32) -> Bool:
61 sel.b(U32.is_eq(need, 0), U32.is_eq(utf8.len(b), 0), U32.is_eq(need, 1))
62
63def utf8.put(emit: Bool, c: U32, rest: String) -> String:
64 match emit:
65 case True{}:
66 SCon{Chr{c}, rest}
67 case False{}:
68 rest
69
70
71def utf8.decode.go(bs: List<&2, U32>, +need: U32, +cp: U32) -> String:
72 match bs:
73 case Nil{}:
74 SNil{}
75 case Con{+b, t}:
76 +cp2 = utf8.cp(b, need, cp)
77 utf8.put(utf8.emit(b, need), cp2, utf8.decode.go(t, utf8.need(b, need), cp2))
78
79def utf8.decode(bs: List<&2, U32>) -> String:
80 utf8.decode.go(bs, 0, 0)
81
82
83
84def b64.char(+n: U32) -> Char:
85 Chr{sel(U32.is_lt(n, 26), (n + 65 : U32),
86 sel(U32.is_lt(n, 52), (n + 71 : U32),
87 sel(U32.is_lt(n, 62), (n - 4 : U32),
88 sel(U32.is_eq(n, 62), 43, 47))))}
89
90def b64.enc.go(bs: List<&2, U32>) -> String:
91 match bs:
92 case Nil{}:
93 SNil{}
94 case Con{+a, Nil{}}:
95 SCon{b64.char((a >> 2n : U32)), SCon{b64.char(((a .&. 3) << 4n : U32)), "=="}}
96 case Con{+a, Con{+b, Nil{}}}:
97 SCon{b64.char((a >> 2n : U32)), SCon{b64.char((((a .&. 3) << 4n) .|. (b >> 4n) : U32)),
98 SCon{b64.char(((b .&. 15) << 2n : U32)), "="}}}
99 case Con{+a, Con{+b, Con{+c, t}}}:
100 SCon{b64.char((a >> 2n : U32)), SCon{b64.char((((a .&. 3) << 4n) .|. (b >> 4n) : U32)),
101 SCon{b64.char((((b .&. 15) << 2n) .|. (c >> 6n) : U32)), SCon{b64.char((c .&. 63 : U32)),
102 b64.enc.go(t)}}}}
103
104
105def encode_bytes(bs: List<&2, U32>) -> String:
106 b64.enc.go(bs)
107
108
109def encode(s: String) -> String:
110 encode_bytes(utf8.encode(s))
111
112
113def b64.val(+c: U32) -> U32:
114 sel(U32.is_ge(c, 65) && U32.is_le(c, 90), (c - 65 : U32),
115 sel(U32.is_ge(c, 97) && U32.is_le(c, 122), (c - 71 : U32),
116 sel(U32.is_ge(c, 48) && U32.is_le(c, 57), (c + 4 : U32),
117 sel(U32.is_eq(c, 43), 62,
118 sel(U32.is_eq(c, 47), 63,
119 sel(U32.is_eq(c, 61), 64, 255))))))
120
121def b64.quad.emit(p3: Bool, p4: Bool, a: U32, b: U32, c: U32, r: List<&2, U32>) -> Maybe<&2, List<&2, U32>>:
122 match p3 p4:
123 case True{} _:
124 Some{a <> r}
125 case False{} True{}:
126 Some{a <> b <> r}
127 case False{} False{}:
128 Some{a <> b <> c <> r}
129
130def b64.quad.pick(ok: Bool, p3: Bool, p4: Bool, +w: U32, +x: U32, +y: U32, +z: U32,
131 rest: Maybe<&2, List<&2, U32>>) -> Maybe<&2, List<&2, U32>>:
132 match ok rest:
133 case False{} _:
134 None{}
135 case True{} None{}:
136 None{}
137 case True{} Some{r}:
138 b64.quad.emit(p3, p4, ((w << 2n) .|. (x >> 4n) : U32), (((x .&. 15) << 4n) .|. (y >> 2n) : U32),
139 (((y .&. 3) << 6n) .|. z : U32), r)
140
141def b64.dec.quad(+w: U32, +x: U32, +y: U32, +z: U32, rest: Maybe<&2, List<&2, U32>>) -> Maybe<&2, List<&2, U32>>:
142 b64.quad.pick(U32.is_lt(w, 64) && U32.is_lt(x, 64) && (U32.is_lt(y, 64) || U32.is_eq(y, 64)) && (U32.is_lt(z, 64) || U32.is_eq(z, 64)),
143 U32.is_eq(y, 64), U32.is_eq(z, 64), w, x, y, z, rest)
144
145
146type Quad is Data:
147 QEnd{}
148 QBad{}
149 Q{w: U32, x: U32, y: U32, z: U32, rest: String}
150
151def b64.quad.of4(c1: U32, c2: U32, c3: U32, s: String) -> Quad:
152 match s:
153 case SCon{Chr{c4}, t}:
154 Q{b64.val(c1), b64.val(c2), b64.val(c3), b64.val(c4), t}
155 case _:
156 QBad{}
157
158def b64.quad.of3(c1: U32, c2: U32, s: String) -> Quad:
159 match s:
160 case SCon{Chr{c3}, t}:
161 b64.quad.of4(c1, c2, c3, t)
162 case _:
163 QBad{}
164
165def b64.quad.of2(c1: U32, s: String) -> Quad:
166 match s:
167 case SCon{Chr{c2}, t}:
168 b64.quad.of3(c1, c2, t)
169 case _:
170 QBad{}
171
172def b64.quad.of(s: String) -> Quad:
173 match s:
174 case SNil{}:
175 QEnd{}
176 case SCon{Chr{c1}, t}:
177 b64.quad.of2(c1, t)
178 case _:
179 QBad{}
180
181
182def b64.dec.go(fuel: Nat, q: Quad) -> Maybe<&2, List<&2, U32>>:
183 match fuel q:
184 case _ QEnd{}:
185 Some{[]}
186 case _ QBad{}:
187 None{}
188 case 0n _:
189 None{}
190 case 1n+p Q{w, x, y, z, t}:
191 b64.dec.quad(w, x, y, z, b64.dec.go(p, b64.quad.of(t)))
192
193def b64.dec.start(+s: String) -> Maybe<&2, List<&2, U32>>:
194 b64.dec.go(String.length(s), b64.quad.of(s))
195
196def b64.strip.put(sp: Bool, c: Char, rest: String) -> String:
197 match sp:
198 case True{}:
199 rest
200 case False{}:
201 SCon{c, rest}
202
203def b64.strip(s: String) -> String:
204 match s:
205 case SNil{}:
206 SNil{}
207 case SCon{+c, t}:
208 b64.strip.put(Char.is_space(c), c, b64.strip(t))
209
210
211def decode_bytes(s: String) -> Maybe<&2, List<&2, U32>>:
212 b64.dec.start(b64.strip(s))
213
214def b64.text(m: Maybe<&2, List<&2, U32>>) -> Maybe<&2, String>:
215 match m:
216 case None{}:
217 None{}
218 case Some{bs}:
219 Some{utf8.decode(bs)}
220
221
222def decode(s: String) -> Maybe<&2, String>:
223 b64.text(decode_bytes(s))