OpenTally/html/worker.js

149 lines
4.3 KiB
JavaScript
Raw Normal View History

importScripts('opentally.js');
var wasm = wasm_bindgen;
2021-07-27 14:57:53 +02:00
var wasmRaw;
// For asyncify
const DATA_ADDR = 16;
const DATA_START = DATA_ADDR + 8;
const DATA_END = 50 * 1024; // Needs to be increased compared with Asyncify default
async function initWasm() {
2021-07-27 14:57:53 +02:00
wasmRaw = await wasm_bindgen('opentally_async.wasm');
new Int32Array(wasmRaw.memory.buffer, DATA_ADDR).set([DATA_START, DATA_END]);
postMessage({'type': 'init', 'version': wasm.version()});
}
initWasm();
2021-08-16 10:48:49 +02:00
var reportStyle;
var numbers, election, opts, state, stageNum;
onmessage = function(evt) {
2021-07-31 07:24:23 +02:00
try {
if (evt.data.type === 'countElection') {
errored = false;
if (evt.data.numbers === 'fixed') {
numbers = 'Fixed';
wasm.fixed_set_dps(evt.data.decimals);
} else if (evt.data.numbers === 'gfixed') {
numbers = 'GuardedFixed';
wasm.gfixed_set_dps(evt.data.decimals);
} else if (evt.data.numbers === 'float64') {
numbers = 'NativeFloat64';
} else if (evt.data.numbers === 'rational') {
numbers = 'Rational';
} else {
throw 'Unknown --numbers';
}
2021-08-16 10:48:49 +02:00
reportStyle = evt.data.reportStyle;
2021-07-31 07:24:23 +02:00
// Init election
election = wasm['election_from_blt_' + numbers](evt.data.bltData);
2021-09-03 15:53:15 +02:00
// Normalise ballots if requested
2021-07-31 07:24:23 +02:00
if (evt.data.normaliseBallots) {
wasm['election_normalise_ballots_' + numbers](election);
}
2021-09-03 15:53:15 +02:00
// Process equal rankings
wasm['election_realise_equal_rankings_' + numbers](election);
2021-07-31 07:24:23 +02:00
// Init constraints if applicable
if (evt.data.conData) {
wasm['election_load_constraints_' + numbers](election, evt.data.conData);
}
// Init STV options
opts = wasm.STVOptions.new.apply(null, evt.data.optsStr);
// Validate options
opts.validate();
// Describe count
postMessage({'type': 'describeCount', 'content': wasm['describe_count_' + numbers](evt.data.bltPath, election, opts)});
// Init results table
2021-08-16 10:48:49 +02:00
postMessage({'type': 'initResultsTable', 'content': wasm['init_results_table_' + numbers](election, opts, reportStyle)});
2021-07-31 07:24:23 +02:00
// Step election
state = wasm['CountState' + numbers].new(election);
wasm['count_init_' + numbers](state, opts);
2021-08-16 10:48:49 +02:00
postMessage({'type': 'updateResultsTable', 'result': wasm['update_results_table_' + numbers](1, state, opts, reportStyle)});
2021-07-31 07:24:23 +02:00
postMessage({'type': 'updateStageComments', 'comment': wasm['update_stage_comments_' + numbers](state)});
stageNum = 2;
resumeCount();
} else if (evt.data.type == 'userInput') {
userInputBuffer = evt.data.response;
// Rewind the stack
// Asyncify will retrace the function calls in the stack until again reaching get_user_input
wasmRaw.asyncify_start_rewind(DATA_ADDR);
resumeCount();
2021-06-04 10:59:50 +02:00
}
2021-07-31 07:24:23 +02:00
} catch (ex) {
if (errored) {
// Panic already logged and sent to UI
} else {
throw ex;
2021-06-27 14:09:34 +02:00
}
}
}
2021-07-27 14:57:53 +02:00
function resumeCount() {
for (;; stageNum++) {
2021-07-27 14:57:53 +02:00
let isDone = wasm['count_one_stage_' + numbers](state, opts);
2021-07-28 08:03:34 +02:00
if (wasmRaw.asyncify_get_state() !== 0) {
2021-07-27 14:57:53 +02:00
// This stage caused a stack unwind in get_user_input so ignore the result
// We will resume execution when a userInput message is received
return;
}
if (isDone) {
break;
}
2021-08-16 10:48:49 +02:00
postMessage({'type': 'updateResultsTable', 'result': wasm['update_results_table_' + numbers](stageNum, state, opts, reportStyle)});
postMessage({'type': 'updateStageComments', 'comment': wasm['update_stage_comments_' + numbers](state)});
}
2021-08-16 10:48:49 +02:00
postMessage({'type': 'updateResultsTable', 'result': wasm['finalise_results_table_' + numbers](state, reportStyle)});
2021-06-16 05:00:54 +02:00
postMessage({'type': 'finalResultSummary', 'summary': wasm['final_result_summary_' + numbers](state, opts)});
}
2021-07-31 07:24:23 +02:00
var errored = false;
function wasm_error(message) {
postMessage({'type': 'errorMessage', 'message': message});
errored = true;
}
2021-07-27 14:57:53 +02:00
var userInputBuffer = null;
2021-07-27 14:57:53 +02:00
function get_user_input(message) {
if (userInputBuffer === null) {
postMessage({'type': 'requireInput', 'message': message});
2021-07-27 14:57:53 +02:00
// Record the current state of the stack
wasmRaw.asyncify_start_unwind(DATA_ADDR);
// No further WebAssembly will be executed and control will return to resumeCount
return null;
} else {
2021-07-27 14:57:53 +02:00
// We have reached the point the stack was originally unwound, so resume normal execution
wasmRaw.asyncify_stop_rewind();
// Return the correct result to WebAssembly
let userInput = userInputBuffer;
userInputBuffer = null;
return userInput;
}
}