7
if (typeof XMLHttpRequest === "undefined") {
8
v86util.load_file = load_file_nodejs;
10
v86util.load_file = load_file;
13
v86util.AsyncXHRBuffer = AsyncXHRBuffer;
14
v86util.AsyncXHRPartfileBuffer = AsyncXHRPartfileBuffer;
15
v86util.AsyncFileBuffer = AsyncFileBuffer;
16
v86util.SyncFileBuffer = SyncFileBuffer;
19
v86util.read_sized_string_from_mem = function read_sized_string_from_mem(mem, offset, len) {
22
return String.fromCharCode(...new Uint8Array(mem.buffer, offset, len));
30
function load_file(filename, options, n_tries) {
31
var http = new XMLHttpRequest();
33
http.open(options.method || "get", filename, true);
35
if (options.as_json) {
36
http.responseType = "json";
38
http.responseType = "arraybuffer";
41
if (options.headers) {
42
var header_names = Object.keys(options.headers);
44
for (var i = 0; i < header_names.length; i++) {
45
var name = header_names[i];
46
http.setRequestHeader(name, options.headers[name]);
51
let start = options.range.start;
52
let end = start + options.range.length - 1;
53
http.setRequestHeader("Range", "bytes=" + start + "-" + end);
57
http.onreadystatechange = function() {
58
if (http.status === 200) {
64
http.onload = function(e) {
65
if (http.readyState === 4) {
66
if (http.status !== 200 && http.status !== 206) {
67
console.error("Loading the image " + filename + " failed (status %d)", http.status);
68
if (http.status >= 500 && http.status < 600) {
71
} else if (http.response) {
72
options.done && options.done(http.response, http);
77
http.onerror = function(e) {
78
console.error("Loading the image " + filename + " failed", e);
82
if (options.progress) {
83
http.onprogress = function(e) {
91
const number_of_tries = n_tries || 0;
92
const timeout = [1, 1, 2, 3, 5, 8, 13, 21][number_of_tries] || 34;
94
load_file(filename, options, number_of_tries + 1);
99
function load_file_nodejs(filename, options) {
100
let fs = require("fs");
103
dbg_assert(!options.as_json);
105
fs["open"](filename, "r", (err, fd) => {
108
let length = options.range.length;
109
var buffer = Buffer.allocUnsafe(length);
111
fs["read"](fd, buffer, 0, length, options.range.start, (err, bytes_read) => {
114
dbg_assert(bytes_read === length);
115
options.done && options.done(new Uint8Array(buffer));
117
fs["close"](fd, (err) => {
124
encoding: options.as_json ? "utf-8" : null,
127
fs["readFile"](filename, o, function(err, data) {
129
console.log("Could not read file:", filename, err);
133
if (options.as_json) {
134
result = JSON.parse(result);
136
result = new Uint8Array(result).buffer;
139
options.done(result);
145
if (typeof XMLHttpRequest === "undefined") {
146
var determine_size = function(path, cb) {
147
require("fs")["stat"](path, (err, stats) => {
151
cb(null, stats.size);
156
var determine_size = function(url, cb) {
157
v86util.load_file(url, {
158
done: (buffer, http) => {
159
var header = http.getResponseHeader("Content-Range") || "";
160
var match = header.match(/\/(\d+)\s*$/);
165
const error = "`Range: bytes=...` header not supported (Got `" + header + "`)";
184
function AsyncXHRBuffer(filename, size) {
185
this.filename = filename;
188
this.block_size = 256;
189
this.byteLength = size;
191
this.loaded_blocks = Object.create(null);
193
this.onload = undefined;
194
this.onprogress = undefined;
197
AsyncXHRBuffer.prototype.load = function() {
198
if (this.byteLength !== undefined) {
199
this.onload && this.onload(Object.create(null));
205
determine_size(this.filename, (error, size) => {
207
throw new Error("Cannot use: " + this.filename + ". " + error);
209
dbg_assert(size >= 0);
210
this.byteLength = size;
211
this.onload && this.onload(Object.create(null));
221
AsyncXHRBuffer.prototype.get_from_cache = function(offset, len, fn) {
222
var number_of_blocks = len / this.block_size;
223
var block_index = offset / this.block_size;
225
for (var i = 0; i < number_of_blocks; i++) {
226
var block = this.loaded_blocks[block_index + i];
233
if (number_of_blocks === 1) {
234
return this.loaded_blocks[block_index];
236
var result = new Uint8Array(len);
237
for (var i = 0; i < number_of_blocks; i++) {
238
result.set(this.loaded_blocks[block_index + i], i * this.block_size);
249
AsyncXHRBuffer.prototype.get = function(offset, len, fn) {
250
console.assert(offset + len <= this.byteLength);
251
console.assert(offset % this.block_size === 0);
252
console.assert(len % this.block_size === 0);
255
var block = this.get_from_cache(offset, len, fn);
258
setTimeout(fn.bind(this, block), 0);
265
v86util.load_file(this.filename, {
266
done: function done(buffer) {
267
var block = new Uint8Array(buffer);
268
this.handle_read(offset, len, block);
287
AsyncXHRBuffer.prototype.set = function(start, data, fn) {
288
console.assert(start + data.byteLength <= this.byteLength);
290
var len = data.length;
292
console.assert(start % this.block_size === 0);
293
console.assert(len % this.block_size === 0);
296
var start_block = start / this.block_size;
297
var block_count = len / this.block_size;
299
for (var i = 0; i < block_count; i++) {
300
var block = this.loaded_blocks[start_block + i];
302
if (block === undefined) {
303
block = this.loaded_blocks[start_block + i] = new Uint8Array(this.block_size);
306
var data_slice = data.subarray(i * this.block_size, (i + 1) * this.block_size);
307
block.set(data_slice);
309
console.assert(block.byteLength === data_slice.length);
321
AsyncXHRBuffer.prototype.handle_read = function(offset, len, block) {
325
var start_block = offset / this.block_size;
326
var block_count = len / this.block_size;
328
for (var i = 0; i < block_count; i++) {
329
var written_block = this.loaded_blocks[start_block + i];
332
block.set(written_block, i * this.block_size);
342
AsyncXHRBuffer.prototype.get_buffer = function(fn) {
347
AsyncXHRBuffer.prototype.get_written_blocks = function() {
348
var count = Object.keys(this.loaded_blocks).length;
350
var buffer = new Uint8Array(count * this.block_size);
354
for (var index of Object.keys(this.loaded_blocks)) {
355
var block = this.loaded_blocks[index];
356
dbg_assert(block.length === this.block_size);
369
block_size: this.block_size,
373
AsyncXHRBuffer.prototype.get_state = function() {
375
const loaded_blocks = [];
377
for (let [index, block] of Object.entries(this.loaded_blocks)) {
378
dbg_assert(isFinite(+index));
379
loaded_blocks.push([+index, block]);
382
state[0] = loaded_blocks;
386
AsyncXHRBuffer.prototype.set_state = function(state) {
387
const loaded_blocks = state[0];
388
this.loaded_blocks = Object.create(null);
390
for (let [index, block] of Object.values(loaded_blocks)) {
391
this.loaded_blocks[index] = block;
404
function AsyncXHRPartfileBuffer(filename, size, fixed_chunk_size) {
405
const parts = filename.match(/(.*)(\..*)/);
408
this.basename = parts[1];
409
this.extension = parts[2];
411
this.basename = filename;
416
this.block_size = 256;
417
this.byteLength = size;
418
this.use_fixed_chunk_size = typeof fixed_chunk_size === "number";
419
this.fixed_chunk_size = fixed_chunk_size;
421
this.loaded_blocks = Object.create(null);
423
this.onload = undefined;
424
this.onprogress = undefined;
427
AsyncXHRPartfileBuffer.prototype.load = function() {
428
if (this.byteLength !== undefined) {
429
this.onload && this.onload(Object.create(null));
433
this.onload && this.onload(Object.create(null));
435
AsyncXHRPartfileBuffer.prototype.get_from_cache = AsyncXHRBuffer.prototype.get_from_cache;
442
AsyncXHRPartfileBuffer.prototype.get = function(offset, len, fn) {
443
console.assert(offset + len <= this.byteLength);
444
console.assert(offset % this.block_size === 0);
445
console.assert(len % this.block_size === 0);
448
var block = this.get_from_cache(offset, len, fn);
451
setTimeout(fn.bind(this, block), 0);
458
if (this.use_fixed_chunk_size) {
459
const fake_offset = parseInt(offset / this.fixed_chunk_size, 10) * this.fixed_chunk_size;
460
const m_offset = offset - fake_offset;
461
const total_count = parseInt(len / this.fixed_chunk_size, 10) + (m_offset ? 2 : 1);
462
const blocks = new Uint8Array(m_offset + (total_count * this.fixed_chunk_size));
465
for (var i = 0; i < total_count; i++) {
466
const cur = i * this.fixed_chunk_size;
467
const part_filename = this.basename + "-" + (cur + fake_offset) + this.extension;
469
v86util.load_file(part_filename, {
470
done: function done(buffer) {
471
const block = new Uint8Array(buffer);
472
blocks.set(block, cur);
474
if (finished === total_count) {
475
const tmp_blocks = blocks.subarray(m_offset, m_offset + len);
476
this.handle_read(offset, len, tmp_blocks);
483
const part_filename = this.basename + "-" + offset + "-" + (offset + len) + this.extension;
485
v86util.load_file(part_filename, {
486
done: function done(buffer) {
487
dbg_assert(buffer.byteLength === len);
488
var block = new Uint8Array(buffer);
489
this.handle_read(offset, len, block);
496
AsyncXHRPartfileBuffer.prototype.set = AsyncXHRBuffer.prototype.set;
497
AsyncXHRPartfileBuffer.prototype.handle_read = AsyncXHRBuffer.prototype.handle_read;
498
AsyncXHRPartfileBuffer.prototype.get_written_blocks = AsyncXHRBuffer.prototype.get_written_blocks;
499
AsyncXHRPartfileBuffer.prototype.get_state = AsyncXHRBuffer.prototype.get_state;
500
AsyncXHRPartfileBuffer.prototype.set_state = AsyncXHRBuffer.prototype.set_state;
508
function SyncFileBuffer(file) {
510
this.byteLength = file.size;
512
if (file.size > (1 << 30)) {
513
console.warn("SyncFileBuffer: Allocating buffer of " + (file.size >> 20) + " MB ...");
516
this.buffer = new ArrayBuffer(file.size);
517
this.onload = undefined;
518
this.onprogress = undefined;
521
SyncFileBuffer.prototype.load = function() {
528
SyncFileBuffer.prototype.load_next = function(start) {
530
var PART_SIZE = 4 << 20;
532
var filereader = new FileReader();
534
filereader.onload = function(e) {
535
var buffer = new Uint8Array(e.target.result);
536
new Uint8Array(this.buffer, start).set(buffer);
537
this.load_next(start + PART_SIZE);
540
if (this.onprogress) {
543
total: this.byteLength,
544
lengthComputable: true,
548
if (start < this.byteLength) {
549
var end = Math.min(start + PART_SIZE, this.byteLength);
550
var slice = this.file.slice(start, end);
551
filereader.readAsArrayBuffer(slice);
553
this.file = undefined;
554
this.onload && this.onload({
565
SyncFileBuffer.prototype.get = function(start, len, fn) {
566
console.assert(start + len <= this.byteLength);
567
fn(new Uint8Array(this.buffer, start, len));
575
SyncFileBuffer.prototype.set = function(offset, slice, fn) {
576
console.assert(offset + slice.byteLength <= this.byteLength);
578
new Uint8Array(this.buffer, offset, slice.byteLength).set(slice);
582
SyncFileBuffer.prototype.get_buffer = function(fn) {
586
SyncFileBuffer.prototype.get_state = function() {
588
state[0] = this.byteLength;
589
state[1] = new Uint8Array(this.buffer);
593
SyncFileBuffer.prototype.set_state = function(state) {
594
this.byteLength = state[0];
595
this.buffer = state[1].slice().buffer;
603
function AsyncFileBuffer(file) {
605
this.byteLength = file.size;
608
this.block_size = 256;
609
this.loaded_blocks = Object.create(null);
611
this.onload = undefined;
612
this.onprogress = undefined;
615
AsyncFileBuffer.prototype.load = function() {
616
this.onload && this.onload(Object.create(null));
624
AsyncFileBuffer.prototype.get = function(offset, len, fn) {
625
console.assert(offset % this.block_size === 0);
626
console.assert(len % this.block_size === 0);
629
var block = this.get_from_cache(offset, len, fn);
635
var fr = new FileReader();
637
fr.onload = function(e) {
638
var buffer = e.target.result;
639
var block = new Uint8Array(buffer);
641
this.handle_read(offset, len, block);
645
fr.readAsArrayBuffer(this.file.slice(offset, offset + len));
647
AsyncFileBuffer.prototype.get_from_cache = AsyncXHRBuffer.prototype.get_from_cache;
648
AsyncFileBuffer.prototype.set = AsyncXHRBuffer.prototype.set;
649
AsyncFileBuffer.prototype.handle_read = AsyncXHRBuffer.prototype.handle_read;
650
AsyncFileBuffer.prototype.get_state = AsyncXHRBuffer.prototype.get_state;
652
AsyncFileBuffer.prototype.get_buffer = function(fn) {
657
AsyncFileBuffer.prototype.get_as_file = function(name) {
659
var existing_blocks = Object.keys(this.loaded_blocks)
661
.sort(function(x, y) {
665
var current_offset = 0;
667
for (var i = 0; i < existing_blocks.length; i++) {
668
var block_index = existing_blocks[i];
669
var block = this.loaded_blocks[block_index];
670
var start = block_index * this.block_size;
671
console.assert(start >= current_offset);
673
if (start !== current_offset) {
674
parts.push(this.file.slice(current_offset, start));
675
current_offset = start;
679
current_offset += block.length;
682
if (current_offset !== this.file.size) {
683
parts.push(this.file.slice(current_offset));
686
var file = new File(parts, name);
687
console.assert(file.size === this.file.size);