@aptre/v86 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/LICENSE.MIT +22 -0
- package/Readme.md +237 -0
- package/dist/v86.browser.js +26666 -0
- package/dist/v86.browser.js.map +7 -0
- package/dist/v86.js +26632 -0
- package/dist/v86.js.map +7 -0
- package/gen/generate_analyzer.ts +512 -0
- package/gen/generate_interpreter.ts +522 -0
- package/gen/generate_jit.ts +624 -0
- package/gen/rust_ast.ts +107 -0
- package/gen/util.ts +35 -0
- package/gen/x86_table.ts +1836 -0
- package/lib/9p.ts +1547 -0
- package/lib/filesystem.ts +1879 -0
- package/lib/marshall.ts +168 -0
- package/lib/softfloat/softfloat.c +32501 -0
- package/lib/zstd/zstddeclib.c +13520 -0
- package/package.json +75 -0
- package/src/acpi.ts +267 -0
- package/src/browser/dummy_screen.ts +106 -0
- package/src/browser/fake_network.ts +1771 -0
- package/src/browser/fetch_network.ts +361 -0
- package/src/browser/filestorage.ts +124 -0
- package/src/browser/inbrowser_network.ts +57 -0
- package/src/browser/keyboard.ts +564 -0
- package/src/browser/main.ts +3415 -0
- package/src/browser/mouse.ts +255 -0
- package/src/browser/network.ts +142 -0
- package/src/browser/print_stats.ts +336 -0
- package/src/browser/screen.ts +978 -0
- package/src/browser/serial.ts +316 -0
- package/src/browser/speaker.ts +1223 -0
- package/src/browser/starter.ts +1688 -0
- package/src/browser/wisp_network.ts +332 -0
- package/src/browser/worker_bus.ts +64 -0
- package/src/buffer.ts +652 -0
- package/src/bus.ts +78 -0
- package/src/const.ts +128 -0
- package/src/cpu.ts +2891 -0
- package/src/dma.ts +474 -0
- package/src/elf.ts +251 -0
- package/src/floppy.ts +1778 -0
- package/src/ide.ts +3455 -0
- package/src/io.ts +504 -0
- package/src/iso9660.ts +317 -0
- package/src/kernel.ts +250 -0
- package/src/lib.ts +645 -0
- package/src/log.ts +149 -0
- package/src/main.ts +199 -0
- package/src/ne2k.ts +1589 -0
- package/src/pci.ts +815 -0
- package/src/pit.ts +406 -0
- package/src/ps2.ts +820 -0
- package/src/rtc.ts +537 -0
- package/src/rust/analysis.rs +101 -0
- package/src/rust/codegen.rs +2660 -0
- package/src/rust/config.rs +3 -0
- package/src/rust/control_flow.rs +425 -0
- package/src/rust/cpu/apic.rs +658 -0
- package/src/rust/cpu/arith.rs +1207 -0
- package/src/rust/cpu/call_indirect.rs +2 -0
- package/src/rust/cpu/cpu.rs +4501 -0
- package/src/rust/cpu/fpu.rs +923 -0
- package/src/rust/cpu/global_pointers.rs +112 -0
- package/src/rust/cpu/instructions.rs +2486 -0
- package/src/rust/cpu/instructions_0f.rs +5261 -0
- package/src/rust/cpu/ioapic.rs +316 -0
- package/src/rust/cpu/memory.rs +351 -0
- package/src/rust/cpu/misc_instr.rs +613 -0
- package/src/rust/cpu/mod.rs +16 -0
- package/src/rust/cpu/modrm.rs +133 -0
- package/src/rust/cpu/pic.rs +402 -0
- package/src/rust/cpu/sse_instr.rs +361 -0
- package/src/rust/cpu/string.rs +701 -0
- package/src/rust/cpu/vga.rs +175 -0
- package/src/rust/cpu_context.rs +69 -0
- package/src/rust/dbg.rs +98 -0
- package/src/rust/gen/analyzer.rs +3807 -0
- package/src/rust/gen/analyzer0f.rs +3992 -0
- package/src/rust/gen/interpreter.rs +4447 -0
- package/src/rust/gen/interpreter0f.rs +5404 -0
- package/src/rust/gen/jit.rs +5080 -0
- package/src/rust/gen/jit0f.rs +5547 -0
- package/src/rust/gen/mod.rs +14 -0
- package/src/rust/jit.rs +2443 -0
- package/src/rust/jit_instructions.rs +7881 -0
- package/src/rust/js_api.rs +6 -0
- package/src/rust/leb.rs +46 -0
- package/src/rust/lib.rs +29 -0
- package/src/rust/modrm.rs +330 -0
- package/src/rust/opstats.rs +249 -0
- package/src/rust/page.rs +15 -0
- package/src/rust/paging.rs +25 -0
- package/src/rust/prefix.rs +15 -0
- package/src/rust/profiler.rs +155 -0
- package/src/rust/regs.rs +38 -0
- package/src/rust/softfloat.rs +286 -0
- package/src/rust/state_flags.rs +27 -0
- package/src/rust/wasmgen/mod.rs +2 -0
- package/src/rust/wasmgen/wasm_builder.rs +1047 -0
- package/src/rust/wasmgen/wasm_opcodes.rs +221 -0
- package/src/rust/zstd.rs +105 -0
- package/src/sb16.ts +1928 -0
- package/src/state.ts +359 -0
- package/src/uart.ts +472 -0
- package/src/vga.ts +2791 -0
- package/src/virtio.ts +1756 -0
- package/src/virtio_balloon.ts +273 -0
- package/src/virtio_console.ts +372 -0
- package/src/virtio_net.ts +326 -0
package/src/lib.ts
ADDED
|
@@ -0,0 +1,645 @@
|
|
|
1
|
+
declare let DEBUG: boolean
|
|
2
|
+
|
|
3
|
+
import { dbg_assert } from './log.js'
|
|
4
|
+
|
|
5
|
+
// pad string with spaces on the right
|
|
6
|
+
export function pads(
|
|
7
|
+
str: string | number | undefined | null,
|
|
8
|
+
len: number,
|
|
9
|
+
): string {
|
|
10
|
+
const s = str || str === 0 ? str + '' : ''
|
|
11
|
+
return s.padEnd(len, ' ')
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// pad string with zeros on the left
|
|
15
|
+
export function pad0(
|
|
16
|
+
str: string | number | undefined | null,
|
|
17
|
+
len: number,
|
|
18
|
+
): string {
|
|
19
|
+
const s = str || str === 0 ? str + '' : ''
|
|
20
|
+
return s.padStart(len, '0')
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const view = function (
|
|
24
|
+
constructor: any,
|
|
25
|
+
memory: { buffer: ArrayBuffer },
|
|
26
|
+
offset: number,
|
|
27
|
+
length: number,
|
|
28
|
+
): any {
|
|
29
|
+
dbg_assert(offset >= 0)
|
|
30
|
+
return new Proxy(
|
|
31
|
+
{},
|
|
32
|
+
{
|
|
33
|
+
get: function (_target, property) {
|
|
34
|
+
const b = new constructor(memory.buffer, offset, length)
|
|
35
|
+
const x = b[property]
|
|
36
|
+
if (typeof x === 'function') {
|
|
37
|
+
return x.bind(b)
|
|
38
|
+
}
|
|
39
|
+
dbg_assert(
|
|
40
|
+
/^\d+$/.test(String(property)) ||
|
|
41
|
+
property === 'buffer' ||
|
|
42
|
+
property === 'length' ||
|
|
43
|
+
property === 'BYTES_PER_ELEMENT' ||
|
|
44
|
+
property === 'byteOffset',
|
|
45
|
+
)
|
|
46
|
+
return x
|
|
47
|
+
},
|
|
48
|
+
set: function (_target, property, value) {
|
|
49
|
+
dbg_assert(/^\d+$/.test(String(property)))
|
|
50
|
+
new constructor(memory.buffer, offset, length)[property] = value
|
|
51
|
+
return true
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function h(n: number, len?: number): string {
|
|
58
|
+
let str: string
|
|
59
|
+
if (!n) {
|
|
60
|
+
str = ''
|
|
61
|
+
} else {
|
|
62
|
+
str = n.toString(16)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return '0x' + pad0(str.toUpperCase(), len || 1)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function hex_dump(buffer: Uint8Array | number[]): string {
|
|
69
|
+
function hex(n: number, len: number): string {
|
|
70
|
+
return pad0(n.toString(16).toUpperCase(), len)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const result: string[] = []
|
|
74
|
+
let offset = 0
|
|
75
|
+
|
|
76
|
+
for (; offset + 15 < buffer.length; offset += 16) {
|
|
77
|
+
let line = hex(offset, 5) + ' '
|
|
78
|
+
|
|
79
|
+
for (let j = 0; j < 0x10; j++) {
|
|
80
|
+
line += hex(buffer[offset + j], 2) + ' '
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
line += ' '
|
|
84
|
+
|
|
85
|
+
for (let j = 0; j < 0x10; j++) {
|
|
86
|
+
const x = buffer[offset + j]
|
|
87
|
+
line +=
|
|
88
|
+
x >= 33 && x !== 34 && x !== 92 && x <= 126
|
|
89
|
+
? String.fromCharCode(x)
|
|
90
|
+
: '.'
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
result.push(line)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
let line = hex(offset, 5) + ' '
|
|
97
|
+
|
|
98
|
+
for (; offset < buffer.length; offset++) {
|
|
99
|
+
line += hex(buffer[offset], 2) + ' '
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const remainder = offset & 0xf
|
|
103
|
+
line += ' '.repeat(0x10 - remainder)
|
|
104
|
+
line += ' '
|
|
105
|
+
|
|
106
|
+
for (let j = 0; j < remainder; j++) {
|
|
107
|
+
const x = buffer[offset + j]
|
|
108
|
+
line +=
|
|
109
|
+
x >= 33 && x !== 34 && x !== 92 && x <= 126
|
|
110
|
+
? String.fromCharCode(x)
|
|
111
|
+
: '.'
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
result.push(line)
|
|
115
|
+
|
|
116
|
+
return '\n' + result.join('\n') + '\n'
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/* global require */
|
|
120
|
+
export let get_rand_int: () => number
|
|
121
|
+
if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
|
|
122
|
+
const rand_data = new Int32Array(1)
|
|
123
|
+
|
|
124
|
+
get_rand_int = function () {
|
|
125
|
+
crypto.getRandomValues(rand_data)
|
|
126
|
+
return rand_data[0]
|
|
127
|
+
}
|
|
128
|
+
} else if (typeof require !== 'undefined') {
|
|
129
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
130
|
+
const nodeCrypto = require('crypto')
|
|
131
|
+
|
|
132
|
+
get_rand_int = function () {
|
|
133
|
+
return nodeCrypto.randomBytes(4).readInt32LE(0)
|
|
134
|
+
}
|
|
135
|
+
} else if (typeof process !== 'undefined') {
|
|
136
|
+
import('node:' + 'crypto').then((nodeCrypto) => {
|
|
137
|
+
get_rand_int = function () {
|
|
138
|
+
return nodeCrypto['randomBytes'](4).readInt32LE(0)
|
|
139
|
+
}
|
|
140
|
+
})
|
|
141
|
+
} else {
|
|
142
|
+
dbg_assert(false, 'Unsupported platform: No cryptographic random values')
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export let int_log2: (x: number) => number
|
|
146
|
+
|
|
147
|
+
if (
|
|
148
|
+
typeof Math.clz32 === 'function' &&
|
|
149
|
+
Math.clz32(0) === 32 &&
|
|
150
|
+
Math.clz32(0x12345) === 15 &&
|
|
151
|
+
Math.clz32(-1) === 0
|
|
152
|
+
) {
|
|
153
|
+
int_log2 = function (x: number): number {
|
|
154
|
+
dbg_assert(x > 0)
|
|
155
|
+
|
|
156
|
+
return 31 - Math.clz32(x)
|
|
157
|
+
}
|
|
158
|
+
} else {
|
|
159
|
+
const int_log2_table = new Int8Array(256)
|
|
160
|
+
|
|
161
|
+
for (let i = 0, b = -2; i < 256; i++) {
|
|
162
|
+
if (!(i & (i - 1))) b++
|
|
163
|
+
|
|
164
|
+
int_log2_table[i] = b
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
int_log2 = function (x: number): number {
|
|
168
|
+
x >>>= 0
|
|
169
|
+
dbg_assert(x > 0)
|
|
170
|
+
|
|
171
|
+
// http://jsperf.com/integer-log2/6
|
|
172
|
+
const tt = x >>> 16
|
|
173
|
+
|
|
174
|
+
if (tt) {
|
|
175
|
+
const t = tt >>> 8
|
|
176
|
+
if (t) {
|
|
177
|
+
return 24 + int_log2_table[t]
|
|
178
|
+
} else {
|
|
179
|
+
return 16 + int_log2_table[tt]
|
|
180
|
+
}
|
|
181
|
+
} else {
|
|
182
|
+
const t = x >>> 8
|
|
183
|
+
if (t) {
|
|
184
|
+
return 8 + int_log2_table[t]
|
|
185
|
+
} else {
|
|
186
|
+
return int_log2_table[x]
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export const round_up_to_next_power_of_2 = function (x: number): number {
|
|
193
|
+
dbg_assert(x >= 0)
|
|
194
|
+
return x <= 1 ? 1 : 1 << (1 + int_log2(x - 1))
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (typeof DEBUG !== 'undefined' && DEBUG) {
|
|
198
|
+
dbg_assert(int_log2(1) === 0)
|
|
199
|
+
dbg_assert(int_log2(2) === 1)
|
|
200
|
+
dbg_assert(int_log2(7) === 2)
|
|
201
|
+
dbg_assert(int_log2(8) === 3)
|
|
202
|
+
dbg_assert(int_log2(123456789) === 26)
|
|
203
|
+
|
|
204
|
+
dbg_assert(round_up_to_next_power_of_2(0) === 1)
|
|
205
|
+
dbg_assert(round_up_to_next_power_of_2(1) === 1)
|
|
206
|
+
dbg_assert(round_up_to_next_power_of_2(2) === 2)
|
|
207
|
+
dbg_assert(round_up_to_next_power_of_2(7) === 8)
|
|
208
|
+
dbg_assert(round_up_to_next_power_of_2(8) === 8)
|
|
209
|
+
dbg_assert(round_up_to_next_power_of_2(123456789) === 134217728)
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export class ByteQueue {
|
|
213
|
+
length: number = 0
|
|
214
|
+
private data: Uint8Array
|
|
215
|
+
private start: number = 0
|
|
216
|
+
private end: number = 0
|
|
217
|
+
private size: number
|
|
218
|
+
|
|
219
|
+
constructor(size: number) {
|
|
220
|
+
this.size = size
|
|
221
|
+
this.data = new Uint8Array(size)
|
|
222
|
+
|
|
223
|
+
dbg_assert((size & (size - 1)) === 0)
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
push(item: number): void {
|
|
227
|
+
if (this.length === this.size) {
|
|
228
|
+
// intentional overwrite
|
|
229
|
+
} else {
|
|
230
|
+
this.length++
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
this.data[this.end] = item
|
|
234
|
+
this.end = (this.end + 1) & (this.size - 1)
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
shift(): number {
|
|
238
|
+
if (!this.length) {
|
|
239
|
+
return -1
|
|
240
|
+
} else {
|
|
241
|
+
const item = this.data[this.start]
|
|
242
|
+
|
|
243
|
+
this.start = (this.start + 1) & (this.size - 1)
|
|
244
|
+
this.length--
|
|
245
|
+
|
|
246
|
+
return item
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
peek(): number {
|
|
251
|
+
if (!this.length) {
|
|
252
|
+
return -1
|
|
253
|
+
} else {
|
|
254
|
+
return this.data[this.start]
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
clear(): void {
|
|
259
|
+
this.start = 0
|
|
260
|
+
this.end = 0
|
|
261
|
+
this.length = 0
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
export class FloatQueue {
|
|
266
|
+
size: number
|
|
267
|
+
data: Float32Array
|
|
268
|
+
start: number = 0
|
|
269
|
+
end: number = 0
|
|
270
|
+
length: number = 0
|
|
271
|
+
|
|
272
|
+
constructor(size: number) {
|
|
273
|
+
this.size = size
|
|
274
|
+
this.data = new Float32Array(size)
|
|
275
|
+
|
|
276
|
+
dbg_assert((size & (size - 1)) === 0)
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
push(item: number): void {
|
|
280
|
+
if (this.length === this.size) {
|
|
281
|
+
// intentional overwrite
|
|
282
|
+
this.start = (this.start + 1) & (this.size - 1)
|
|
283
|
+
} else {
|
|
284
|
+
this.length++
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
this.data[this.end] = item
|
|
288
|
+
this.end = (this.end + 1) & (this.size - 1)
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
shift(): number | undefined {
|
|
292
|
+
if (!this.length) {
|
|
293
|
+
return undefined
|
|
294
|
+
} else {
|
|
295
|
+
const item = this.data[this.start]
|
|
296
|
+
|
|
297
|
+
this.start = (this.start + 1) & (this.size - 1)
|
|
298
|
+
this.length--
|
|
299
|
+
|
|
300
|
+
return item
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
shift_block(count: number): Float32Array {
|
|
305
|
+
const slice = new Float32Array(count)
|
|
306
|
+
|
|
307
|
+
if (count > this.length) {
|
|
308
|
+
count = this.length
|
|
309
|
+
}
|
|
310
|
+
let slice_end = this.start + count
|
|
311
|
+
|
|
312
|
+
const partial = this.data.subarray(this.start, slice_end)
|
|
313
|
+
|
|
314
|
+
slice.set(partial)
|
|
315
|
+
if (slice_end >= this.size) {
|
|
316
|
+
slice_end -= this.size
|
|
317
|
+
slice.set(this.data.subarray(0, slice_end), partial.length)
|
|
318
|
+
}
|
|
319
|
+
this.start = slice_end
|
|
320
|
+
|
|
321
|
+
this.length -= count
|
|
322
|
+
|
|
323
|
+
return slice
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
peek(): number | undefined {
|
|
327
|
+
if (!this.length) {
|
|
328
|
+
return undefined
|
|
329
|
+
} else {
|
|
330
|
+
return this.data[this.start]
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
clear(): void {
|
|
335
|
+
this.start = 0
|
|
336
|
+
this.end = 0
|
|
337
|
+
this.length = 0
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
export function dump_file(ab: BlobPart | BlobPart[], name: string): void {
|
|
342
|
+
if (!Array.isArray(ab)) {
|
|
343
|
+
ab = [ab]
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
const blob = new Blob(ab)
|
|
347
|
+
download(blob, name)
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
export function download(file_or_blob: Blob | File, name: string): void {
|
|
351
|
+
const a = document.createElement('a')
|
|
352
|
+
a['download'] = name
|
|
353
|
+
a.href = window.URL.createObjectURL(file_or_blob)
|
|
354
|
+
a.dataset['downloadurl'] = [
|
|
355
|
+
'application/octet-stream',
|
|
356
|
+
a['download'],
|
|
357
|
+
a.href,
|
|
358
|
+
].join(':')
|
|
359
|
+
|
|
360
|
+
if (document.createEvent) {
|
|
361
|
+
const ev = document.createEvent('MouseEvent')
|
|
362
|
+
ev.initMouseEvent(
|
|
363
|
+
'click',
|
|
364
|
+
true,
|
|
365
|
+
true,
|
|
366
|
+
window,
|
|
367
|
+
0,
|
|
368
|
+
0,
|
|
369
|
+
0,
|
|
370
|
+
0,
|
|
371
|
+
0,
|
|
372
|
+
false,
|
|
373
|
+
false,
|
|
374
|
+
false,
|
|
375
|
+
false,
|
|
376
|
+
0,
|
|
377
|
+
null,
|
|
378
|
+
)
|
|
379
|
+
a.dispatchEvent(ev)
|
|
380
|
+
} else {
|
|
381
|
+
a.click()
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
window.URL.revokeObjectURL(a.href)
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
export class Bitmap {
|
|
388
|
+
view: Uint8Array
|
|
389
|
+
|
|
390
|
+
constructor(length_or_buffer: number | ArrayBuffer) {
|
|
391
|
+
if (typeof length_or_buffer === 'number') {
|
|
392
|
+
this.view = new Uint8Array((length_or_buffer + 7) >> 3)
|
|
393
|
+
} else if (length_or_buffer instanceof ArrayBuffer) {
|
|
394
|
+
this.view = new Uint8Array(length_or_buffer)
|
|
395
|
+
} else {
|
|
396
|
+
dbg_assert(false, 'Bitmap: Invalid argument')
|
|
397
|
+
this.view = new Uint8Array(0)
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
set(index: number, value: number): void {
|
|
402
|
+
const bit_index = index & 7
|
|
403
|
+
const byte_index = index >> 3
|
|
404
|
+
const bit_mask = 1 << bit_index
|
|
405
|
+
|
|
406
|
+
this.view[byte_index] = value
|
|
407
|
+
? this.view[byte_index] | bit_mask
|
|
408
|
+
: this.view[byte_index] & ~bit_mask
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
get(index: number): number {
|
|
412
|
+
const bit_index = index & 7
|
|
413
|
+
const byte_index = index >> 3
|
|
414
|
+
|
|
415
|
+
return (this.view[byte_index] >> bit_index) & 1
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
get_buffer(): ArrayBufferLike {
|
|
419
|
+
return this.view.buffer
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
export interface LoadFileOptions {
|
|
424
|
+
done?: (result: any, http?: XMLHttpRequest) => void
|
|
425
|
+
progress?: (e: ProgressEvent) => void
|
|
426
|
+
as_json?: boolean
|
|
427
|
+
method?: string
|
|
428
|
+
headers?: Record<string, string>
|
|
429
|
+
range?: { start: number; length: number }
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
export let load_file: (
|
|
433
|
+
filename: string,
|
|
434
|
+
options: LoadFileOptions,
|
|
435
|
+
n_tries?: number,
|
|
436
|
+
) => Promise<void>
|
|
437
|
+
export let get_file_size: (path: string) => Promise<number>
|
|
438
|
+
|
|
439
|
+
if (
|
|
440
|
+
typeof XMLHttpRequest === 'undefined' ||
|
|
441
|
+
(typeof process !== 'undefined' &&
|
|
442
|
+
process.versions &&
|
|
443
|
+
process.versions.node)
|
|
444
|
+
) {
|
|
445
|
+
let fs: any
|
|
446
|
+
|
|
447
|
+
load_file = async function (
|
|
448
|
+
filename: string,
|
|
449
|
+
options: LoadFileOptions,
|
|
450
|
+
_n_tries?: number,
|
|
451
|
+
) {
|
|
452
|
+
if (!fs) {
|
|
453
|
+
// string concat to work around closure compiler 'Invalid module path "node:fs/promises" for resolution mode'
|
|
454
|
+
fs = await import('node:' + 'fs/promises')
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
if (options.range) {
|
|
458
|
+
dbg_assert(!options.as_json)
|
|
459
|
+
|
|
460
|
+
const fd = await fs['open'](filename, 'r')
|
|
461
|
+
|
|
462
|
+
const length = options.range.length
|
|
463
|
+
const buffer = Buffer.allocUnsafe(length)
|
|
464
|
+
|
|
465
|
+
try {
|
|
466
|
+
const result = await fd['read']({
|
|
467
|
+
buffer,
|
|
468
|
+
position: options.range.start,
|
|
469
|
+
})
|
|
470
|
+
dbg_assert(result.bytesRead === length)
|
|
471
|
+
} finally {
|
|
472
|
+
await fd['close']()
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
if (options.done) {
|
|
476
|
+
options.done(new Uint8Array(buffer))
|
|
477
|
+
}
|
|
478
|
+
} else {
|
|
479
|
+
const o = {
|
|
480
|
+
encoding: options.as_json ? 'utf-8' : null,
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
const data = await fs['readFile'](filename, o)
|
|
484
|
+
if (options.as_json) {
|
|
485
|
+
options.done!(JSON.parse(data))
|
|
486
|
+
} else {
|
|
487
|
+
options.done!(new Uint8Array(data).buffer)
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
get_file_size = async function (path: string) {
|
|
493
|
+
if (!fs) {
|
|
494
|
+
// string concat to work around closure compiler 'Invalid module path "node:fs/promises" for resolution mode'
|
|
495
|
+
fs = await import('node:' + 'fs/promises')
|
|
496
|
+
}
|
|
497
|
+
const stat = await fs['stat'](path)
|
|
498
|
+
return stat.size
|
|
499
|
+
}
|
|
500
|
+
} else {
|
|
501
|
+
load_file = async function (
|
|
502
|
+
filename: string,
|
|
503
|
+
options: LoadFileOptions,
|
|
504
|
+
n_tries?: number,
|
|
505
|
+
) {
|
|
506
|
+
const http = new XMLHttpRequest()
|
|
507
|
+
|
|
508
|
+
http.open(options.method || 'get', filename, true)
|
|
509
|
+
|
|
510
|
+
if (options.as_json) {
|
|
511
|
+
http.responseType = 'json'
|
|
512
|
+
} else {
|
|
513
|
+
http.responseType = 'arraybuffer'
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
if (options.headers) {
|
|
517
|
+
const header_names = Object.keys(options.headers)
|
|
518
|
+
|
|
519
|
+
for (let i = 0; i < header_names.length; i++) {
|
|
520
|
+
const name = header_names[i]
|
|
521
|
+
http.setRequestHeader(name, options.headers[name])
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
if (options.range) {
|
|
526
|
+
const start = options.range.start
|
|
527
|
+
const end = start + options.range.length - 1
|
|
528
|
+
http.setRequestHeader('Range', 'bytes=' + start + '-' + end)
|
|
529
|
+
http.setRequestHeader('X-Accept-Encoding', 'identity')
|
|
530
|
+
|
|
531
|
+
// Abort if server responds with complete file in response to range
|
|
532
|
+
// request, to prevent downloading large files from broken http servers
|
|
533
|
+
http.onreadystatechange = function () {
|
|
534
|
+
if (http.status === 200) {
|
|
535
|
+
console.error(
|
|
536
|
+
'Server sent full file in response to ranged request, aborting',
|
|
537
|
+
{ filename },
|
|
538
|
+
)
|
|
539
|
+
http.abort()
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
http.onload = function (_e) {
|
|
545
|
+
if (http.readyState === 4) {
|
|
546
|
+
if (http.status !== 200 && http.status !== 206) {
|
|
547
|
+
console.error(
|
|
548
|
+
'Loading the image ' + filename + ' failed (status %d)',
|
|
549
|
+
http.status,
|
|
550
|
+
)
|
|
551
|
+
if (http.status >= 500 && http.status < 600) {
|
|
552
|
+
retry()
|
|
553
|
+
}
|
|
554
|
+
} else if (http.response) {
|
|
555
|
+
if (options.range) {
|
|
556
|
+
const enc = http.getResponseHeader('Content-Encoding')
|
|
557
|
+
if (enc && enc !== 'identity') {
|
|
558
|
+
console.error(
|
|
559
|
+
'Server sent Content-Encoding in response to ranged request',
|
|
560
|
+
{ filename, enc },
|
|
561
|
+
)
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
if (options.done) {
|
|
565
|
+
options.done(http.response, http)
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
http.onerror = function (e) {
|
|
572
|
+
console.error('Loading the image ' + filename + ' failed', e)
|
|
573
|
+
retry()
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
if (options.progress) {
|
|
577
|
+
http.onprogress = function (e) {
|
|
578
|
+
options.progress!(e)
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
http.send(null)
|
|
583
|
+
|
|
584
|
+
function retry() {
|
|
585
|
+
const number_of_tries = n_tries || 0
|
|
586
|
+
const timeout = [1, 1, 2, 3, 5, 8, 13, 21][number_of_tries] || 34
|
|
587
|
+
setTimeout(() => {
|
|
588
|
+
load_file(filename, options, number_of_tries + 1)
|
|
589
|
+
}, 1000 * timeout)
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
get_file_size = async function (url: string) {
|
|
594
|
+
return new Promise((resolve, reject) => {
|
|
595
|
+
load_file(url, {
|
|
596
|
+
done: (_buffer, http) => {
|
|
597
|
+
const header =
|
|
598
|
+
http!.getResponseHeader('Content-Range') || ''
|
|
599
|
+
const match = header.match(/\/(\d+)\s*$/)
|
|
600
|
+
|
|
601
|
+
if (match) {
|
|
602
|
+
resolve(+match[1])
|
|
603
|
+
} else {
|
|
604
|
+
const error = new Error(
|
|
605
|
+
'`Range: bytes=...` header not supported (Got `' +
|
|
606
|
+
header +
|
|
607
|
+
'`)',
|
|
608
|
+
)
|
|
609
|
+
reject(error)
|
|
610
|
+
}
|
|
611
|
+
},
|
|
612
|
+
headers: {
|
|
613
|
+
Range: 'bytes=0-0',
|
|
614
|
+
'X-Accept-Encoding': 'identity',
|
|
615
|
+
},
|
|
616
|
+
})
|
|
617
|
+
})
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
// Reads len characters at offset from Memory object mem as a JS string
|
|
622
|
+
export function read_sized_string_from_mem(
|
|
623
|
+
mem: { buffer: ArrayBuffer },
|
|
624
|
+
offset: number,
|
|
625
|
+
len: number,
|
|
626
|
+
): string {
|
|
627
|
+
offset >>>= 0
|
|
628
|
+
len >>>= 0
|
|
629
|
+
return String.fromCharCode(...new Uint8Array(mem.buffer, offset, len))
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
const CHARMAPS: Record<string, string> = {
|
|
633
|
+
cp437: ' \u263A\u263B\u2665\u2666\u2663\u2660\u2022\u25D8\u25CB\u25D9\u2642\u2640\u266A\u266B\u263C\u25BA\u25C4\u2195\u203C\u00B6\u00A7\u25AC\u21A8\u2191\u2193\u2192\u2190\u221F\u2194\u25B2\u25BC !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u2302\u00C7\u00FC\u00E9\u00E2\u00E4\u00E0\u00E5\u00E7\u00EA\u00EB\u00E8\u00EF\u00EE\u00EC\u00C4\u00C5\u00C9\u00E6\u00C6\u00F4\u00F6\u00F2\u00FB\u00F9\u00FF\u00D6\u00DC\u00A2\u00A3\u00A5\u20A7\u0192\u00E1\u00ED\u00F3\u00FA\u00F1\u00D1\u00AA\u00BA\u00BF\u2310\u00AC\u00BD\u00BC\u00A1\u00AB\u00BB\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255D\u255C\u255B\u2510\u2514\u2534\u252C\u251C\u2500\u253C\u255E\u255F\u255A\u2554\u2569\u2566\u2560\u2550\u256C\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256B\u256A\u2518\u250C\u2588\u2584\u258C\u2590\u2580\u03B1\u00DF\u0393\u03C0\u03A3\u03C3\u00B5\u03C4\u03A6\u0398\u03A9\u03B4\u221E\u03C6\u03B5\u2229\u2261\u00B1\u2265\u2264\u2320\u2321\u00F7\u2248\u00B0\u2219\u00B7\u221A\u207F\u00B2\u25A0 ',
|
|
634
|
+
cp858: '\u00C7\u00FC\u00E9\u00E2\u00E4\u00E0\u00E5\u00E7\u00EA\u00EB\u00E8\u00EF\u00EE\u00EC\u00C4\u00C5\u00C9\u00E6\u00C6\u00F4\u00F6\u00F2\u00FB\u00F9\u00FF\u00D6\u00DC\u00F8\u00A3\u00D8\u00D7\u0192\u00E1\u00ED\u00F3\u00FA\u00F1\u00D1\u00AA\u00BA\u00BF\u00AE\u00AC\u00BD\u00BC\u00A1\u00AB\u00BB\u2591\u2592\u2593\u2502\u2524\u00C1\u00C2\u00C0\u00A9\u2563\u2551\u2557\u255D\u00A2\u00A5\u2510\u2514\u2534\u252C\u251C\u2500\u253C\u00E3\u00C3\u255A\u2554\u2569\u2566\u2560\u2550\u256C\u00A4\u00F0\u00D0\u00CA\u00CB\u00C8\u20AC\u00CD\u00CE\u00CF\u2518\u250C\u2588\u2584\u00A6\u00CC\u2580\u00D3\u00DF\u00D4\u00D2\u00F5\u00D5\u00B5\u00FE\u00DE\u00DA\u00DB\u00D9\u00FD\u00DD\u00AF\u00B4\u00AD\u00B1\u2017\u00BE\u00B6\u00A7\u00F7\u00B8\u00B0\u00A8\u00B7\u00B9\u00B3\u00B2\u25A0 ',
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
CHARMAPS.cp858 = CHARMAPS.cp437.slice(0, 128) + CHARMAPS.cp858
|
|
638
|
+
CHARMAPS.ascii = CHARMAPS.cp437
|
|
639
|
+
.split('')
|
|
640
|
+
.map((c, i) => (i > 31 && i < 128 ? c : '.'))
|
|
641
|
+
.join('')
|
|
642
|
+
|
|
643
|
+
export function get_charmap(encoding: string): string {
|
|
644
|
+
return encoding && CHARMAPS[encoding] ? CHARMAPS[encoding] : CHARMAPS.cp437
|
|
645
|
+
}
|