base64.bend
bend-encoding@0.2.0.0 · 496 lines · 0 definitions
1import Base
2import bend-encoding@0.2.0.0/bytes.bend as B
3
4type B64.Alpha is Data:
5 B64Std{}
6 B64Url{}
7
8type B64.Pad is Data:
9 B64Pad{}
10 B64Raw{}
11
12type B64.G is Data:
13 G0{}
14 G1{}
15 G2{}
16
17type B64.D is Data:
18 D0{}
19 D1{a: U32}
20 D2{a: U32, b: U32}
21 D3{a: U32, b: U32, c: U32}
22 Eq1{a: U32, b: U32}
23 PadEnd{}
24
25type B64.Tok is Data:
26 TokVal{n: U32}
27 TokPad{}
28 TokBad{c: Char}
29
30type Base64.Error is Data:
31 InvalidChar{offset: Nat, char: Char}
32 InvalidLength{offset: Nat}
33 NonCanonical{offset: Nat}
34 InvalidByte{index: Nat, value: U32}
35
36def Base64.from_bytes_err(e: B.Bytes.Error) -> Base64.Error:
37 match e:
38 case B.InvalidByte{index, value}:
39 InvalidByte{index, value}
40
41def B64.ch.62(alpha: B64.Alpha) -> Char:
42 match alpha:
43 case B64Std{}:
44 '+'
45 case B64Url{}:
46 '-'
47
48def B64.ch.63(alpha: B64.Alpha) -> Char:
49 match alpha:
50 case B64Std{}:
51 '/'
52 case B64Url{}:
53 '_'
54
55def B64.ch.pm(+alpha: B64.Alpha, is62: Bool) -> Char:
56 match is62:
57 case True{}:
58 B64.ch.62(alpha)
59 case False{}:
60 B64.ch.63(alpha)
61
62def B64.ch.lt62(+n: U32, lt: Bool, +alpha: B64.Alpha) -> Char:
63 match lt:
64 case True{}:
65 Char.from_u32((Char.to_u32('0') + (n - 52 : U32) : U32))
66 case False{}:
67 B64.ch.pm(alpha, U32.is_eq(n, 62))
68
69def B64.ch.lt52(+n: U32, lt: Bool, +alpha: B64.Alpha) -> Char:
70 match lt:
71 case True{}:
72 Char.from_u32((Char.to_u32('a') + (n - 26 : U32) : U32))
73 case False{}:
74 B64.ch.lt62(n, U32.is_lt(n, 62), alpha)
75
76def B64.ch.lt26(+n: U32, lt: Bool, +alpha: B64.Alpha) -> Char:
77 match lt:
78 case True{}:
79 Char.from_u32((Char.to_u32('A') + n : U32))
80 case False{}:
81 B64.ch.lt52(n, U32.is_lt(n, 52), alpha)
82
83def B64.ch(+n: U32, +alpha: B64.Alpha) -> Char:
84 B64.ch.lt26(n, U32.is_lt(n, 26), alpha)
85
86def B64.push4(
87 s0: U32, s1: U32, s2: U32, s3: U32, acc: String, +alpha: B64.Alpha
88) -> String:
89 SCon{
90 B64.ch(s3, alpha),
91 SCon{
92 B64.ch(s2, alpha),
93 SCon{B64.ch(s1, alpha), SCon{B64.ch(s0, alpha), acc}}
94 }
95 }
96
97def B64.emit3(+a: U32, +b: U32, +c: U32, acc: String, +alpha: B64.Alpha) -> String:
98 B64.push4(
99 U32.shrn(a, 2n),
100 U32.or(U32.shln(U32.and(a, 3), 4n), U32.shrn(b, 4n)),
101 U32.or(U32.shln(U32.and(b, 15), 2n), U32.shrn(c, 6n)),
102 U32.and(c, 63),
103 acc,
104 alpha
105 )
106
107def B64.pad1.fin(
108 s0: U32, s1: U32, acc: String, +alpha: B64.Alpha, pad: B64.Pad
109) -> String:
110 match pad:
111 case B64Pad{}:
112 SCon{
113 '=', SCon{'=', SCon{B64.ch(s1, alpha), SCon{B64.ch(s0, alpha), acc}}}
114 }
115 case B64Raw{}:
116 SCon{B64.ch(s1, alpha), SCon{B64.ch(s0, alpha), acc}}
117
118def B64.pad1(+a: U32, acc: String, +alpha: B64.Alpha, pad: B64.Pad) -> String:
119 B64.pad1.fin(U32.shrn(a, 2n), U32.shln(U32.and(a, 3), 4n), acc, alpha, pad)
120
121def B64.pad2.fin(
122 s0: U32, s1: U32, s2: U32, acc: String, +alpha: B64.Alpha, pad: B64.Pad
123) -> String:
124 match pad:
125 case B64Pad{}:
126 SCon{
127 '=',
128 SCon{
129 B64.ch(s2, alpha),
130 SCon{B64.ch(s1, alpha), SCon{B64.ch(s0, alpha), acc}}
131 }
132 }
133 case B64Raw{}:
134 SCon{
135 B64.ch(s2, alpha), SCon{B64.ch(s1, alpha), SCon{B64.ch(s0, alpha), acc}}
136 }
137
138def B64.pad2(+a: U32, +b: U32, acc: String, +alpha: B64.Alpha, pad: B64.Pad) -> String:
139 B64.pad2.fin(
140 U32.shrn(a, 2n),
141 U32.or(U32.shln(U32.and(a, 3), 4n), U32.shrn(b, 4n)),
142 U32.shln(U32.and(b, 15), 2n),
143 acc,
144 alpha,
145 pad
146 )
147
148def Base64.encode.go(
149 xs: List<&2, U32>, phase: B64.G, a: U32, b: U32, acc: String,
150 +alpha: B64.Alpha, +pad: B64.Pad
151) -> String:
152 match xs phase:
153 case Nil{} G0{}:
154 String.reverse(acc)
155 case Nil{} G1{}:
156 String.reverse(B64.pad1(a, acc, alpha, pad))
157 case Nil{} G2{}:
158 String.reverse(B64.pad2(a, b, acc, alpha, pad))
159 case Con{+h, t} G0{}:
160 Base64.encode.go(t, G1{}, h, 0, acc, alpha, pad)
161 case Con{+h, t} G1{}:
162 Base64.encode.go(t, G2{}, a, h, acc, alpha, pad)
163 case Con{+h, t} G2{}:
164 Base64.encode.go(t, G0{}, 0, 0, B64.emit3(a, b, h, acc, alpha), alpha, pad)
165
166def Base64.encode.with(bytes: B.Bytes, +alpha: B64.Alpha, +pad: B64.Pad) -> String:
167 match bytes:
168 case B.Bytes{data}:
169 Base64.encode.go(data, G0{}, 0, 0, SNil{}, alpha, pad)
170
171def Base64.encode(bytes: B.Bytes) -> String:
172 Base64.encode.with(bytes, B64Std{}, B64Pad{})
173
174def Base64Url.encode.p(bytes: B.Bytes, padded: Bool) -> String:
175 match padded:
176 case True{}:
177 Base64.encode.with(bytes, B64Url{}, B64Pad{})
178 case False{}:
179 Base64.encode.with(bytes, B64Url{}, B64Raw{})
180
181def Base64Url.encode(bytes: B.Bytes, padded: Bool) -> String:
182 Base64Url.encode.p(bytes, padded)
183
184def B64.tok.under.yes(alpha: B64.Alpha, +c: Char) -> B64.Tok:
185 match alpha:
186 case B64Url{}:
187 TokVal{63}
188 case B64Std{}:
189 TokBad{c}
190
191def B64.tok.under(under: Bool, alpha: B64.Alpha, +c: Char) -> B64.Tok:
192 match under:
193 case False{}:
194 TokBad{c}
195 case True{}:
196 B64.tok.under.yes(alpha, c)
197
198def B64.tok.minus.yes(alpha: B64.Alpha, +c: Char) -> B64.Tok:
199 match alpha:
200 case B64Url{}:
201 TokVal{62}
202 case B64Std{}:
203 TokBad{c}
204
205def B64.tok.minus(minus: Bool, +alpha: B64.Alpha, +c: Char) -> B64.Tok:
206 match minus:
207 case True{}:
208 B64.tok.minus.yes(alpha, c)
209 case False{}:
210 B64.tok.under(Char.is_eq(c, '_'), alpha, c)
211
212def B64.tok.slash.yes(alpha: B64.Alpha, +c: Char) -> B64.Tok:
213 match alpha:
214 case B64Std{}:
215 TokVal{63}
216 case B64Url{}:
217 TokBad{c}
218
219def B64.tok.slash(slash: Bool, +alpha: B64.Alpha, +c: Char) -> B64.Tok:
220 match slash:
221 case True{}:
222 B64.tok.slash.yes(alpha, c)
223 case False{}:
224 B64.tok.minus(Char.is_eq(c, '-'), alpha, c)
225
226def B64.tok.plus.yes(alpha: B64.Alpha, +c: Char) -> B64.Tok:
227 match alpha:
228 case B64Std{}:
229 TokVal{62}
230 case B64Url{}:
231 TokBad{c}
232
233def B64.tok.plus(plus: Bool, +alpha: B64.Alpha, +c: Char) -> B64.Tok:
234 match plus:
235 case True{}:
236 B64.tok.plus.yes(alpha, c)
237 case False{}:
238 B64.tok.slash(Char.is_eq(c, '/'), alpha, c)
239
240def B64.tok.sym(+c: Char, +alpha: B64.Alpha) -> B64.Tok:
241 B64.tok.plus(Char.is_eq(c, '+'), alpha, c)
242
243def B64.tok.az2(+x: U32, +c: Char, +alpha: B64.Alpha, le: Bool) -> B64.Tok:
244 match le:
245 case True{}:
246 TokVal{(x - Char.to_u32('G') : U32)}
247 case False{}:
248 B64.tok.sym(c, alpha)
249
250def B64.tok.az(+x: U32, +c: Char, +alpha: B64.Alpha, ge: Bool) -> B64.Tok:
251 match ge:
252 case False{}:
253 B64.tok.sym(c, alpha)
254 case True{}:
255 B64.tok.az2(x, c, alpha, U32.is_le(x, Char.to_u32('z')))
256
257def B64.tok.AZ2(+x: U32, +c: Char, +alpha: B64.Alpha, le: Bool) -> B64.Tok:
258 match le:
259 case True{}:
260 TokVal{(x - Char.to_u32('A') : U32)}
261 case False{}:
262 B64.tok.az(x, c, alpha, U32.is_ge(x, Char.to_u32('a')))
263
264def B64.tok.AZ(+x: U32, +c: Char, +alpha: B64.Alpha, ge: Bool) -> B64.Tok:
265 match ge:
266 case False{}:
267 B64.tok.az(x, c, alpha, U32.is_ge(x, Char.to_u32('a')))
268 case True{}:
269 B64.tok.AZ2(x, c, alpha, U32.is_le(x, Char.to_u32('Z')))
270
271def B64.tok.d092(+x: U32, +c: Char, +alpha: B64.Alpha, le: Bool) -> B64.Tok:
272 match le:
273 case True{}:
274 TokVal{(x - Char.to_u32('0') + 52 : U32)}
275 case False{}:
276 B64.tok.AZ(x, c, alpha, U32.is_ge(x, Char.to_u32('A')))
277
278def B64.tok.d09(+x: U32, +c: Char, +alpha: B64.Alpha, ge: Bool) -> B64.Tok:
279 match ge:
280 case False{}:
281 B64.tok.AZ(x, c, alpha, U32.is_ge(x, Char.to_u32('A')))
282 case True{}:
283 B64.tok.d092(x, c, alpha, U32.is_le(x, Char.to_u32('9')))
284
285def B64.tok.eq(+x: U32, +c: Char, +alpha: B64.Alpha, eq: Bool) -> B64.Tok:
286 match eq:
287 case True{}:
288 TokPad{}
289 case False{}:
290 B64.tok.d09(x, c, alpha, U32.is_ge(x, Char.to_u32('0')))
291
292def B64.tok.u(+x: U32, +c: Char, +alpha: B64.Alpha) -> B64.Tok:
293 B64.tok.eq(x, c, alpha, U32.is_eq(x, Char.to_u32('=')))
294
295def B64.tok(+c: Char, +alpha: B64.Alpha) -> B64.Tok:
296 B64.tok.u(Char.to_u32(c), c, alpha)
297
298def B64.zero4(b: U32) -> Bool:
299 U32.is_eq(U32.and(b, 15), 0)
300
301def B64.zero2(c: U32) -> Bool:
302 U32.is_eq(U32.and(c, 3), 0)
303
304def B64.byte1(+a: U32, +b: U32) -> U32:
305 U32.or(U32.shln(a, 2n), U32.shrn(b, 4n))
306
307def B64.byte2(+b: U32, +c: U32) -> U32:
308 U32.or(U32.shln(U32.and(b, 15), 4n), U32.shrn(c, 2n))
309
310def B64.byte3(+c: U32, d: U32) -> U32:
311 U32.or(U32.shln(U32.and(c, 3), 6n), d)
312
313def B64.fin1(
314 ok: Bool, +a: U32, +b: U32, acc: List<&2, U32>, off: Nat
315) -> Result<&2, &2, Base64.Error, B.Bytes>:
316 match ok:
317 case False{}:
318 Fail{NonCanonical{off}}
319 case True{}:
320 Done{B.Bytes{List.reverse(&2, U32, Con{B64.byte1(a, b), acc})}}
321
322def B64.fin1.from(+a: U32, +b: U32, acc: List<&2, U32>, off: Nat) ->
323 Result<&2, &2, Base64.Error, B.Bytes>:
324 B64.fin1(B64.zero4(b), a, b, acc, off)
325
326def B64.fin2(
327 ok: Bool, +a: U32, +b: U32, +c: U32, acc: List<&2, U32>, off: Nat
328) -> Result<&2, &2, Base64.Error, B.Bytes>:
329 match ok:
330 case False{}:
331 Fail{NonCanonical{off}}
332 case True{}:
333 Done{
334 B.Bytes{
335 List.reverse(
336 &2, U32, Con{B64.byte2(b, c), Con{B64.byte1(a, b), acc}}
337 )
338 }
339 }
340
341def B64.fin2.from(+a: U32, +b: U32, +c: U32, acc: List<&2, U32>, off: Nat) ->
342 Result<&2, &2, Base64.Error, B.Bytes>:
343 B64.fin2(B64.zero2(c), a, b, c, acc, off)
344
345def B64.emit2(+a: U32, +b: U32, +c: U32, acc: List<&2, U32>) -> List<&2, U32>:
346 Con{B64.byte2(b, c), Con{B64.byte1(a, b), acc}}
347
348def B64.emit4(
349 +a: U32, +b: U32, +c: U32, d: U32, acc: List<&2, U32>
350) -> List<&2, U32>:
351 Con{B64.byte3(c, d), Con{B64.byte2(b, c), Con{B64.byte1(a, b), acc}}}
352
353def Base64.decode.go(
354 s: String, tok: B64.Tok, st: B64.D, pad: B64.Pad, acc: List<&2, U32>,
355 off: Nat, +alpha: B64.Alpha
356) -> Result<&2, &2, Base64.Error, B.Bytes>:
357 match s tok st pad:
358 case _ TokBad{c} _ _:
359 Fail{InvalidChar{off, c}}
360 case SNil{} TokVal{n} D0{} _:
361 Fail{InvalidLength{off}}
362 case SNil{} TokVal{n} D1{a} B64Raw{}:
363 B64.fin1.from(a, n, acc, off)
364 case SNil{} TokVal{_} D1{_} B64Pad{}:
365 Fail{InvalidLength{off}}
366 case SNil{} TokVal{n} D2{a, b} B64Raw{}:
367 B64.fin2.from(a, b, n, acc, off)
368 case SNil{} TokVal{_} D2{_, _} B64Pad{}:
369 Fail{InvalidLength{off}}
370 case SNil{} TokVal{n} D3{a, b, c} _:
371 Done{B.Bytes{List.reverse(&2, U32, B64.emit4(a, b, c, n, acc))}}
372 case SNil{} TokPad{} D2{a, b} B64Pad{}:
373 Fail{InvalidLength{off}}
374 case SNil{} TokPad{} D3{a, b, c} B64Pad{}:
375 B64.fin2.from(a, b, c, acc, off)
376 case SNil{} TokPad{} Eq1{a, b} B64Pad{}:
377 B64.fin1.from(a, b, acc, off)
378 case SNil{} TokPad{} PadEnd{} B64Pad{}:
379 Fail{InvalidLength{off}}
380 case SNil{} TokPad{} _ _:
381 Fail{InvalidLength{off}}
382 case SNil{} TokVal{_} Eq1{_, _} _:
383 Fail{InvalidLength{off}}
384 case SNil{} TokVal{_} PadEnd{} _:
385 Fail{InvalidLength{off}}
386 case SCon{+h, t} TokVal{n} D0{} p:
387 Base64.decode.go(t, B64.tok(h, alpha), D1{n}, p, acc, 1n+off, alpha)
388 case SCon{+h, t} TokVal{n} D1{a} p:
389 Base64.decode.go(t, B64.tok(h, alpha), D2{a, n}, p, acc, 1n+off, alpha)
390 case SCon{+h, t} TokVal{n} D2{a, b} p:
391 Base64.decode.go(t, B64.tok(h, alpha), D3{a, b, n}, p, acc, 1n+off, alpha)
392 case SCon{+h, t} TokVal{n} D3{a, b, c} p:
393 Base64.decode.go(
394 t, B64.tok(h, alpha), D0{}, p, B64.emit4(a, b, c, n, acc), 1n+off, alpha
395 )
396 case SCon{+h, t} TokPad{} D2{a, b} B64Pad{}:
397 Base64.decode.go(t, B64.tok(h, alpha), Eq1{a, b}, B64Pad{}, acc, 1n+off, alpha)
398 case SCon{+h, t} TokPad{} D3{a, b, c} B64Pad{}:
399 Base64.decode.go(
400 t, B64.tok(h, alpha), PadEnd{}, B64Pad{},
401 B64.emit2(a, b, c, acc), 1n+off, alpha
402 )
403 case SCon{+h, t} TokPad{} Eq1{a, b} B64Pad{}:
404 Base64.decode.go(
405 t, B64.tok(h, alpha), PadEnd{}, B64Pad{},
406 Con{B64.byte1(a, b), acc}, 1n+off, alpha
407 )
408 case SCon{_, _} TokPad{} D0{} _:
409 Fail{InvalidChar{off, '='}}
410 case SCon{_, _} TokPad{} D1{_} _:
411 Fail{InvalidChar{off, '='}}
412 case SCon{_, _} TokPad{} PadEnd{} _:
413 Fail{InvalidChar{off, '='}}
414 case SCon{_, _} TokVal{_} Eq1{_, _} _:
415 Fail{InvalidChar{off, '='}}
416 case SCon{_, _} TokVal{_} PadEnd{} _:
417 Fail{InvalidLength{off}}
418 case SCon{_, _} TokPad{} D2{_, _} B64Raw{}:
419 Fail{InvalidChar{off, '='}}
420 case SCon{_, _} TokPad{} D3{_, _, _} B64Raw{}:
421 Fail{InvalidChar{off, '='}}
422 case SCon{_, _} TokPad{} Eq1{_, _} B64Raw{}:
423 Fail{InvalidChar{off, '='}}
424
425def Base64.decode.start(
426 s: String, +alpha: B64.Alpha, +pad: B64.Pad
427) -> Result<&2, &2, Base64.Error, B.Bytes>:
428 match s:
429 case SNil{}:
430 Done{B.Bytes{Nil{}}}
431 case SCon{+h, t}:
432 Base64.decode.go(t, B64.tok(h, alpha), D0{}, pad, Nil{}, 0n, alpha)
433
434def Base64.decode(text: String) -> Result<&2, &2, Base64.Error, B.Bytes>:
435 Base64.decode.start(text, B64Std{}, B64Pad{})
436
437def Base64Url.decode.p(
438 text: String, padded: Bool
439) -> Result<&2, &2, Base64.Error, B.Bytes>:
440 match padded:
441 case True{}:
442 Base64.decode.start(text, B64Url{}, B64Pad{})
443 case False{}:
444 Base64.decode.start(text, B64Url{}, B64Raw{})
445
446def Base64Url.decode(
447 text: String, padded: Bool
448) -> Result<&2, &2, Base64.Error, B.Bytes>:
449 Base64Url.decode.p(text, padded)
450
451def Base64.encode_u32_list.from(
452 r: Result<&2, &2, B.Bytes.Error, B.Bytes>
453) -> Result<&2, &2, Base64.Error, String>:
454 match r:
455 case Fail{e}:
456 Fail{Base64.from_bytes_err(e)}
457 case Done{b}:
458 Done{Base64.encode(b)}
459
460def Base64.encode_u32_list(
461 values: List<&2, U32>
462) -> Result<&2, &2, Base64.Error, String>:
463 Base64.encode_u32_list.from(B.Bytes.from_u32_list(values))
464
465def Base64Url.encode_u32_list.from(
466 r: Result<&2, &2, B.Bytes.Error, B.Bytes>, padded: Bool
467) -> Result<&2, &2, Base64.Error, String>:
468 match r:
469 case Fail{e}:
470 Fail{Base64.from_bytes_err(e)}
471 case Done{b}:
472 Done{Base64Url.encode(b, padded)}
473
474def Base64Url.encode_u32_list(
475 values: List<&2, U32>, padded: Bool
476) -> Result<&2, &2, Base64.Error, String>:
477 Base64Url.encode_u32_list.from(B.Bytes.from_u32_list(values), padded)
478
479def Base64.decode_u32_list.from(
480 r: Result<&2, &2, Base64.Error, B.Bytes>
481) -> Result<&2, &2, Base64.Error, List<&2, U32>>:
482 match r:
483 case Fail{e}:
484 Fail{e}
485 case Done{b}:
486 Done{B.Bytes.to_u32_list(b)}
487
488def Base64.decode_u32_list(
489 text: String
490) -> Result<&2, &2, Base64.Error, List<&2, U32>>:
491 Base64.decode_u32_list.from(Base64.decode(text))
492
493def Base64Url.decode_u32_list(
494 text: String, padded: Bool
495) -> Result<&2, &2, Base64.Error, List<&2, U32>>:
496 Base64.decode_u32_list.from(Base64Url.decode(text, padded))