sen-ether-client 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/API.md +239 -0
- package/LICENSE +21 -0
- package/README.md +227 -0
- package/bin/node-sen-probe.js +426 -0
- package/bin/node-sen-scan.js +77 -0
- package/index.js +75 -0
- package/lib/bus.js +740 -0
- package/lib/client.js +634 -0
- package/lib/codec.js +501 -0
- package/lib/crc32.js +26 -0
- package/lib/discovery.js +439 -0
- package/lib/hash32.js +40 -0
- package/lib/protocol/generated.js +157 -0
- package/lib/sen.js +1346 -0
- package/lib/values.js +421 -0
- package/package.json +31 -0
- package/resources/protocol/ether/discovery.stl +19 -0
- package/resources/protocol/ether/runtime.stl +40 -0
- package/resources/protocol/kernel/basic_types.stl +274 -0
- package/resources/protocol/kernel/bus_protocol.stl +198 -0
- package/resources/protocol/kernel/type_specs.stl +554 -0
- package/resources/protocol/protocol.json +15 -0
- package/scripts/generate-protocol.mjs +111 -0
package/lib/codec.js
ADDED
|
@@ -0,0 +1,501 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CPU_ARCH,
|
|
3
|
+
ETHER_CONTROL_MESSAGE_KEY,
|
|
4
|
+
ETHER_PROTOCOL_VERSION,
|
|
5
|
+
KERNEL_PROTOCOL_VERSION,
|
|
6
|
+
OS_KIND
|
|
7
|
+
} from './protocol/generated.js';
|
|
8
|
+
|
|
9
|
+
const textDecoder = new TextDecoder();
|
|
10
|
+
const textEncoder = new TextEncoder();
|
|
11
|
+
|
|
12
|
+
export { ETHER_CONTROL_MESSAGE_KEY, ETHER_PROTOCOL_VERSION, KERNEL_PROTOCOL_VERSION };
|
|
13
|
+
|
|
14
|
+
export const PROCESS_MESSAGE_CATEGORY = Object.freeze({
|
|
15
|
+
busMessage: 0,
|
|
16
|
+
controlMessage: 1
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
function enumCode(name, values, label) {
|
|
20
|
+
const index = values.indexOf(name);
|
|
21
|
+
if (index === -1) {
|
|
22
|
+
throw new TypeError(`unknown SEN ${label}: ${name}`);
|
|
23
|
+
}
|
|
24
|
+
return index;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function controlTypeFromKey(key) {
|
|
28
|
+
for (const [type, value] of Object.entries(ETHER_CONTROL_MESSAGE_KEY)) {
|
|
29
|
+
if (value === key) {
|
|
30
|
+
return type;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Minimal little-endian reader compatible with SEN InputStream.
|
|
38
|
+
*/
|
|
39
|
+
export class SenBinaryReader {
|
|
40
|
+
/**
|
|
41
|
+
* @param {Buffer | Uint8Array | ArrayBuffer} buffer
|
|
42
|
+
*/
|
|
43
|
+
constructor(buffer) {
|
|
44
|
+
this.buffer = Buffer.isBuffer(buffer) ? buffer : Buffer.from(buffer);
|
|
45
|
+
this.offset = 0;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
remaining() {
|
|
49
|
+
return this.buffer.length - this.offset;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
ensure(size) {
|
|
53
|
+
if (this.remaining() < size) {
|
|
54
|
+
throw new RangeError(`SEN buffer underflow at ${this.offset}; need ${size} bytes`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
readUInt8() {
|
|
59
|
+
this.ensure(1);
|
|
60
|
+
const value = this.buffer.readUInt8(this.offset);
|
|
61
|
+
this.offset += 1;
|
|
62
|
+
return value;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
readUInt16() {
|
|
66
|
+
this.ensure(2);
|
|
67
|
+
const value = this.buffer.readUInt16LE(this.offset);
|
|
68
|
+
this.offset += 2;
|
|
69
|
+
return value;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
readInt16() {
|
|
73
|
+
this.ensure(2);
|
|
74
|
+
const value = this.buffer.readInt16LE(this.offset);
|
|
75
|
+
this.offset += 2;
|
|
76
|
+
return value;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
readUInt32() {
|
|
80
|
+
this.ensure(4);
|
|
81
|
+
const value = this.buffer.readUInt32LE(this.offset);
|
|
82
|
+
this.offset += 4;
|
|
83
|
+
return value;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
readInt32() {
|
|
87
|
+
this.ensure(4);
|
|
88
|
+
const value = this.buffer.readInt32LE(this.offset);
|
|
89
|
+
this.offset += 4;
|
|
90
|
+
return value;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
readUInt64() {
|
|
94
|
+
this.ensure(8);
|
|
95
|
+
const value = this.buffer.readBigUInt64LE(this.offset);
|
|
96
|
+
this.offset += 8;
|
|
97
|
+
return value;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
readInt64() {
|
|
101
|
+
this.ensure(8);
|
|
102
|
+
const value = this.buffer.readBigInt64LE(this.offset);
|
|
103
|
+
this.offset += 8;
|
|
104
|
+
return value;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
readFloat32() {
|
|
108
|
+
this.ensure(4);
|
|
109
|
+
const value = this.buffer.readFloatLE(this.offset);
|
|
110
|
+
this.offset += 4;
|
|
111
|
+
return value;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
readFloat64() {
|
|
115
|
+
this.ensure(8);
|
|
116
|
+
const value = this.buffer.readDoubleLE(this.offset);
|
|
117
|
+
this.offset += 8;
|
|
118
|
+
return value;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
readBool() {
|
|
122
|
+
return this.readUInt8() !== 0;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
readString() {
|
|
126
|
+
const size = this.readUInt32();
|
|
127
|
+
this.ensure(size);
|
|
128
|
+
const value = textDecoder.decode(this.buffer.subarray(this.offset, this.offset + size));
|
|
129
|
+
this.offset += size;
|
|
130
|
+
return value;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
readBuffer() {
|
|
134
|
+
const size = this.readUInt32();
|
|
135
|
+
this.ensure(size);
|
|
136
|
+
const value = this.buffer.subarray(this.offset, this.offset + size);
|
|
137
|
+
this.offset += size;
|
|
138
|
+
return value;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Minimal little-endian writer compatible with SEN OutputStream.
|
|
144
|
+
*/
|
|
145
|
+
export class SenBinaryWriter {
|
|
146
|
+
constructor() {
|
|
147
|
+
this.chunks = [];
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
writeUInt8(value) {
|
|
151
|
+
const buffer = Buffer.allocUnsafe(1);
|
|
152
|
+
buffer.writeUInt8(value);
|
|
153
|
+
this.chunks.push(buffer);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
writeUInt16(value) {
|
|
157
|
+
const buffer = Buffer.allocUnsafe(2);
|
|
158
|
+
buffer.writeUInt16LE(value);
|
|
159
|
+
this.chunks.push(buffer);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
writeInt16(value) {
|
|
163
|
+
const buffer = Buffer.allocUnsafe(2);
|
|
164
|
+
buffer.writeInt16LE(value);
|
|
165
|
+
this.chunks.push(buffer);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
writeUInt32(value) {
|
|
169
|
+
const buffer = Buffer.allocUnsafe(4);
|
|
170
|
+
buffer.writeUInt32LE(value >>> 0);
|
|
171
|
+
this.chunks.push(buffer);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
writeInt32(value) {
|
|
175
|
+
const buffer = Buffer.allocUnsafe(4);
|
|
176
|
+
buffer.writeInt32LE(value);
|
|
177
|
+
this.chunks.push(buffer);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
writeUInt64(value) {
|
|
181
|
+
const buffer = Buffer.allocUnsafe(8);
|
|
182
|
+
buffer.writeBigUInt64LE(BigInt(value));
|
|
183
|
+
this.chunks.push(buffer);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
writeInt64(value) {
|
|
187
|
+
const buffer = Buffer.allocUnsafe(8);
|
|
188
|
+
buffer.writeBigInt64LE(BigInt(value));
|
|
189
|
+
this.chunks.push(buffer);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
writeFloat32(value) {
|
|
193
|
+
const buffer = Buffer.allocUnsafe(4);
|
|
194
|
+
buffer.writeFloatLE(value);
|
|
195
|
+
this.chunks.push(buffer);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
writeFloat64(value) {
|
|
199
|
+
const buffer = Buffer.allocUnsafe(8);
|
|
200
|
+
buffer.writeDoubleLE(value);
|
|
201
|
+
this.chunks.push(buffer);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
writeBool(value) {
|
|
205
|
+
this.writeUInt8(value ? 1 : 0);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
writeString(value) {
|
|
209
|
+
const encoded = textEncoder.encode(String(value ?? ''));
|
|
210
|
+
this.writeUInt32(encoded.length);
|
|
211
|
+
if (encoded.length !== 0) {
|
|
212
|
+
this.chunks.push(Buffer.from(encoded));
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
writeBuffer(value) {
|
|
217
|
+
const buffer = Buffer.isBuffer(value) ? value : Buffer.from(value ?? []);
|
|
218
|
+
this.writeUInt32(buffer.length);
|
|
219
|
+
if (buffer.length !== 0) {
|
|
220
|
+
this.chunks.push(buffer);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
toBuffer() {
|
|
225
|
+
return Buffer.concat(this.chunks);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* @param {number} value
|
|
231
|
+
* @returns {string}
|
|
232
|
+
*/
|
|
233
|
+
export function uint32ToIpString(value) {
|
|
234
|
+
return [
|
|
235
|
+
(value >>> 24) & 0xff,
|
|
236
|
+
(value >>> 16) & 0xff,
|
|
237
|
+
(value >>> 8) & 0xff,
|
|
238
|
+
value & 0xff
|
|
239
|
+
].join('.');
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* @param {string} value
|
|
244
|
+
* @returns {number}
|
|
245
|
+
*/
|
|
246
|
+
export function ipStringToUint32(value) {
|
|
247
|
+
const parts = String(value).split('.').map(Number);
|
|
248
|
+
if (parts.length !== 4 || parts.some(part => !Number.isInteger(part) || part < 0 || part > 255)) {
|
|
249
|
+
throw new TypeError(`invalid IPv4 address: ${value}`);
|
|
250
|
+
}
|
|
251
|
+
return (((parts[0] << 24) >>> 0) + (parts[1] << 16) + (parts[2] << 8) + parts[3]) >>> 0;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export function readProcessInfo(reader) {
|
|
255
|
+
const hostId = reader.readUInt32();
|
|
256
|
+
const processId = reader.readUInt32();
|
|
257
|
+
const sessionId = reader.readUInt32();
|
|
258
|
+
const sessionName = reader.readString();
|
|
259
|
+
const appName = reader.readString();
|
|
260
|
+
const hostName = reader.readString();
|
|
261
|
+
const osKindCode = reader.readUInt8();
|
|
262
|
+
const osName = reader.readString();
|
|
263
|
+
const cpuArchCode = reader.readUInt8();
|
|
264
|
+
|
|
265
|
+
return {
|
|
266
|
+
hostId,
|
|
267
|
+
processId,
|
|
268
|
+
sessionId,
|
|
269
|
+
sessionName,
|
|
270
|
+
appName,
|
|
271
|
+
hostName,
|
|
272
|
+
osKindCode,
|
|
273
|
+
osKind: OS_KIND[osKindCode] ?? `unknown:${osKindCode}`,
|
|
274
|
+
osName,
|
|
275
|
+
cpuArchCode,
|
|
276
|
+
cpuArch: CPU_ARCH[cpuArchCode] ?? `unknown:${cpuArchCode}`
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
export function writeProcessInfo(writer, info) {
|
|
281
|
+
writer.writeUInt32(info.hostId);
|
|
282
|
+
writer.writeUInt32(info.processId);
|
|
283
|
+
writer.writeUInt32(info.sessionId);
|
|
284
|
+
writer.writeString(info.sessionName);
|
|
285
|
+
writer.writeString(info.appName);
|
|
286
|
+
writer.writeString(info.hostName);
|
|
287
|
+
writer.writeUInt8(info.osKindCode ?? enumCode(info.osKind, OS_KIND, 'OsKind'));
|
|
288
|
+
writer.writeString(info.osName);
|
|
289
|
+
writer.writeUInt8(info.cpuArchCode ?? enumCode(info.cpuArch, CPU_ARCH, 'CpuArch'));
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function readProtocolVersion(reader) {
|
|
293
|
+
return {
|
|
294
|
+
kernel: reader.readUInt32(),
|
|
295
|
+
ether: reader.readUInt32()
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function writeProtocolVersion(writer, version) {
|
|
300
|
+
writer.writeUInt32(version.kernel);
|
|
301
|
+
writer.writeUInt32(version.ether);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function readHello(reader) {
|
|
305
|
+
return {
|
|
306
|
+
info: readProcessInfo(reader),
|
|
307
|
+
udpPort: reader.readUInt16(),
|
|
308
|
+
version: readProtocolVersion(reader)
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function writeHello(writer, value) {
|
|
313
|
+
writeProcessInfo(writer, value.info);
|
|
314
|
+
writer.writeUInt16(value.udpPort);
|
|
315
|
+
writeProtocolVersion(writer, value.version);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function readBusParticipantMessage(reader) {
|
|
319
|
+
return {
|
|
320
|
+
participantId: reader.readUInt32(),
|
|
321
|
+
busId: reader.readUInt32(),
|
|
322
|
+
busName: reader.readString()
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function writeBusParticipantMessage(writer, value) {
|
|
327
|
+
writer.writeUInt32(value.participantId);
|
|
328
|
+
writer.writeUInt32(value.busId);
|
|
329
|
+
writer.writeString(value.busName);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Encode sen.components.ether.ControlMessage.
|
|
334
|
+
*
|
|
335
|
+
* Source schema:
|
|
336
|
+
* components/ether/stl/runtime.stl
|
|
337
|
+
*
|
|
338
|
+
* SEN generated serialization writes a u32 alternative key followed by the
|
|
339
|
+
* selected struct payload.
|
|
340
|
+
*
|
|
341
|
+
* @param {{ type: 'Hello' | 'Ready' | 'BusJoined' | 'BusLeft', value?: object } | { Hello: object } | { Ready: object } | { BusJoined: object } | { BusLeft: object }} message
|
|
342
|
+
*/
|
|
343
|
+
export function encodeEtherControlMessage(message) {
|
|
344
|
+
const type = message.type ?? Object.keys(message).find(key => key in ETHER_CONTROL_MESSAGE_KEY);
|
|
345
|
+
const value = message.value ?? message[type] ?? {};
|
|
346
|
+
|
|
347
|
+
if (!(type in ETHER_CONTROL_MESSAGE_KEY)) {
|
|
348
|
+
throw new TypeError(`unknown SEN ether ControlMessage: ${type}`);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const writer = new SenBinaryWriter();
|
|
352
|
+
writer.writeUInt32(ETHER_CONTROL_MESSAGE_KEY[type]);
|
|
353
|
+
|
|
354
|
+
switch (type) {
|
|
355
|
+
case 'Hello':
|
|
356
|
+
writeHello(writer, value);
|
|
357
|
+
break;
|
|
358
|
+
case 'Ready':
|
|
359
|
+
break;
|
|
360
|
+
case 'BusJoined':
|
|
361
|
+
case 'BusLeft':
|
|
362
|
+
writeBusParticipantMessage(writer, value);
|
|
363
|
+
break;
|
|
364
|
+
default:
|
|
365
|
+
throw new TypeError(`unhandled SEN ether ControlMessage: ${type}`);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
return writer.toBuffer();
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Decode sen.components.ether.ControlMessage.
|
|
373
|
+
*
|
|
374
|
+
* @param {Buffer | Uint8Array | ArrayBuffer} buffer
|
|
375
|
+
*/
|
|
376
|
+
export function decodeEtherControlMessage(buffer) {
|
|
377
|
+
const reader = new SenBinaryReader(buffer);
|
|
378
|
+
const key = reader.readUInt32();
|
|
379
|
+
const type = controlTypeFromKey(key);
|
|
380
|
+
|
|
381
|
+
if (!type) {
|
|
382
|
+
throw new RangeError(`unknown SEN ether ControlMessage key: ${key}`);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
let value = {};
|
|
386
|
+
switch (type) {
|
|
387
|
+
case 'Hello':
|
|
388
|
+
value = readHello(reader);
|
|
389
|
+
break;
|
|
390
|
+
case 'Ready':
|
|
391
|
+
break;
|
|
392
|
+
case 'BusJoined':
|
|
393
|
+
case 'BusLeft':
|
|
394
|
+
value = readBusParticipantMessage(reader);
|
|
395
|
+
break;
|
|
396
|
+
default:
|
|
397
|
+
throw new TypeError(`unhandled SEN ether ControlMessage: ${type}`);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
return {
|
|
401
|
+
type,
|
|
402
|
+
value,
|
|
403
|
+
bytesRead: reader.offset
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* Encode the 5-byte ProcessHandler TCP frame header plus payload.
|
|
409
|
+
*
|
|
410
|
+
* Source implementation:
|
|
411
|
+
* components/ether/src/process_handler.cpp
|
|
412
|
+
*
|
|
413
|
+
* @param {number} category
|
|
414
|
+
* @param {Buffer | Uint8Array | ArrayBuffer} payload
|
|
415
|
+
*/
|
|
416
|
+
export function encodeProcessTcpFrame(category, payload) {
|
|
417
|
+
const body = Buffer.isBuffer(payload) ? payload : Buffer.from(payload ?? []);
|
|
418
|
+
const header = Buffer.allocUnsafe(5);
|
|
419
|
+
header.writeUInt8(category, 0);
|
|
420
|
+
header.writeUInt32LE(body.length, 1);
|
|
421
|
+
return Buffer.concat([header, body]);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* @param {Buffer | Uint8Array | ArrayBuffer} buffer
|
|
426
|
+
*/
|
|
427
|
+
export function decodeProcessTcpHeader(buffer) {
|
|
428
|
+
const frame = Buffer.isBuffer(buffer) ? buffer : Buffer.from(buffer);
|
|
429
|
+
if (frame.length < 5) {
|
|
430
|
+
throw new RangeError(`SEN TCP frame header needs 5 bytes; got ${frame.length}`);
|
|
431
|
+
}
|
|
432
|
+
return {
|
|
433
|
+
category: frame.readUInt8(0),
|
|
434
|
+
payloadSize: frame.readUInt32LE(1)
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
function readEndpoint(reader) {
|
|
439
|
+
const ip = reader.readUInt32();
|
|
440
|
+
const port = reader.readUInt16();
|
|
441
|
+
return {
|
|
442
|
+
ip,
|
|
443
|
+
host: uint32ToIpString(ip),
|
|
444
|
+
port
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
function writeEndpoint(writer, endpoint) {
|
|
449
|
+
writer.writeUInt32(endpoint.ip ?? ipStringToUint32(endpoint.host));
|
|
450
|
+
writer.writeUInt16(endpoint.port);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* Decode sen.components.ether.SessionPresenceBeam.
|
|
455
|
+
*
|
|
456
|
+
* Source schema:
|
|
457
|
+
* components/ether/stl/discovery.stl
|
|
458
|
+
*
|
|
459
|
+
* @param {Buffer | Uint8Array | ArrayBuffer} buffer
|
|
460
|
+
*/
|
|
461
|
+
export function decodeSessionPresenceBeam(buffer) {
|
|
462
|
+
const reader = new SenBinaryReader(buffer);
|
|
463
|
+
const protocolVersion = reader.readUInt16();
|
|
464
|
+
const info = readProcessInfo(reader);
|
|
465
|
+
const beamPeriodNs = reader.readInt64();
|
|
466
|
+
const endpointCount = reader.readUInt32();
|
|
467
|
+
const endpoints = [];
|
|
468
|
+
|
|
469
|
+
for (let i = 0; i < endpointCount; i += 1) {
|
|
470
|
+
endpoints.push(readEndpoint(reader));
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
return {
|
|
474
|
+
protocolVersion,
|
|
475
|
+
info,
|
|
476
|
+
beamPeriodNs,
|
|
477
|
+
beamPeriodMs: Number(beamPeriodNs) / 1_000_000,
|
|
478
|
+
endpoints,
|
|
479
|
+
bytesRead: reader.offset
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* Encode sen.components.ether.SessionPresenceBeam. Mostly used by tests and
|
|
485
|
+
* future active discovery mode.
|
|
486
|
+
*
|
|
487
|
+
* @param {object} beam
|
|
488
|
+
*/
|
|
489
|
+
export function encodeSessionPresenceBeam(beam) {
|
|
490
|
+
const writer = new SenBinaryWriter();
|
|
491
|
+
writer.writeUInt16(beam.protocolVersion);
|
|
492
|
+
writeProcessInfo(writer, beam.info);
|
|
493
|
+
writer.writeInt64(beam.beamPeriodNs);
|
|
494
|
+
writer.writeUInt32(beam.endpoints?.length ?? 0);
|
|
495
|
+
|
|
496
|
+
for (const endpoint of beam.endpoints ?? []) {
|
|
497
|
+
writeEndpoint(writer, endpoint);
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
return writer.toBuffer();
|
|
501
|
+
}
|
package/lib/crc32.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const table = new Uint32Array(256);
|
|
2
|
+
|
|
3
|
+
for (let i = 0; i < table.length; i += 1) {
|
|
4
|
+
let value = i;
|
|
5
|
+
for (let bit = 0; bit < 8; bit += 1) {
|
|
6
|
+
value = (value & 1) ? ((value >>> 1) ^ 0xedb88320) : (value >>> 1);
|
|
7
|
+
}
|
|
8
|
+
table[i] = value >>> 0;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* CRC32 compatible with sen::crc32 from sen/core/base/hash32.h.
|
|
13
|
+
*
|
|
14
|
+
* @param {string | Buffer | Uint8Array} input
|
|
15
|
+
* @returns {number}
|
|
16
|
+
*/
|
|
17
|
+
export function crc32(input) {
|
|
18
|
+
const bytes = typeof input === 'string' ? new TextEncoder().encode(input) : input;
|
|
19
|
+
let checksum = 0xffffffff;
|
|
20
|
+
|
|
21
|
+
for (const byte of bytes) {
|
|
22
|
+
checksum = table[(checksum ^ byte) & 0xff] ^ (checksum >>> 8);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return (~checksum) >>> 0;
|
|
26
|
+
}
|