buffer.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804
  1. "use strict";
  2. (function()
  3. {
  4. v86util.SyncBuffer = SyncBuffer;
  5. v86util.AsyncXHRBuffer = AsyncXHRBuffer;
  6. v86util.AsyncXHRPartfileBuffer = AsyncXHRPartfileBuffer;
  7. v86util.AsyncFileBuffer = AsyncFileBuffer;
  8. v86util.SyncFileBuffer = SyncFileBuffer;
  9. v86util.buffer_from_object = buffer_from_object;
  10. // The smallest size the emulated hardware can emit
  11. const BLOCK_SIZE = 256;
  12. const ASYNC_SAFE = false;
  13. /**
  14. * Synchronous access to ArrayBuffer
  15. * @constructor
  16. */
  17. function SyncBuffer(buffer)
  18. {
  19. dbg_assert(buffer instanceof ArrayBuffer);
  20. this.buffer = buffer;
  21. this.byteLength = buffer.byteLength;
  22. this.onload = undefined;
  23. this.onprogress = undefined;
  24. }
  25. SyncBuffer.prototype.load = function()
  26. {
  27. this.onload && this.onload({ buffer: this.buffer });
  28. };
  29. /**
  30. * @this {SyncBuffer|SyncFileBuffer}
  31. * @param {number} start
  32. * @param {number} len
  33. * @param {function(!Uint8Array)} fn
  34. */
  35. SyncBuffer.prototype.get = function(start, len, fn)
  36. {
  37. dbg_assert(start + len <= this.byteLength);
  38. fn(new Uint8Array(this.buffer, start, len));
  39. };
  40. /**
  41. * @this {SyncBuffer|SyncFileBuffer}
  42. * @param {number} start
  43. * @param {!Uint8Array} slice
  44. * @param {function()} fn
  45. */
  46. SyncBuffer.prototype.set = function(start, slice, fn)
  47. {
  48. dbg_assert(start + slice.byteLength <= this.byteLength);
  49. new Uint8Array(this.buffer, start, slice.byteLength).set(slice);
  50. fn();
  51. };
  52. /**
  53. * @this {SyncBuffer|SyncFileBuffer}
  54. * @param {function(!ArrayBuffer)} fn
  55. */
  56. SyncBuffer.prototype.get_buffer = function(fn)
  57. {
  58. fn(this.buffer);
  59. };
  60. /**
  61. * @this {SyncBuffer|SyncFileBuffer}
  62. */
  63. SyncBuffer.prototype.get_state = function()
  64. {
  65. const state = [];
  66. state[0] = this.byteLength;
  67. state[1] = new Uint8Array(this.buffer);
  68. return state;
  69. };
  70. /**
  71. * @this {SyncBuffer|SyncFileBuffer}
  72. */
  73. SyncBuffer.prototype.set_state = function(state)
  74. {
  75. this.byteLength = state[0];
  76. this.buffer = state[1].slice().buffer;
  77. };
  78. /**
  79. * Asynchronous access to ArrayBuffer, loading blocks lazily as needed,
  80. * using the `Range: bytes=...` header
  81. *
  82. * @constructor
  83. * @param {string} filename Name of the file to download
  84. * @param {number|undefined} size
  85. * @param {number|undefined} fixed_chunk_size
  86. */
  87. function AsyncXHRBuffer(filename, size, fixed_chunk_size)
  88. {
  89. this.filename = filename;
  90. this.byteLength = size;
  91. this.block_cache = new Map();
  92. this.block_cache_is_write = new Set();
  93. this.fixed_chunk_size = fixed_chunk_size;
  94. this.cache_reads = !!fixed_chunk_size; // TODO: could also be useful in other cases (needs testing)
  95. this.onload = undefined;
  96. this.onprogress = undefined;
  97. }
  98. AsyncXHRBuffer.prototype.load = function()
  99. {
  100. if(this.byteLength !== undefined)
  101. {
  102. this.onload && this.onload(Object.create(null));
  103. return;
  104. }
  105. // Determine the size using a request
  106. determine_size(this.filename, (error, size) =>
  107. {
  108. if(error)
  109. {
  110. throw new Error("Cannot use: " + this.filename + ". " + error);
  111. }
  112. else
  113. {
  114. dbg_assert(size >= 0);
  115. this.byteLength = size;
  116. this.onload && this.onload(Object.create(null));
  117. }
  118. });
  119. };
  120. /**
  121. * @param {number} offset
  122. * @param {number} len
  123. * @this {AsyncXHRBuffer|AsyncXHRPartfileBuffer|AsyncFileBuffer}
  124. */
  125. AsyncXHRBuffer.prototype.get_from_cache = function(offset, len)
  126. {
  127. var number_of_blocks = len / BLOCK_SIZE;
  128. var block_index = offset / BLOCK_SIZE;
  129. for(var i = 0; i < number_of_blocks; i++)
  130. {
  131. var block = this.block_cache.get(block_index + i);
  132. if(!block)
  133. {
  134. return;
  135. }
  136. }
  137. if(number_of_blocks === 1)
  138. {
  139. return this.block_cache.get(block_index);
  140. }
  141. else
  142. {
  143. var result = new Uint8Array(len);
  144. for(var i = 0; i < number_of_blocks; i++)
  145. {
  146. result.set(this.block_cache.get(block_index + i), i * BLOCK_SIZE);
  147. }
  148. return result;
  149. }
  150. };
  151. /**
  152. * @param {number} offset
  153. * @param {number} len
  154. * @param {function(!Uint8Array)} fn
  155. */
  156. AsyncXHRBuffer.prototype.get = function(offset, len, fn)
  157. {
  158. dbg_assert(offset + len <= this.byteLength);
  159. dbg_assert(offset % BLOCK_SIZE === 0);
  160. dbg_assert(len % BLOCK_SIZE === 0);
  161. dbg_assert(len);
  162. var block = this.get_from_cache(offset, len);
  163. if(block)
  164. {
  165. if(ASYNC_SAFE)
  166. {
  167. setTimeout(fn.bind(this, block), 0);
  168. }
  169. else
  170. {
  171. fn(block);
  172. }
  173. return;
  174. }
  175. var requested_start = offset;
  176. var requested_length = len;
  177. if(this.fixed_chunk_size)
  178. {
  179. requested_start = offset - (offset % this.fixed_chunk_size);
  180. requested_length = Math.ceil((offset - requested_start + len) / this.fixed_chunk_size) * this.fixed_chunk_size;
  181. }
  182. v86util.load_file(this.filename, {
  183. done: function done(buffer)
  184. {
  185. var block = new Uint8Array(buffer);
  186. this.handle_read(requested_start, requested_length, block);
  187. if(requested_start === offset && requested_length === len)
  188. {
  189. fn(block);
  190. }
  191. else
  192. {
  193. fn(block.subarray(offset - requested_start, offset - requested_start + len));
  194. }
  195. }.bind(this),
  196. range: { start: requested_start, length: requested_length },
  197. });
  198. };
  199. /**
  200. * Relies on this.byteLength and this.block_cache
  201. *
  202. * @this {AsyncXHRBuffer|AsyncXHRPartfileBuffer|AsyncFileBuffer}
  203. *
  204. * @param {number} start
  205. * @param {!Uint8Array} data
  206. * @param {function()} fn
  207. */
  208. AsyncXHRBuffer.prototype.set = function(start, data, fn)
  209. {
  210. var len = data.length;
  211. dbg_assert(start + data.byteLength <= this.byteLength);
  212. dbg_assert(start % BLOCK_SIZE === 0);
  213. dbg_assert(len % BLOCK_SIZE === 0);
  214. dbg_assert(len);
  215. var start_block = start / BLOCK_SIZE;
  216. var block_count = len / BLOCK_SIZE;
  217. for(var i = 0; i < block_count; i++)
  218. {
  219. var block = this.block_cache.get(start_block + i);
  220. if(block === undefined)
  221. {
  222. const data_slice = data.slice(i * BLOCK_SIZE, (i + 1) * BLOCK_SIZE);
  223. this.block_cache.set(start_block + i, data_slice);
  224. }
  225. else
  226. {
  227. const data_slice = data.subarray(i * BLOCK_SIZE, (i + 1) * BLOCK_SIZE);
  228. dbg_assert(block.byteLength === data_slice.length);
  229. block.set(data_slice);
  230. }
  231. this.block_cache_is_write.add(start_block + i);
  232. }
  233. fn();
  234. };
  235. /**
  236. * @this {AsyncXHRBuffer|AsyncXHRPartfileBuffer|AsyncFileBuffer}
  237. * @param {number} offset
  238. * @param {number} len
  239. * @param {!Uint8Array} block
  240. */
  241. AsyncXHRBuffer.prototype.handle_read = function(offset, len, block)
  242. {
  243. // Used by AsyncXHRBuffer, AsyncXHRPartfileBuffer and AsyncFileBuffer
  244. // Overwrites blocks from the original source that have been written since
  245. var start_block = offset / BLOCK_SIZE;
  246. var block_count = len / BLOCK_SIZE;
  247. for(var i = 0; i < block_count; i++)
  248. {
  249. const cached_block = this.block_cache.get(start_block + i);
  250. if(cached_block)
  251. {
  252. block.set(cached_block, i * BLOCK_SIZE);
  253. }
  254. else if(this.cache_reads)
  255. {
  256. this.block_cache.set(start_block + i, block.slice(i * BLOCK_SIZE, (i + 1) * BLOCK_SIZE));
  257. }
  258. }
  259. };
  260. AsyncXHRBuffer.prototype.get_buffer = function(fn)
  261. {
  262. // We must download all parts, unlikely a good idea for big files
  263. fn();
  264. };
  265. ///**
  266. // * @this {AsyncXHRBuffer|AsyncXHRPartfileBuffer|AsyncFileBuffer}
  267. // */
  268. //AsyncXHRBuffer.prototype.get_block_cache = function()
  269. //{
  270. // var count = Object.keys(this.block_cache).length;
  271. // var buffer = new Uint8Array(count * BLOCK_SIZE);
  272. // var indices = [];
  273. // var i = 0;
  274. // for(var index of Object.keys(this.block_cache))
  275. // {
  276. // var block = this.block_cache.get(index);
  277. // dbg_assert(block.length === BLOCK_SIZE);
  278. // index = +index;
  279. // indices.push(index);
  280. // buffer.set(
  281. // block,
  282. // i * BLOCK_SIZE
  283. // );
  284. // i++;
  285. // }
  286. // return {
  287. // buffer,
  288. // indices,
  289. // block_size: BLOCK_SIZE,
  290. // };
  291. //};
  292. /**
  293. * @this {AsyncXHRBuffer|AsyncXHRPartfileBuffer|AsyncFileBuffer}
  294. */
  295. AsyncXHRBuffer.prototype.get_state = function()
  296. {
  297. const state = [];
  298. const block_cache = [];
  299. for(let [index, block] of this.block_cache)
  300. {
  301. dbg_assert(isFinite(index));
  302. if(this.block_cache_is_write.has(index))
  303. {
  304. block_cache.push([index, block]);
  305. }
  306. }
  307. state[0] = block_cache;
  308. return state;
  309. };
  310. /**
  311. * @this {AsyncXHRBuffer|AsyncXHRPartfileBuffer|AsyncFileBuffer}
  312. */
  313. AsyncXHRBuffer.prototype.set_state = function(state)
  314. {
  315. const block_cache = state[0];
  316. this.block_cache.clear();
  317. this.block_cache_is_write.clear();
  318. for(let [index, block] of block_cache)
  319. {
  320. dbg_assert(isFinite(index));
  321. this.block_cache.set(index, block);
  322. this.block_cache_is_write.add(index);
  323. }
  324. };
  325. /**
  326. * Asynchronous access to ArrayBuffer, loading blocks lazily as needed,
  327. * downloading files named filename-%d-%d.ext (where the %d are start and end offset).
  328. * Or, if partfile_alt_format is set, filename-%08d.ext (where %d is the part number, compatible with gnu split).
  329. *
  330. * @constructor
  331. * @param {string} filename Name of the file to download
  332. * @param {number|undefined} size
  333. * @param {number|undefined} fixed_chunk_size
  334. * @param {boolean|undefined} partfile_alt_format
  335. */
  336. function AsyncXHRPartfileBuffer(filename, size, fixed_chunk_size, partfile_alt_format, zstd_decompress)
  337. {
  338. const parts = filename.match(/\.[^\.]+(\.zst)?$/);
  339. this.extension = parts ? parts[0] : "";
  340. this.basename = filename.substring(0, filename.length - this.extension.length);
  341. this.is_zstd = this.extension.endsWith(".zst");
  342. if(!this.basename.endsWith("/"))
  343. {
  344. this.basename += "-";
  345. }
  346. this.block_cache = new Map();
  347. this.block_cache_is_write = new Set();
  348. this.byteLength = size;
  349. this.fixed_chunk_size = fixed_chunk_size;
  350. this.partfile_alt_format = !!partfile_alt_format;
  351. this.zstd_decompress = zstd_decompress;
  352. this.cache_reads = !!fixed_chunk_size; // TODO: could also be useful in other cases (needs testing)
  353. this.onload = undefined;
  354. this.onprogress = undefined;
  355. }
  356. AsyncXHRPartfileBuffer.prototype.load = function()
  357. {
  358. if(this.byteLength !== undefined)
  359. {
  360. this.onload && this.onload(Object.create(null));
  361. return;
  362. }
  363. dbg_assert(false);
  364. this.onload && this.onload(Object.create(null));
  365. };
  366. /**
  367. * @param {number} offset
  368. * @param {number} len
  369. * @param {function(!Uint8Array)} fn
  370. */
  371. AsyncXHRPartfileBuffer.prototype.get = function(offset, len, fn)
  372. {
  373. dbg_assert(offset + len <= this.byteLength);
  374. dbg_assert(offset % BLOCK_SIZE === 0);
  375. dbg_assert(len % BLOCK_SIZE === 0);
  376. dbg_assert(len);
  377. const block = this.get_from_cache(offset, len);
  378. if(block)
  379. {
  380. if(ASYNC_SAFE)
  381. {
  382. setTimeout(fn.bind(this, block), 0);
  383. }
  384. else
  385. {
  386. fn(block);
  387. }
  388. return;
  389. }
  390. if(this.fixed_chunk_size)
  391. {
  392. const start_index = Math.floor(offset / this.fixed_chunk_size);
  393. const m_offset = offset - start_index * this.fixed_chunk_size;
  394. dbg_assert(m_offset >= 0);
  395. const total_count = Math.ceil((m_offset + len) / this.fixed_chunk_size);
  396. const blocks = new Uint8Array(total_count * this.fixed_chunk_size);
  397. let finished = 0;
  398. for(let i = 0; i < total_count; i++)
  399. {
  400. const offset = (start_index + i) * this.fixed_chunk_size;
  401. const part_filename =
  402. this.partfile_alt_format ?
  403. // matches output of gnu split:
  404. // split -b 512 -a8 -d --additional-suffix .img w95.img w95-
  405. this.basename + (start_index + i + "").padStart(8, "0") + this.extension
  406. :
  407. this.basename + offset + "-" + (offset + this.fixed_chunk_size) + this.extension;
  408. // XXX: unnecessary allocation
  409. const block = this.get_from_cache(offset, this.fixed_chunk_size);
  410. if(block)
  411. {
  412. blocks.set(block, i * this.fixed_chunk_size);
  413. finished++;
  414. if(finished === total_count)
  415. {
  416. fn(blocks.subarray(m_offset, m_offset + len));
  417. }
  418. }
  419. else
  420. {
  421. v86util.load_file(part_filename, {
  422. done: async function done(buffer)
  423. {
  424. let block = new Uint8Array(buffer);
  425. if(this.is_zstd)
  426. {
  427. const decompressed = await this.zstd_decompress(this.fixed_chunk_size, block);
  428. block = new Uint8Array(decompressed);
  429. }
  430. blocks.set(block, i * this.fixed_chunk_size);
  431. this.handle_read((start_index + i) * this.fixed_chunk_size, this.fixed_chunk_size|0, block);
  432. finished++;
  433. if(finished === total_count)
  434. {
  435. fn(blocks.subarray(m_offset, m_offset + len));
  436. }
  437. }.bind(this),
  438. });
  439. }
  440. }
  441. }
  442. else
  443. {
  444. const part_filename = this.basename + offset + "-" + (offset + len) + this.extension;
  445. v86util.load_file(part_filename, {
  446. done: function done(buffer)
  447. {
  448. dbg_assert(buffer.byteLength === len);
  449. var block = new Uint8Array(buffer);
  450. this.handle_read(offset, len, block);
  451. fn(block);
  452. }.bind(this),
  453. });
  454. }
  455. };
  456. AsyncXHRPartfileBuffer.prototype.get_from_cache = AsyncXHRBuffer.prototype.get_from_cache;
  457. AsyncXHRPartfileBuffer.prototype.set = AsyncXHRBuffer.prototype.set;
  458. AsyncXHRPartfileBuffer.prototype.handle_read = AsyncXHRBuffer.prototype.handle_read;
  459. //AsyncXHRPartfileBuffer.prototype.get_block_cache = AsyncXHRBuffer.prototype.get_block_cache;
  460. AsyncXHRPartfileBuffer.prototype.get_state = AsyncXHRBuffer.prototype.get_state;
  461. AsyncXHRPartfileBuffer.prototype.set_state = AsyncXHRBuffer.prototype.set_state;
  462. /**
  463. * Synchronous access to File, loading blocks from the input type=file
  464. * The whole file is loaded into memory during initialisation
  465. *
  466. * @constructor
  467. */
  468. function SyncFileBuffer(file)
  469. {
  470. this.file = file;
  471. this.byteLength = file.size;
  472. if(file.size > (1 << 30))
  473. {
  474. console.warn("SyncFileBuffer: Allocating buffer of " + (file.size >> 20) + " MB ...");
  475. }
  476. this.buffer = new ArrayBuffer(file.size);
  477. this.onload = undefined;
  478. this.onprogress = undefined;
  479. }
  480. SyncFileBuffer.prototype.load = function()
  481. {
  482. this.load_next(0);
  483. };
  484. /**
  485. * @param {number} start
  486. */
  487. SyncFileBuffer.prototype.load_next = function(start)
  488. {
  489. /** @const */
  490. var PART_SIZE = 4 << 20;
  491. var filereader = new FileReader();
  492. filereader.onload = function(e)
  493. {
  494. var buffer = new Uint8Array(e.target.result);
  495. new Uint8Array(this.buffer, start).set(buffer);
  496. this.load_next(start + PART_SIZE);
  497. }.bind(this);
  498. if(this.onprogress)
  499. {
  500. this.onprogress({
  501. loaded: start,
  502. total: this.byteLength,
  503. lengthComputable: true,
  504. });
  505. }
  506. if(start < this.byteLength)
  507. {
  508. var end = Math.min(start + PART_SIZE, this.byteLength);
  509. var slice = this.file.slice(start, end);
  510. filereader.readAsArrayBuffer(slice);
  511. }
  512. else
  513. {
  514. this.file = undefined;
  515. this.onload && this.onload({ buffer: this.buffer });
  516. }
  517. };
  518. SyncFileBuffer.prototype.get = SyncBuffer.prototype.get;
  519. SyncFileBuffer.prototype.set = SyncBuffer.prototype.set;
  520. SyncFileBuffer.prototype.get_buffer = SyncBuffer.prototype.get_buffer;
  521. SyncFileBuffer.prototype.get_state = SyncBuffer.prototype.get_state;
  522. SyncFileBuffer.prototype.set_state = SyncBuffer.prototype.set_state;
  523. /**
  524. * Asynchronous access to File, loading blocks from the input type=file
  525. *
  526. * @constructor
  527. */
  528. function AsyncFileBuffer(file)
  529. {
  530. this.file = file;
  531. this.byteLength = file.size;
  532. this.block_cache = new Map();
  533. this.block_cache_is_write = new Set();
  534. this.onload = undefined;
  535. this.onprogress = undefined;
  536. }
  537. AsyncFileBuffer.prototype.load = function()
  538. {
  539. this.onload && this.onload(Object.create(null));
  540. };
  541. /**
  542. * @param {number} offset
  543. * @param {number} len
  544. * @param {function(!Uint8Array)} fn
  545. */
  546. AsyncFileBuffer.prototype.get = function(offset, len, fn)
  547. {
  548. dbg_assert(offset % BLOCK_SIZE === 0);
  549. dbg_assert(len % BLOCK_SIZE === 0);
  550. dbg_assert(len);
  551. var block = this.get_from_cache(offset, len);
  552. if(block)
  553. {
  554. fn(block);
  555. return;
  556. }
  557. var fr = new FileReader();
  558. fr.onload = function(e)
  559. {
  560. var buffer = e.target.result;
  561. var block = new Uint8Array(buffer);
  562. this.handle_read(offset, len, block);
  563. fn(block);
  564. }.bind(this);
  565. fr.readAsArrayBuffer(this.file.slice(offset, offset + len));
  566. };
  567. AsyncFileBuffer.prototype.get_from_cache = AsyncXHRBuffer.prototype.get_from_cache;
  568. AsyncFileBuffer.prototype.set = AsyncXHRBuffer.prototype.set;
  569. AsyncFileBuffer.prototype.handle_read = AsyncXHRBuffer.prototype.handle_read;
  570. AsyncFileBuffer.prototype.get_state = AsyncXHRBuffer.prototype.get_state;
  571. AsyncFileBuffer.prototype.set_state = AsyncXHRBuffer.prototype.set_state;
  572. AsyncFileBuffer.prototype.get_buffer = function(fn)
  573. {
  574. // We must load all parts, unlikely a good idea for big files
  575. fn();
  576. };
  577. AsyncFileBuffer.prototype.get_as_file = function(name)
  578. {
  579. var parts = [];
  580. var existing_blocks = Array.from(this.block_cache.keys()).sort(function(x, y) { return x - y; });
  581. var current_offset = 0;
  582. for(var i = 0; i < existing_blocks.length; i++)
  583. {
  584. var block_index = existing_blocks[i];
  585. var block = this.block_cache.get(block_index);
  586. var start = block_index * BLOCK_SIZE;
  587. dbg_assert(start >= current_offset);
  588. if(start !== current_offset)
  589. {
  590. parts.push(this.file.slice(current_offset, start));
  591. current_offset = start;
  592. }
  593. parts.push(block);
  594. current_offset += block.length;
  595. }
  596. if(current_offset !== this.file.size)
  597. {
  598. parts.push(this.file.slice(current_offset));
  599. }
  600. var file = new File(parts, name);
  601. dbg_assert(file.size === this.file.size);
  602. return file;
  603. };
  604. if(typeof XMLHttpRequest === "undefined")
  605. {
  606. var determine_size = function(path, cb)
  607. {
  608. require("fs")["stat"](path, (err, stats) =>
  609. {
  610. if(err)
  611. {
  612. cb(err);
  613. }
  614. else
  615. {
  616. cb(null, stats.size);
  617. }
  618. });
  619. };
  620. }
  621. else
  622. {
  623. var determine_size = function(url, cb)
  624. {
  625. v86util.load_file(url, {
  626. done: (buffer, http) =>
  627. {
  628. var header = http.getResponseHeader("Content-Range") || "";
  629. var match = header.match(/\/(\d+)\s*$/);
  630. if(match)
  631. {
  632. cb(null, +match[1]);
  633. }
  634. else
  635. {
  636. const error = "`Range: bytes=...` header not supported (Got `" + header + "`)";
  637. cb(error);
  638. }
  639. },
  640. headers: {
  641. Range: "bytes=0-0",
  642. "X-Accept-Encoding": "identity"
  643. }
  644. });
  645. };
  646. }
  647. function buffer_from_object(obj, zstd_decompress_worker)
  648. {
  649. // TODO: accept Uint8Array, ArrayBuffer, File, url rather than { url }
  650. if(obj.buffer instanceof ArrayBuffer)
  651. {
  652. return new v86util.SyncBuffer(obj.buffer);
  653. }
  654. else if(typeof File !== "undefined" && obj.buffer instanceof File)
  655. {
  656. // SyncFileBuffer:
  657. // - loads the whole disk image into memory, impossible for large files (more than 1GB)
  658. // - can later serve get/set operations fast and synchronously
  659. // - takes some time for first load, neglectable for small files (up to 100Mb)
  660. //
  661. // AsyncFileBuffer:
  662. // - loads slices of the file asynchronously as requested
  663. // - slower get/set
  664. // Heuristics: If file is larger than or equal to 256M, use AsyncFileBuffer
  665. let is_async = obj.async;
  666. if(is_async === undefined)
  667. {
  668. is_async = obj.buffer.size >= 256 * 1024 * 1024;
  669. }
  670. if(is_async)
  671. {
  672. return new v86util.AsyncFileBuffer(obj.buffer);
  673. }
  674. else
  675. {
  676. return new v86util.SyncFileBuffer(obj.buffer);
  677. }
  678. }
  679. else if(obj.url)
  680. {
  681. // Note: Only async for now
  682. if(obj.use_parts)
  683. {
  684. return new v86util.AsyncXHRPartfileBuffer(obj.url, obj.size, obj.fixed_chunk_size, false, zstd_decompress_worker);
  685. }
  686. else
  687. {
  688. return new v86util.AsyncXHRBuffer(obj.url, obj.size, obj.fixed_chunk_size);
  689. }
  690. }
  691. else
  692. {
  693. dbg_log("Ignored file: url=" + obj.url + " buffer=" + obj.buffer);
  694. }
  695. }
  696. })();