123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325 |
- "use strict";
- /** @const */
- var STATE_VERSION = 6;
- /** @const */
- var STATE_MAGIC = 0x86768676|0;
- /** @const */
- var STATE_INDEX_MAGIC = 0;
- /** @const */
- var STATE_INDEX_VERSION = 1;
- /** @const */
- var STATE_INDEX_TOTAL_LEN = 2;
- /** @const */
- var STATE_INDEX_INFO_LEN = 3;
- /** @const */
- var STATE_INFO_BLOCK_START = 16;
- const ZSTD_MAGIC = 0xFD2FB528;
- /** @constructor */
- function StateLoadError(msg)
- {
- this.message = msg;
- }
- StateLoadError.prototype = new Error;
- const CONSTRUCTOR_TABLE = {
- "Uint8Array": Uint8Array,
- "Int8Array": Int8Array,
- "Uint16Array": Uint16Array,
- "Int16Array": Int16Array,
- "Uint32Array": Uint32Array,
- "Int32Array": Int32Array,
- "Float32Array": Float32Array,
- "Float64Array": Float64Array,
- };
- function save_object(obj, saved_buffers)
- {
- if(typeof obj !== "object" || obj === null)
- {
- dbg_assert(typeof obj !== "function");
- return obj;
- }
- if(obj instanceof Array)
- {
- return obj.map(x => save_object(x, saved_buffers));
- }
- if(obj.constructor === Object)
- {
- console.log(obj);
- dbg_assert(obj.constructor !== Object, "Expected non-object");
- }
- if(obj.BYTES_PER_ELEMENT)
- {
- // Uint8Array, etc.
- var buffer = new Uint8Array(obj.buffer, obj.byteOffset, obj.length * obj.BYTES_PER_ELEMENT);
- const constructor = obj.constructor.name.replace("bound ", "");
- dbg_assert(CONSTRUCTOR_TABLE[constructor]);
- return {
- "__state_type__": constructor,
- "buffer_id": saved_buffers.push(buffer) - 1,
- };
- }
- if(DEBUG && !obj.get_state)
- {
- console.log("Object without get_state: ", obj);
- }
- var state = obj.get_state();
- var result = [];
- for(var i = 0; i < state.length; i++)
- {
- var value = state[i];
- dbg_assert(typeof value !== "function");
- result[i] = save_object(value, saved_buffers);
- }
- return result;
- }
- function restore_buffers(obj, buffers)
- {
- if(typeof obj !== "object" || obj === null)
- {
- dbg_assert(typeof obj !== "function");
- return obj;
- }
- if(obj instanceof Array)
- {
- for(let i = 0; i < obj.length; i++)
- {
- obj[i] = restore_buffers(obj[i], buffers);
- }
- return obj;
- }
- const type = obj["__state_type__"];
- dbg_assert(type !== undefined);
- const constructor = CONSTRUCTOR_TABLE[type];
- dbg_assert(constructor, "Unkown type: " + type);
- const buffer = buffers[obj["buffer_id"]];
- return new constructor(buffer);
- }
- CPU.prototype.save_state = function()
- {
- var saved_buffers = [];
- var state = save_object(this, saved_buffers);
- var buffer_infos = [];
- var total_buffer_size = 0;
- for(var i = 0; i < saved_buffers.length; i++)
- {
- var len = saved_buffers[i].byteLength;
- buffer_infos[i] = {
- offset: total_buffer_size,
- length: len,
- };
- total_buffer_size += len;
- // align
- total_buffer_size = total_buffer_size + 3 & ~3;
- }
- var info_object = JSON.stringify({
- "buffer_infos": buffer_infos,
- "state": state,
- });
- var info_block = new TextEncoder().encode(info_object);
- var buffer_block_start = STATE_INFO_BLOCK_START + info_block.length;
- buffer_block_start = buffer_block_start + 3 & ~3;
- var total_size = buffer_block_start + total_buffer_size;
- //console.log("State: json_size=" + Math.ceil(buffer_block_start / 1024 / 1024) + "MB " +
- // "buffer_size=" + Math.ceil(total_buffer_size / 1024 / 1024) + "MB");
- var result = new ArrayBuffer(total_size);
- var header_block = new Int32Array(
- result,
- 0,
- STATE_INFO_BLOCK_START / 4
- );
- new Uint8Array(result, STATE_INFO_BLOCK_START, info_block.length).set(info_block);
- var buffer_block = new Uint8Array(
- result,
- buffer_block_start
- );
- header_block[STATE_INDEX_MAGIC] = STATE_MAGIC;
- header_block[STATE_INDEX_VERSION] = STATE_VERSION;
- header_block[STATE_INDEX_TOTAL_LEN] = total_size;
- header_block[STATE_INDEX_INFO_LEN] = info_block.length;
- for(var i = 0; i < saved_buffers.length; i++)
- {
- var buffer = saved_buffers[i];
- dbg_assert(buffer.constructor === Uint8Array);
- buffer_block.set(buffer, buffer_infos[i].offset);
- }
- dbg_log("State: json size " + (info_block.byteLength >> 10) + "k");
- dbg_log("State: Total buffers size " + (buffer_block.byteLength >> 10) + "k");
- return result;
- };
- CPU.prototype.restore_state = function(state)
- {
- state = new Uint8Array(state);
- function read_state_header(state, check_length)
- {
- const len = state.length;
- if(len < STATE_INFO_BLOCK_START)
- {
- throw new StateLoadError("Invalid length: " + len);
- }
- const header_block = new Int32Array(state.buffer, state.byteOffset, 4);
- if(header_block[STATE_INDEX_MAGIC] !== STATE_MAGIC)
- {
- throw new StateLoadError("Invalid header: " + h(header_block[STATE_INDEX_MAGIC] >>> 0));
- }
- if(header_block[STATE_INDEX_VERSION] !== STATE_VERSION)
- {
- throw new StateLoadError(
- "Version mismatch: dump=" + header_block[STATE_INDEX_VERSION] +
- " we=" + STATE_VERSION);
- }
- if(check_length && header_block[STATE_INDEX_TOTAL_LEN] !== len)
- {
- throw new StateLoadError(
- "Length doesn't match header: " +
- "real=" + len + " header=" + header_block[STATE_INDEX_TOTAL_LEN]);
- }
- return header_block[STATE_INDEX_INFO_LEN];
- }
- function read_info_block(info_block_buffer)
- {
- const info_block = new TextDecoder().decode(info_block_buffer);
- return JSON.parse(info_block);
- }
- if(new Uint32Array(state.buffer, 0, 1)[0] === ZSTD_MAGIC)
- {
- const ctx = this.zstd_create_ctx(state.length);
- new Uint8Array(this.wasm_memory.buffer, this.zstd_get_src_ptr(ctx), state.length).set(state);
- let ptr = this.zstd_read(ctx, 16);
- const header_block = new Uint8Array(this.wasm_memory.buffer, ptr, 16);
- const info_block_len = read_state_header(header_block, false);
- this.zstd_read_free(ptr, 16);
- ptr = this.zstd_read(ctx, info_block_len);
- const info_block_buffer = new Uint8Array(this.wasm_memory.buffer, ptr, info_block_len);
- const info_block_obj = read_info_block(info_block_buffer);
- this.zstd_read_free(ptr, info_block_len);
- let state_object = info_block_obj["state"];
- const buffer_infos = info_block_obj["buffer_infos"];
- const buffers = [];
- let position = STATE_INFO_BLOCK_START + info_block_len;
- for(const buffer_info of buffer_infos)
- {
- const front_padding = (position + 3 & ~3) - position;
- const CHUNK_SIZE = 1 * 1024 * 1024;
- if(buffer_info.length > CHUNK_SIZE)
- {
- const ptr = this.zstd_read(ctx, front_padding);
- this.zstd_read_free(ptr, front_padding);
- const buffer = new Uint8Array(buffer_info.length);
- buffers.push(buffer.buffer);
- let have = 0;
- while(have < buffer_info.length)
- {
- const remaining = buffer_info.length - have;
- dbg_assert(remaining >= 0);
- const to_read = Math.min(remaining, CHUNK_SIZE);
- const ptr = this.zstd_read(ctx, to_read);
- buffer.set(new Uint8Array(this.wasm_memory.buffer, ptr, to_read), have);
- this.zstd_read_free(ptr, to_read);
- have += to_read;
- }
- }
- else
- {
- const ptr = this.zstd_read(ctx, front_padding + buffer_info.length);
- const offset = ptr + front_padding;
- buffers.push(this.wasm_memory.buffer.slice(offset, offset + buffer_info.length));
- this.zstd_read_free(ptr, front_padding + buffer_info.length);
- }
- position += front_padding + buffer_info.length;
- }
- state_object = restore_buffers(state_object, buffers);
- this.set_state(state_object);
- this.zstd_free_ctx(ctx);
- }
- else
- {
- const info_block_len = read_state_header(state, true);
- if(info_block_len < 0 || info_block_len + 12 >= state.length)
- {
- throw new StateLoadError("Invalid info block length: " + info_block_len);
- }
- const info_block_buffer = state.subarray(STATE_INFO_BLOCK_START, STATE_INFO_BLOCK_START + info_block_len);
- const info_block_obj = read_info_block(info_block_buffer);
- let state_object = info_block_obj["state"];
- const buffer_infos = info_block_obj["buffer_infos"];
- let buffer_block_start = STATE_INFO_BLOCK_START + info_block_len;
- buffer_block_start = buffer_block_start + 3 & ~3;
- const buffers = buffer_infos.map(buffer_info => {
- const offset = buffer_block_start + buffer_info.offset;
- return state.buffer.slice(offset, offset + buffer_info.length);
- });
- state_object = restore_buffers(state_object, buffers);
- this.set_state(state_object);
- }
- };
|