SandboXP

Форк
0
/
state.js 
325 строк · 9.2 Кб
1
"use strict";
2

3
/** @const */
4
var STATE_VERSION = 6;
5

6
/** @const */
7
var STATE_MAGIC = 0x86768676|0;
8

9
/** @const */
10
var STATE_INDEX_MAGIC = 0;
11

12
/** @const */
13
var STATE_INDEX_VERSION = 1;
14

15
/** @const */
16
var STATE_INDEX_TOTAL_LEN = 2;
17

18
/** @const */
19
var STATE_INDEX_INFO_LEN = 3;
20

21
/** @const */
22
var STATE_INFO_BLOCK_START = 16;
23

24
const ZSTD_MAGIC = 0xFD2FB528;
25

26
/** @constructor */
27
function StateLoadError(msg)
28
{
29
    this.message = msg;
30
}
31
StateLoadError.prototype = new Error;
32

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,
42
};
43

44
function save_object(obj, saved_buffers)
45
{
46
    if(typeof obj !== "object" || obj === null)
47
    {
48
        dbg_assert(typeof obj !== "function");
49
        return obj;
50
    }
51

52
    if(obj instanceof Array)
53
    {
54
        return obj.map(x => save_object(x, saved_buffers));
55
    }
56

57
    if(obj.constructor === Object)
58
    {
59
        console.log(obj);
60
        dbg_assert(obj.constructor !== Object, "Expected non-object");
61
    }
62

63
    if(obj.BYTES_PER_ELEMENT)
64
    {
65
        // Uint8Array, etc.
66
        var buffer = new Uint8Array(obj.buffer, obj.byteOffset, obj.length * obj.BYTES_PER_ELEMENT);
67

68
        const constructor = obj.constructor.name.replace("bound ", "");
69

70
        dbg_assert(CONSTRUCTOR_TABLE[constructor]);
71

72
        return {
73
            "__state_type__": constructor,
74
            "buffer_id": saved_buffers.push(buffer) - 1,
75
        };
76
    }
77

78
    if(DEBUG && !obj.get_state)
79
    {
80
        console.log("Object without get_state: ", obj);
81
    }
82

83
    var state = obj.get_state();
84
    var result = [];
85

86
    for(var i = 0; i < state.length; i++)
87
    {
88
        var value = state[i];
89

90
        dbg_assert(typeof value !== "function");
91

92
        result[i] = save_object(value, saved_buffers);
93
    }
94

95
    return result;
96
}
97

98
function restore_buffers(obj, buffers)
99
{
100
    if(typeof obj !== "object" || obj === null)
101
    {
102
        dbg_assert(typeof obj !== "function");
103
        return obj;
104
    }
105

106
    if(obj instanceof Array)
107
    {
108
        for(let i = 0; i < obj.length; i++)
109
        {
110
            obj[i] = restore_buffers(obj[i], buffers);
111
        }
112

113
        return obj;
114
    }
115

116
    const type = obj["__state_type__"];
117
    dbg_assert(type !== undefined);
118

119
    const constructor = CONSTRUCTOR_TABLE[type];
120
    dbg_assert(constructor, "Unkown type: " + type);
121

122
    const buffer = buffers[obj["buffer_id"]];
123
    return new constructor(buffer);
124
}
125

126
CPU.prototype.save_state = function()
127
{
128
    var saved_buffers = [];
129
    var state = save_object(this, saved_buffers);
130

131
    var buffer_infos = [];
132
    var total_buffer_size = 0;
133

134
    for(var i = 0; i < saved_buffers.length; i++)
135
    {
136
        var len = saved_buffers[i].byteLength;
137

138
        buffer_infos[i] = {
139
            offset: total_buffer_size,
140
            length: len,
141
        };
142

143
        total_buffer_size += len;
144

145
        // align
146
        total_buffer_size = total_buffer_size + 3 & ~3;
147
    }
148

149
    var info_object = JSON.stringify({
150
        "buffer_infos": buffer_infos,
151
        "state": state,
152
    });
153
    var info_block = new TextEncoder().encode(info_object);
154

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;
158

159
    //console.log("State: json_size=" + Math.ceil(buffer_block_start / 1024 / 1024) + "MB " +
160
    //               "buffer_size=" + Math.ceil(total_buffer_size / 1024 / 1024) + "MB");
161

162
    var result = new ArrayBuffer(total_size);
163

164
    var header_block = new Int32Array(
165
        result,
166
        0,
167
        STATE_INFO_BLOCK_START / 4
168
    );
169
    new Uint8Array(result, STATE_INFO_BLOCK_START, info_block.length).set(info_block);
170
    var buffer_block = new Uint8Array(
171
        result,
172
        buffer_block_start
173
    );
174

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;
179

180
    for(var i = 0; i < saved_buffers.length; i++)
181
    {
182
        var buffer = saved_buffers[i];
183
        dbg_assert(buffer.constructor === Uint8Array);
184
        buffer_block.set(buffer, buffer_infos[i].offset);
185
    }
186

187
    dbg_log("State: json size " + (info_block.byteLength >> 10) + "k");
188
    dbg_log("State: Total buffers size " + (buffer_block.byteLength >> 10) + "k");
189

190
    return result;
191
};
192

