-
-
Notifications
You must be signed in to change notification settings - Fork 34
Expand file tree
/
Copy pathsab-ipc.js
More file actions
139 lines (121 loc) · 4.11 KB
/
Copy pathsab-ipc.js
File metadata and controls
139 lines (121 loc) · 4.11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
'use strict';
const { parseCamaroJson } = require('./json-parse');
const FN = { transform: 0, toJson: 1, prettyPrint: 2 };
const CTRL = {
STATE: 0,
REQUEST_ID: 1,
XML_LEN: 2,
RESULT_LEN: 3,
FN: 4,
HAS_NAN: 5,
};
const STATE = { IDLE: 0, BUSY: 1, DONE: 2, ERROR: -1 };
const textEncoder = typeof TextEncoder !== 'undefined' ? new TextEncoder() : null;
const textDecoder = typeof TextDecoder !== 'undefined' ? new TextDecoder() : null;
function sabEnabled() {
if (typeof SharedArrayBuffer === 'undefined') return false;
const raw = process.env.CAMARO_SAB_IPC;
if (raw === '0' || raw === 'false' || raw === 'off') return false;
return true;
}
function defaultXmlSabBytes() {
const n = Number(process.env.CAMARO_SAB_XML_BYTES || 0);
return n > 0 ? n : 512 * 1024;
}
function defaultResultSabBytes() {
const n = Number(process.env.CAMARO_SAB_RESULT_BYTES || 0);
return n > 0 ? n : 256 * 1024;
}
function fitsSabXmlPayload(xml) {
const max = defaultXmlSabBytes();
if (typeof xml === 'string') return xml.length * 3 <= max;
if (typeof Buffer !== 'undefined' && Buffer.isBuffer(xml)) return xml.length <= max;
if (xml instanceof ArrayBuffer) return xml.byteLength <= max;
if (ArrayBuffer.isView(xml)) return xml.byteLength <= max;
return false;
}
function createSabChannel() {
return {
control: new Int32Array(new SharedArrayBuffer(32)),
xml: new Uint8Array(new SharedArrayBuffer(defaultXmlSabBytes())),
result: new Uint8Array(new SharedArrayBuffer(defaultResultSabBytes())),
};
}
function fnCode(fn) {
if (fn === 'transform') return FN.transform;
if (fn === 'toJson') return FN.toJson;
if (fn === 'prettyPrint') return FN.prettyPrint;
return -1;
}
function writeBytesToSab(channel, bytes) {
if (bytes.byteLength > channel.xml.byteLength) {
throw new RangeError(`XML payload (${bytes.byteLength} bytes) exceeds CAMARO_SAB_XML_BYTES (${channel.xml.byteLength})`);
}
channel.xml.set(bytes);
Atomics.store(channel.control, CTRL.XML_LEN, bytes.byteLength);
}
function writeStringXmlToSab(channel, xml) {
if (!textEncoder) {
return writeBytesToSab(channel, new Uint8Array(Buffer.from(xml, 'utf8')));
}
const worstCase = xml.length * 3;
if (worstCase > channel.xml.byteLength) {
throw new RangeError(`XML payload (> ${worstCase} bytes) exceeds CAMARO_SAB_XML_BYTES (${channel.xml.byteLength})`);
}
const { written } = textEncoder.encodeInto(xml, channel.xml);
Atomics.store(channel.control, CTRL.XML_LEN, written);
}
function writeResultToSab(channel, payload) {
let str;
let hasNan = 0;
if (typeof payload === 'string') {
str = payload;
} else if (payload && typeof payload === 'object' && typeof payload.json === 'string') {
str = payload.json;
hasNan = 1;
} else {
str = JSON.stringify(payload);
}
if (textEncoder) {
const { read, written } = textEncoder.encodeInto(str, channel.result);
if (read !== str.length) {
throw new RangeError(`Result exceeds CAMARO_SAB_RESULT_BYTES (${channel.result.byteLength})`);
}
Atomics.store(channel.control, CTRL.RESULT_LEN, written);
} else {
const encoded = new Uint8Array(Buffer.from(str, 'utf8'));
if (encoded.byteLength > channel.result.byteLength) {
throw new RangeError(`Result (${encoded.byteLength} bytes) exceeds CAMARO_SAB_RESULT_BYTES (${channel.result.byteLength})`);
}
channel.result.set(encoded);
Atomics.store(channel.control, CTRL.RESULT_LEN, encoded.byteLength);
}
Atomics.store(channel.control, CTRL.HAS_NAN, hasNan);
return hasNan === 1;
}
function readResultFromSab(channel) {
const len = Atomics.load(channel.control, CTRL.RESULT_LEN);
const hasNan = Atomics.load(channel.control, CTRL.HAS_NAN) === 1;
const slice = channel.result.subarray(0, len);
const str = textDecoder ? textDecoder.decode(slice) : Buffer.from(slice).toString('utf8');
return parseCamaroJson(hasNan ? { json: str, hasNan: true } : str);
}
function stripXmlFromTask(task) {
const args = task.args.slice();
args[0] = null;
return { fn: task.fn, args, recycleXml: false };
}
module.exports = {
FN,
CTRL,
STATE,
sabEnabled,
createSabChannel,
fnCode,
writeBytesToSab,
writeStringXmlToSab,
writeResultToSab,
readResultFromSab,
fitsSabXmlPayload,
stripXmlFromTask,
};