7
var STATE_MAGIC = 0x86768676|0;
10
var STATE_INDEX_MAGIC = 0;
13
var STATE_INDEX_VERSION = 1;
16
var STATE_INDEX_TOTAL_LEN = 2;
19
var STATE_INDEX_INFO_LEN = 3;
22
var STATE_INFO_BLOCK_START = 16;
24
const ZSTD_MAGIC = 0xFD2FB528;
27
function StateLoadError(msg)
31
StateLoadError.prototype = new Error;
33
const CONSTRUCTOR_TABLE = {
34
"Uint8Array": Uint8Array,
35
"Int8Array": Int8Array,
36
"Uint16Array": Uint16Array,
37
"Int16Array": Int16Array,
38
"Uint32Array": Uint32Array,
39
"Int32Array": Int32Array,
40
"Float32Array": Float32Array,
41
"Float64Array": Float64Array,
44
function save_object(obj, saved_buffers)
46
if(typeof obj !== "object" || obj === null)
48
dbg_assert(typeof obj !== "function");
52
if(obj instanceof Array)
54
return obj.map(x => save_object(x, saved_buffers));
57
if(obj.constructor === Object)
60
dbg_assert(obj.constructor !== Object, "Expected non-object");
63
if(obj.BYTES_PER_ELEMENT)
66
var buffer = new Uint8Array(obj.buffer, obj.byteOffset, obj.length * obj.BYTES_PER_ELEMENT);
68
const constructor = obj.constructor.name.replace("bound ", "");
70
dbg_assert(CONSTRUCTOR_TABLE[constructor]);
73
"__state_type__": constructor,
74
"buffer_id": saved_buffers.push(buffer) - 1,
78
if(DEBUG && !obj.get_state)
80
console.log("Object without get_state: ", obj);
83
var state = obj.get_state();
86
for(var i = 0; i < state.length; i++)
90
dbg_assert(typeof value !== "function");
92
result[i] = save_object(value, saved_buffers);
98
function restore_buffers(obj, buffers)
100
if(typeof obj !== "object" || obj === null)
102
dbg_assert(typeof obj !== "function");
106
if(obj instanceof Array)
108
for(let i = 0; i < obj.length; i++)
110
obj[i] = restore_buffers(obj[i], buffers);
116
const type = obj["__state_type__"];
117
dbg_assert(type !== undefined);
119
const constructor = CONSTRUCTOR_TABLE[type];
120
dbg_assert(constructor, "Unkown type: " + type);
122
const buffer = buffers[obj["buffer_id"]];
123
return new constructor(buffer);
126
CPU.prototype.save_state = function()
128
var saved_buffers = [];
129
var state = save_object(this, saved_buffers);
131
var buffer_infos = [];
132
var total_buffer_size = 0;
134
for(var i = 0; i < saved_buffers.length; i++)
136
var len = saved_buffers[i].byteLength;
139
offset: total_buffer_size,
143
total_buffer_size += len;
146
total_buffer_size = total_buffer_size + 3 & ~3;
149
var info_object = JSON.stringify({
150
"buffer_infos": buffer_infos,
153
var info_block = new TextEncoder().encode(info_object);
155
var buffer_block_start = STATE_INFO_BLOCK_START + info_block.length;
156
buffer_block_start = buffer_block_start + 3 & ~3;
157
var total_size = buffer_block_start + total_buffer_size;
162
var result = new ArrayBuffer(total_size);
164
var header_block = new Int32Array(
167
STATE_INFO_BLOCK_START / 4
169
new Uint8Array(result, STATE_INFO_BLOCK_START, info_block.length).set(info_block);
170
var buffer_block = new Uint8Array(
175
header_block[STATE_INDEX_MAGIC] = STATE_MAGIC;
176
header_block[STATE_INDEX_VERSION] = STATE_VERSION;
177
header_block[STATE_INDEX_TOTAL_LEN] = total_size;
178
header_block[STATE_INDEX_INFO_LEN] = info_block.length;
180
for(var i = 0; i < saved_buffers.length; i++)
182
var buffer = saved_buffers[i];
183
dbg_assert(buffer.constructor === Uint8Array);
184
buffer_block.set(buffer, buffer_infos[i].offset);
187
dbg_log("State: json size " + (info_block.byteLength >> 10) + "k");
188
dbg_log("State: Total buffers size " + (buffer_block.byteLength >> 10) + "k");
193
CPU.prototype.restore_state = function(state)
195
state = new Uint8Array(state);
197
function read_state_header(state, check_length)
199
const len = state.length;
201
if(len < STATE_INFO_BLOCK_START)
203
throw new StateLoadError("Invalid length: " + len);
206
const header_block = new Int32Array(state.buffer, state.byteOffset, 4);
208
if(header_block[STATE_INDEX_MAGIC] !== STATE_MAGIC)
210
throw new StateLoadError("Invalid header: " + h(header_block[STATE_INDEX_MAGIC] >>> 0));
213
if(header_block[STATE_INDEX_VERSION] !== STATE_VERSION)
215
throw new StateLoadError(
216
"Version mismatch: dump=" + header_block[STATE_INDEX_VERSION] +
217
" we=" + STATE_VERSION);
220
if(check_length && header_block[STATE_INDEX_TOTAL_LEN] !== len)
222
throw new StateLoadError(
223
"Length doesn't match header: " +
224
"real=" + len + " header=" + header_block[STATE_INDEX_TOTAL_LEN]);
227
return header_block[STATE_INDEX_INFO_LEN];
230
function read_info_block(info_block_buffer)
232
const info_block = new TextDecoder().decode(info_block_buffer);
233
return JSON.parse(info_block);
236
if(new Uint32Array(state.buffer, 0, 1)[0] === ZSTD_MAGIC)
238
const ctx = this.zstd_create_ctx(state.length);
240
new Uint8Array(this.wasm_memory.buffer, this.zstd_get_src_ptr(ctx), state.length).set(state);
242
let ptr = this.zstd_read(ctx, 16);
243
const header_block = new Uint8Array(this.wasm_memory.buffer, ptr, 16);
244
const info_block_len = read_state_header(header_block, false);
245
this.zstd_read_free(ptr, 16);
247
ptr = this.zstd_read(ctx, info_block_len);
248
const info_block_buffer = new Uint8Array(this.wasm_memory.buffer, ptr, info_block_len);
249
const info_block_obj = read_info_block(info_block_buffer);
250
this.zstd_read_free(ptr, info_block_len);
252
let state_object = info_block_obj["state"];
253
const buffer_infos = info_block_obj["buffer_infos"];
256
let position = STATE_INFO_BLOCK_START + info_block_len;
258
for(const buffer_info of buffer_infos)
260
const front_padding = (position + 3 & ~3) - position;
261
const CHUNK_SIZE = 1 * 1024 * 1024;
263
if(buffer_info.length > CHUNK_SIZE)
265
const ptr = this.zstd_read(ctx, front_padding);
266
this.zstd_read_free(ptr, front_padding);
268
const buffer = new Uint8Array(buffer_info.length);
269
buffers.push(buffer.buffer);
272
while(have < buffer_info.length)
274
const remaining = buffer_info.length - have;
275
dbg_assert(remaining >= 0);
276
const to_read = Math.min(remaining, CHUNK_SIZE);
278
const ptr = this.zstd_read(ctx, to_read);
279
buffer.set(new Uint8Array(this.wasm_memory.buffer, ptr, to_read), have);
280
this.zstd_read_free(ptr, to_read);
287
const ptr = this.zstd_read(ctx, front_padding + buffer_info.length);
288
const offset = ptr + front_padding;
289
buffers.push(this.wasm_memory.buffer.slice(offset, offset + buffer_info.length));
290
this.zstd_read_free(ptr, front_padding + buffer_info.length);
293
position += front_padding + buffer_info.length;
296
state_object = restore_buffers(state_object, buffers);
297
this.set_state(state_object);
299
this.zstd_free_ctx(ctx);
303
const info_block_len = read_state_header(state, true);
305
if(info_block_len < 0 || info_block_len + 12 >= state.length)
307
throw new StateLoadError("Invalid info block length: " + info_block_len);
310
const info_block_buffer = state.subarray(STATE_INFO_BLOCK_START, STATE_INFO_BLOCK_START + info_block_len);
311
const info_block_obj = read_info_block(info_block_buffer);
312
let state_object = info_block_obj["state"];
313
const buffer_infos = info_block_obj["buffer_infos"];
314
let buffer_block_start = STATE_INFO_BLOCK_START + info_block_len;
315
buffer_block_start = buffer_block_start + 3 & ~3;
317
const buffers = buffer_infos.map(buffer_info => {
318
const offset = buffer_block_start + buffer_info.offset;
319
return state.buffer.slice(offset, offset + buffer_info.length);
322
state_object = restore_buffers(state_object, buffers);
323
this.set_state(state_object);