193
CPU.prototype.restore_state = function(state)
194
{
195
    state = new Uint8Array(state);
196

197
    function read_state_header(state, check_length)
198
    {
199
        const len = state.length;
200

201
        if(len < STATE_INFO_BLOCK_START)
202
        {
203
            throw new StateLoadError("Invalid length: " + len);
204
        }
205

206
        const header_block = new Int32Array(state.buffer, state.byteOffset, 4);
207

208
        if(header_block[STATE_INDEX_MAGIC] !== STATE_MAGIC)
209
        {
210
            throw new StateLoadError("Invalid header: " + h(header_block[STATE_INDEX_MAGIC] >>> 0));
211
        }
212

213
        if(header_block[STATE_INDEX_VERSION] !== STATE_VERSION)
214
        {
215
            throw new StateLoadError(
216
                    "Version mismatch: dump=" + header_block[STATE_INDEX_VERSION] +
217
                    " we=" + STATE_VERSION);
218
        }
219

220
        if(check_length && header_block[STATE_INDEX_TOTAL_LEN] !== len)
221
        {
222
            throw new StateLoadError(
223
                    "Length doesn't match header: " +
224
                    "real=" + len + " header=" + header_block[STATE_INDEX_TOTAL_LEN]);
225
        }
226

227
        return header_block[STATE_INDEX_INFO_LEN];
228
    }
229

230
    function read_info_block(info_block_buffer)
231
    {
232
        const info_block = new TextDecoder().decode(info_block_buffer);
233
        return JSON.parse(info_block);
234
    }
235

236
    if(new Uint32Array(state.buffer, 0, 1)[0] === ZSTD_MAGIC)
237
    {
238
        const ctx = this.zstd_create_ctx(state.length);
239

240
        new Uint8Array(this.wasm_memory.buffer, this.zstd_get_src_ptr(ctx), state.length).set(state);
241

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);
246

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);
251

252
        let state_object = info_block_obj["state"];
253
        const buffer_infos = info_block_obj["buffer_infos"];
254
        const buffers = [];
255

256
        let position = STATE_INFO_BLOCK_START + info_block_len;
257

258
        for(const buffer_info of buffer_infos)
259
        {
260
            const front_padding = (position + 3 & ~3) - position;
261
            const CHUNK_SIZE = 1 * 1024 * 1024;
262

263
            if(buffer_info.length > CHUNK_SIZE)
264
            {
265
                const ptr = this.zstd_read(ctx, front_padding);
266
                this.zstd_read_free(ptr, front_padding);
267

268
                const buffer = new Uint8Array(buffer_info.length);
269
                buffers.push(buffer.buffer);
270

271
                let have = 0;
272
                while(have < buffer_info.length)
273
                {
274
                    const remaining = buffer_info.length - have;
275
                    dbg_assert(remaining >= 0);
276
                    const to_read = Math.min(remaining, CHUNK_SIZE);
277

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);
281

282
                    have += to_read;
283
                }
284
            }
285
            else
286
            {
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);
291
            }
292

293
            position += front_padding + buffer_info.length;
294
        }
295

296
        state_object = restore_buffers(state_object, buffers);
297
        this.set_state(state_object);
298

299
        this.zstd_free_ctx(ctx);
300
    }
301
    else
302
    {
303
        const info_block_len = read_state_header(state, true);
304

305
        if(info_block_len < 0 || info_block_len + 12 >= state.length)
306
        {
307
            throw new StateLoadError("Invalid info block length: " + info_block_len);
308
        }
309

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;
316

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);
320
        });
321

322
        state_object = restore_buffers(state_object, buffers);
323
        this.set_state(state_object);
324
    }
325
};
326

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.