char/avril

(wip) why'd you have to go and make things so complicaaaaated?

git clone https://git.t4t.associates/char/avril

Charlotte Somport web extension to typescript44fe2c6

main
3.8 KiB120 linesraw
1import {
2  ArrayPrototypeJoin as join,
3  ArrayPrototypePush as push,
4  String,
5  StringPrototypeSlice as slice,
6  uncurryThis,
7} from "ext:core/primordials";
8import { createException } from "./events.js";
9import { array, required, string } from "./idl.js";
10
11const alphabet =
12  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
13const charCode = uncurryThis(String.prototype.charCodeAt);
14const fromCharCode = String.fromCharCode;
15function invalidCharacter(): Error {
16  return createException({
17    message: "The string contains characters outside the Latin1 range.",
18    name: "InvalidCharacterError",
19  }) as Error;
20}
21function sextet(code: number): number {
22  if (code >= 65 && code <= 90) return code - 65;
23  if (code >= 97 && code <= 122) return code - 71;
24  if (code >= 48 && code <= 57) return code + 4;
25  if (code === 43) return 62;
26  if (code === 47) return 63;
27  return -1;
28}
29
30const api = {
31  btoa(data: unknown): string {
32    required(arguments.length, 1);
33    data = string(data);
34    const output = array<string>();
35    let chunk = "";
36    for (let index = 0; index < (data as string).length; index += 3) {
37      const a = charCode(data as string, index);
38      const b = index + 1 < (data as string).length
39        ? charCode(data as string, index + 1)
40        : -1;
41      const c = index + 2 < (data as string).length
42        ? charCode(data as string, index + 2)
43        : -1;
44      if (a > 255 || b > 255 || c > 255) throw invalidCharacter();
45      chunk += alphabet[a >> 2];
46      chunk += alphabet[((a & 3) << 4) | (b < 0 ? 0 : b >> 4)];
47      chunk += b < 0
48        ? "=="
49        : c < 0
50        ? alphabet[(b & 15) << 2] + "="
51        : alphabet[((b & 15) << 2) | (c >> 6)] + alphabet[c & 63];
52      if (chunk.length >= 4096) {
53        push(output, chunk);
54        chunk = "";
55      }
56    }
57    if (chunk) push(output, chunk);
58    return join(output, "");
59  },
60
61  atob(data: unknown): string {
62    required(arguments.length, 1);
63    data = string(data);
64    const inputParts = array<string>();
65    for (let index = 0; index < (data as string).length; index++) {
66      const code = charCode(data as string, index);
67      if (
68        code === 9 || code === 10 || code === 12 || code === 13 || code === 32
69      ) {
70        continue;
71      }
72      push(inputParts, (data as string)[index]);
73    }
74    let input = join(inputParts, "");
75    const length = input.length;
76    if (
77      length % 4 === 0 && length >= 2 &&
78      charCode(input, length - 1) === 61 &&
79      charCode(input, length - 2) === 61
80    ) {
81      input = slice(input, 0, length - 2);
82    } else if (length % 4 === 0 && charCode(input, length - 1) === 61) {
83      input = slice(input, 0, length - 1);
84    }
85    const remainder = input.length % 4;
86    if (remainder === 1) throw invalidCharacter();
87    const values = array<number>();
88    for (let index = 0; index < input.length; index++) {
89      const value = sextet(charCode(input, index));
90      if (value < 0) throw invalidCharacter();
91      values[index] = value;
92    }
93    const output = array<string>();
94    let chunk = "";
95    let index = 0;
96    for (; index + 3 < values.length; index += 4) {
97      chunk += fromCharCode(
98        (values[index] << 2) | (values[index + 1] >> 4),
99        ((values[index + 1] & 15) << 4) | (values[index + 2] >> 2),
100        ((values[index + 2] & 3) << 6) | values[index + 3],
101      );
102      if (chunk.length >= 4096) {
103        push(output, chunk);
104        chunk = "";
105      }
106    }
107    if (remainder === 2) {
108      chunk += fromCharCode((values[index] << 2) | (values[index + 1] >> 4));
109    } else if (remainder === 3) {
110      chunk += fromCharCode(
111        (values[index] << 2) | (values[index + 1] >> 4),
112        ((values[index + 1] & 15) << 4) | (values[index + 2] >> 2),
113      );
114    }
115    if (chunk) push(output, chunk);
116    return join(output, "");
117  },
118};
119const { atob, btoa } = api;
120export { atob, btoa };