/
deno-dom-native.ts
108 lines (89 loc) · 3.01 KB
/
deno-dom-native.ts
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
import { Plug } from "https://deno.land/x/plug/mod.ts";
import { register } from "./src/parser.ts";
const nativeEnv = "DENO_DOM_PLUGIN";
let denoNativePluginPath: string | undefined;
// Try to read the environment
try {
denoNativePluginPath = Deno.env.get(nativeEnv);
} catch {}
// These types are only to deal with `as const` `readonly` shenanigans
type DeepWriteable<T> = { -readonly [P in keyof T]: DeepWriteable<T[P]> };
const _symbols = {
deno_dom_usize_len: { parameters: [], result: "usize" },
deno_dom_parse_sync: {
parameters: ["buffer", "usize", "buffer"],
result: "void",
},
deno_dom_parse_frag_sync: {
parameters: ["buffer", "usize", "buffer"],
result: "void",
},
deno_dom_is_big_endian: { parameters: [], result: "u32" },
deno_dom_copy_buf: { parameters: ["buffer", "buffer"], result: "void" },
} as const;
const symbols = _symbols as DeepWriteable<typeof _symbols>;
let dylib: Deno.DynamicLibrary<typeof symbols>;
if (denoNativePluginPath) {
// Load the plugin locally
dylib = Deno.dlopen(denoNativePluginPath, symbols);
} else {
const host = `${Deno.build.os}-${Deno.build.arch}` as const;
let name = "";
switch (host) {
case "linux-x86_64":
case "darwin-x86_64":
case "windows-x86_64":
name = "plugin";
break;
case "linux-aarch64":
name = "plugin-linux-aarch64";
break;
default:
console.error(`deno-dom-native: host ${host} has no supported backend`);
Deno.exit(1);
}
// Download the plugin
dylib = await Plug.prepare({
name,
url:
"https://github.com/b-fuze/deno-dom/releases/download/v0.1.35-alpha-artifacts/",
}, symbols);
}
const utf8Encoder = new TextEncoder();
const utf8Decoder = new TextDecoder();
const usizeBytes = Number(dylib.symbols.deno_dom_usize_len());
const isBigEndian = Boolean(dylib.symbols.deno_dom_is_big_endian() as number);
const dylibParseSync = dylib.symbols.deno_dom_parse_sync.bind(dylib.symbols);
const dylibParseFragSync = dylib.symbols.deno_dom_parse_frag_sync.bind(
dylib.symbols,
);
// Reused for each invocation. Not thread safe, but JS isn't multithreaded
// anyways.
const returnBufSizeLenRaw = new ArrayBuffer(usizeBytes * 2);
const returnBufSizeLen = new Uint8Array(returnBufSizeLenRaw);
function genericParse(
parser: (
srcBuf: Uint8Array,
srcLength: number,
returnBuf: Uint8Array,
) => void,
srcHtml: string,
): string {
const encodedHtml = utf8Encoder.encode(srcHtml);
parser(encodedHtml, encodedHtml.length, returnBufSizeLen);
const outBufSize = Number(
new DataView(returnBufSizeLenRaw).getBigUint64(0, !isBigEndian),
);
const outBuf = new Uint8Array(outBufSize);
dylib.symbols.deno_dom_copy_buf(returnBufSizeLen.slice(usizeBytes), outBuf);
return utf8Decoder.decode(outBuf);
}
function parse(html: string): string {
return genericParse(dylibParseSync, html);
}
function parseFrag(html: string): string {
return genericParse(dylibParseFragSync, html);
}
// Register parse function
register(parse, parseFrag);
export * from "./src/api.ts";