layout-data 0.2.0 → 0.3.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/CHANGELOG.md +30 -0
- package/README.md +1 -3
- package/dist/types/Ldata.d.ts +16 -5
- package/package.json +10 -8
- package/src/Ldata.js +541 -0
- package/src/Ltag.js +87 -0
- package/src/codecs.js +64 -0
- package/src/dispatch.js +23 -0
- package/src/endian.js +197 -0
- package/src/index.js +8 -0
- package/src/internal_types.js +47 -0
- package/src/layout.js +303 -0
- package/src/observer.js +147 -0
- package/src/registry.js +83 -0
- package/src/reserved.js +35 -0
- package/src/structure.js +238 -0
- package/src/typedefs.js +143 -0
- package/src/types.js +74 -0
- package/dist/layout-data.js +0 -8320
package/src/Ltag.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { EventEmitter } from "node:events";
|
|
2
|
+
|
|
3
|
+
/** @import { Endian, Tag_Listener, Tag_Type } from "./typedefs.js" */
|
|
4
|
+
/** @import { Layout_Entry } from "./internal_types.js" */
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The interface of one tag of an Ldata (doc/spec-data-api.md §3). It holds no
|
|
8
|
+
* value: reading `value` decodes it from the buffer of the Ldata, writing it
|
|
9
|
+
* is the same as `ldata[name] = value`, and `load` / `store` are `load_tag` /
|
|
10
|
+
* `store_tag` of the Ldata for the name of the tag. `change` events
|
|
11
|
+
* (old_value, new_value) are emitted by the Observer of the Ldata.
|
|
12
|
+
*/
|
|
13
|
+
export class Ltag extends EventEmitter {
|
|
14
|
+
/** @type {import("./Ldata.js").Ldata} */
|
|
15
|
+
#ldata;
|
|
16
|
+
/** @type {Layout_Entry} */
|
|
17
|
+
#entry;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @param {import("./Ldata.js").Ldata} ldata - The Ldata the tag belongs to.
|
|
21
|
+
* @param {Layout_Entry} entry - The compiled entry of the tag.
|
|
22
|
+
*/
|
|
23
|
+
constructor(ldata, entry) {
|
|
24
|
+
super();
|
|
25
|
+
this.#ldata = ldata;
|
|
26
|
+
this.#entry = entry;
|
|
27
|
+
this.setMaxListeners(20);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Subscribes a listener; `change` is typed (old_value, new_value), any
|
|
32
|
+
* listener for another event name (doc/spec-data-api.md §3).
|
|
33
|
+
*
|
|
34
|
+
* @template {string | symbol} E
|
|
35
|
+
* @param {E} event
|
|
36
|
+
* @param {Tag_Listener<E>} listener
|
|
37
|
+
* @returns {this}
|
|
38
|
+
*/
|
|
39
|
+
on(event, listener) { return super.on(event, listener); }
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Subscribes a listener for one emission, typed like `on`.
|
|
43
|
+
*
|
|
44
|
+
* @template {string | symbol} E
|
|
45
|
+
* @param {E} event
|
|
46
|
+
* @param {Tag_Listener<E>} listener
|
|
47
|
+
* @returns {this}
|
|
48
|
+
*/
|
|
49
|
+
once(event, listener) { return super.once(event, listener); }
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Removes a listener, typed like `on`.
|
|
53
|
+
*
|
|
54
|
+
* @template {string | symbol} E
|
|
55
|
+
* @param {E} event
|
|
56
|
+
* @param {Tag_Listener<E>} listener
|
|
57
|
+
* @returns {this}
|
|
58
|
+
*/
|
|
59
|
+
off(event, listener) { return super.off(event, listener); }
|
|
60
|
+
|
|
61
|
+
/** @returns {string} */
|
|
62
|
+
get name() { return this.#entry.name; }
|
|
63
|
+
/** @returns {Tag_Type} Lower case type name. */
|
|
64
|
+
get type() { return this.#entry.type; }
|
|
65
|
+
/** @returns {number} Size in bytes; a Bool has the byte it lies in. */
|
|
66
|
+
get size() { return this.#entry.size; }
|
|
67
|
+
/** @returns {number | boolean} */
|
|
68
|
+
get value() { return this.#ldata[this.#entry.name]; }
|
|
69
|
+
/** @param {number | boolean} value */
|
|
70
|
+
set value(value) { this.#ldata[this.#entry.name] = value; }
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Same as `ldata.load_tag(name, io_buffer, endian)` (doc/spec-data-api.md §2.9).
|
|
74
|
+
*
|
|
75
|
+
* @param {Uint8Array} io_buffer
|
|
76
|
+
* @param {Endian} endian
|
|
77
|
+
*/
|
|
78
|
+
load(io_buffer, endian) { this.#ldata.load_tag(this.#entry.name, io_buffer, endian); }
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Same as `ldata.store_tag(name, io_buffer, endian)` (doc/spec-data-api.md §2.9).
|
|
82
|
+
*
|
|
83
|
+
* @param {Uint8Array} io_buffer
|
|
84
|
+
* @param {Endian} endian
|
|
85
|
+
*/
|
|
86
|
+
store(io_buffer, endian) { this.#ldata.store_tag(this.#entry.name, io_buffer, endian); }
|
|
87
|
+
}
|
package/src/codecs.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// Codecs of the tag types on a big-endian buffer (doc/design.md §4).
|
|
2
|
+
// `byte` is the byte offset of the tag; `mask` is the bit mask of a Bool.
|
|
3
|
+
// Device byte order is handled at the IO boundary, not here.
|
|
4
|
+
|
|
5
|
+
/** The largest finite float32 value; a finite number above it is refused by a Real. */
|
|
6
|
+
export const FLOAT32_MAX = 3.4028234663852886e38;
|
|
7
|
+
|
|
8
|
+
const read_bool = (buffer, byte, mask) => (buffer[byte] & mask) !== 0;
|
|
9
|
+
const write_bool = (buffer, byte, mask, value) => {
|
|
10
|
+
buffer[byte] = value ? buffer[byte] | mask : buffer[byte] & ~mask;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/** A Bool takes any value, by truthiness. */
|
|
14
|
+
const check_bool = () => null;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The check of an integer type: a number that is an integer inside the range.
|
|
18
|
+
*
|
|
19
|
+
* @param {number} min
|
|
20
|
+
* @param {number} max
|
|
21
|
+
* @returns {(value: any) => string | null}
|
|
22
|
+
*/
|
|
23
|
+
const check_integer = (min, max) => (value) => {
|
|
24
|
+
if (typeof value !== "number") return `a ${typeof value} is not a number`;
|
|
25
|
+
if (!Number.isInteger(value)) return `${value} is not an integer`;
|
|
26
|
+
if (value < min || value > max) return `${value} is outside ${min}..${max}`;
|
|
27
|
+
return null;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/** The check of a Real: any number, NaN and the infinities included, but no finite value beyond float32. */
|
|
31
|
+
const check_real = (value) => {
|
|
32
|
+
if (typeof value !== "number") return `a ${typeof value} is not a number`;
|
|
33
|
+
if (Number.isFinite(value) && Math.abs(value) > FLOAT32_MAX) return `${value} is beyond the float32 range`;
|
|
34
|
+
return null;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const check_byte = check_integer(0, 0xff);
|
|
38
|
+
const check_sint = check_integer(-0x80, 0x7f);
|
|
39
|
+
const check_int = check_integer(-0x8000, 0x7fff);
|
|
40
|
+
const check_uint = check_integer(0, 0xffff);
|
|
41
|
+
const check_dint = check_integer(-0x80000000, 0x7fffffff);
|
|
42
|
+
const check_udint = check_integer(0, 0xffffffff);
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Codec of each tag type (lower case): its size in bytes, `check(value)`, which
|
|
46
|
+
* returns null for a value that can be written and the reason otherwise,
|
|
47
|
+
* `get(buffer, byte, mask)` and `set(buffer, byte, mask, value)`. `set` is
|
|
48
|
+
* called with a checked value only (doc/spec-data-api.md §2.3).
|
|
49
|
+
*
|
|
50
|
+
* @type {Record<string, import("./internal_types.js").Codec>}
|
|
51
|
+
*/
|
|
52
|
+
export const CODECS = {
|
|
53
|
+
bool: { size: 1, check: check_bool, get: read_bool, set: write_bool },
|
|
54
|
+
byte: { size: 1, check: check_byte, get: (b, i) => b.readUInt8(i), set: (b, i, _m, v) => b.writeUInt8(v, i) },
|
|
55
|
+
usint: { size: 1, check: check_byte, get: (b, i) => b.readUInt8(i), set: (b, i, _m, v) => b.writeUInt8(v, i) },
|
|
56
|
+
sint: { size: 1, check: check_sint, get: (b, i) => b.readInt8(i), set: (b, i, _m, v) => b.writeInt8(v, i) },
|
|
57
|
+
int: { size: 2, check: check_int, get: (b, i) => b.readInt16BE(i), set: (b, i, _m, v) => b.writeInt16BE(v, i) },
|
|
58
|
+
uint: { size: 2, check: check_uint, get: (b, i) => b.readUInt16BE(i), set: (b, i, _m, v) => b.writeUInt16BE(v, i) },
|
|
59
|
+
word: { size: 2, check: check_uint, get: (b, i) => b.readUInt16BE(i), set: (b, i, _m, v) => b.writeUInt16BE(v, i) },
|
|
60
|
+
dint: { size: 4, check: check_dint, get: (b, i) => b.readInt32BE(i), set: (b, i, _m, v) => b.writeInt32BE(v, i) },
|
|
61
|
+
udint: { size: 4, check: check_udint, get: (b, i) => b.readUInt32BE(i), set: (b, i, _m, v) => b.writeUInt32BE(v, i) },
|
|
62
|
+
dword: { size: 4, check: check_udint, get: (b, i) => b.readUInt32BE(i), set: (b, i, _m, v) => b.writeUInt32BE(v, i) },
|
|
63
|
+
real: { size: 4, check: check_real, get: (b, i) => b.readFloatBE(i), set: (b, i, _m, v) => b.writeFloatBE(v, i) },
|
|
64
|
+
};
|
package/src/dispatch.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Dispatch of events that does not stop at a listener that throws
|
|
2
|
+
// (doc/spec-data-api.md §4.4, doc/design.md §5.1). Internal.
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Calls every listener of an event, in the order they subscribed, and pushes
|
|
6
|
+
* what a listener throws to `errors` instead of letting it stop the others.
|
|
7
|
+
* A `once` listener removes itself as usual.
|
|
8
|
+
*
|
|
9
|
+
* @param {import("node:events").EventEmitter | undefined} emitter - Nothing happens for undefined.
|
|
10
|
+
* @param {string} event
|
|
11
|
+
* @param {any[]} args
|
|
12
|
+
* @param {unknown[]} errors
|
|
13
|
+
*/
|
|
14
|
+
export function emit_all(emitter, event, args, errors) {
|
|
15
|
+
if (!emitter) return;
|
|
16
|
+
for (const listener of emitter.rawListeners(event)) {
|
|
17
|
+
try {
|
|
18
|
+
listener.apply(emitter, args);
|
|
19
|
+
} catch (error) {
|
|
20
|
+
errors.push(error);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
package/src/endian.js
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
// Conversion between the device byte order of an IO buffer and the big-endian
|
|
2
|
+
// buffer of an Ldata, per compiled layout entry (doc/spec-data-api.md §2.9,
|
|
3
|
+
// doc/design.md §6). Device byte order acts per 16-bit register and per 32-bit
|
|
4
|
+
// value; the IO buffer is indexed like the Ldata buffer. Only io_index_table is
|
|
5
|
+
// exported by the package: Ldata calls the other functions with its own buffer.
|
|
6
|
+
import { compiled_of } from "./registry.js";
|
|
7
|
+
|
|
8
|
+
/** @import { Endian, Layout } from "./typedefs.js" */
|
|
9
|
+
/** @import { Layout_Entry } from "./internal_types.js" */
|
|
10
|
+
|
|
11
|
+
// The device byte orders; there are no aliases.
|
|
12
|
+
const ENDIANS = ["BE", "LE", "BEBS", "LEBS"];
|
|
13
|
+
|
|
14
|
+
// Endians that swap the two bytes of a 16-bit register.
|
|
15
|
+
const BYTE_SWAP_ENDIANS = ["LE", "BEBS"];
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Throws for a byte order that is not one of BE, LE, BEBS and LEBS, instead of
|
|
19
|
+
* treating it as BE and corrupting the data silently.
|
|
20
|
+
*
|
|
21
|
+
* @param {string} endian
|
|
22
|
+
* @throws {RangeError}
|
|
23
|
+
*/
|
|
24
|
+
export function check_endian(endian) {
|
|
25
|
+
if (!ENDIANS.includes(endian)) {
|
|
26
|
+
throw new RangeError(`unknown byte order ${endian}: use BE, LE, BEBS or LEBS`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Converts the endianness of a 2-byte value between the specified endian format and Big Endian (BE).
|
|
32
|
+
*
|
|
33
|
+
* This function performs bidirectional conversion:
|
|
34
|
+
* - If the data is not in BE, it converts it to BE.
|
|
35
|
+
* - If the data is in BE, it converts it to the specified endian format.
|
|
36
|
+
*
|
|
37
|
+
* @param {Uint8Array} buffer_from - The source buffer containing the data to be converted.
|
|
38
|
+
* @param {Uint8Array} buffer_to - The destination buffer where the converted data will be written.
|
|
39
|
+
* @param {number} index - The starting index in the buffers for the 2-byte value.
|
|
40
|
+
* @param {Endian} endian - The endian format to convert to/from.
|
|
41
|
+
*/
|
|
42
|
+
function convert_endian_2byte(buffer_from, buffer_to, index, endian) {
|
|
43
|
+
if (BYTE_SWAP_ENDIANS.includes(endian)) { // LE, BEBS: BA
|
|
44
|
+
buffer_to[index] = buffer_from[index + 1];
|
|
45
|
+
buffer_to[index + 1] = buffer_from[index];
|
|
46
|
+
} else { // BE, LEBS: AB
|
|
47
|
+
buffer_to[index] = buffer_from[index];
|
|
48
|
+
buffer_to[index + 1] = buffer_from[index + 1];
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Converts the endianness of a 4-byte value between the specified endian format and Big Endian (BE).
|
|
54
|
+
*
|
|
55
|
+
* This function performs bidirectional conversion:
|
|
56
|
+
* - If the data is not in BE, it converts it to BE.
|
|
57
|
+
* - If the data is in BE, it converts it to the specified endian format.
|
|
58
|
+
*
|
|
59
|
+
* The function handles different endian formats:
|
|
60
|
+
* - LE (Little Endian): DCBA <-> ABCD
|
|
61
|
+
* - BEBS (Big Endian Byte Swap): BADC <-> ABCD
|
|
62
|
+
* - LEBS (Little Endian Byte Swap): CDAB <-> ABCD
|
|
63
|
+
*
|
|
64
|
+
* @param {Uint8Array} buffer_from - The source buffer containing the data to be converted.
|
|
65
|
+
* @param {Uint8Array} buffer_to - The destination buffer where the converted data will be written.
|
|
66
|
+
* @param {number} index - The starting index in the buffers for the 4-byte value.
|
|
67
|
+
* @param {Endian} endian - The endian format to convert to/from.
|
|
68
|
+
*/
|
|
69
|
+
function convert_endian_4bytes(buffer_from, buffer_to, index, endian) {
|
|
70
|
+
switch (endian) {
|
|
71
|
+
case "LE": { // DCBA <-> ABCD
|
|
72
|
+
buffer_to[index] = buffer_from[index + 3];
|
|
73
|
+
buffer_to[index + 1] = buffer_from[index + 2];
|
|
74
|
+
buffer_to[index + 2] = buffer_from[index + 1];
|
|
75
|
+
buffer_to[index + 3] = buffer_from[index];
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
case "BEBS": { // BADC <-> ABCD
|
|
79
|
+
buffer_to[index] = buffer_from[index + 1];
|
|
80
|
+
buffer_to[index + 1] = buffer_from[index];
|
|
81
|
+
buffer_to[index + 2] = buffer_from[index + 3];
|
|
82
|
+
buffer_to[index + 3] = buffer_from[index + 2];
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
case "LEBS": { // CDAB <-> ABCD
|
|
86
|
+
buffer_to[index] = buffer_from[index + 2];
|
|
87
|
+
buffer_to[index + 1] = buffer_from[index + 3];
|
|
88
|
+
buffer_to[index + 2] = buffer_from[index];
|
|
89
|
+
buffer_to[index + 3] = buffer_from[index + 1];
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
default: { // BE
|
|
93
|
+
for (let k = 0; k < 4; k++) buffer_to[index + k] = buffer_from[index + k];
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Index in a device-ordered IO buffer of the byte of a single-byte entry
|
|
100
|
+
* (Bool, Byte, SInt, USInt): when the endian swaps the register bytes, the
|
|
101
|
+
* byte sits in the other half of its register.
|
|
102
|
+
*
|
|
103
|
+
* @param {number} byte - Byte offset of the entry.
|
|
104
|
+
* @param {Endian} endian - Device byte order.
|
|
105
|
+
* @returns {number}
|
|
106
|
+
*/
|
|
107
|
+
export function io_index(byte, endian) {
|
|
108
|
+
return BYTE_SWAP_ENDIANS.includes(endian) ? byte ^ 1 : byte;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* The index in a device-ordered IO buffer of every byte of a data block: the
|
|
113
|
+
* byte `i` of the block is the byte `table[i]` of the IO buffer. The table
|
|
114
|
+
* makes loading and storing a whole block a plain byte copy, and it agrees
|
|
115
|
+
* with `load_entry` / `store_entry` for every entry (a Bool or a single-byte
|
|
116
|
+
* entry maps as `io_index`, a multi-byte entry as its endian).
|
|
117
|
+
*
|
|
118
|
+
* @param {Layout} layout - The compiled layout of the data block.
|
|
119
|
+
* @param {Endian} endian - Device byte order: BE, LE, BEBS or LEBS.
|
|
120
|
+
* @returns {Int32Array}
|
|
121
|
+
* @throws {RangeError} For another byte order.
|
|
122
|
+
*/
|
|
123
|
+
export function io_index_table(layout, endian) {
|
|
124
|
+
check_endian(endian);
|
|
125
|
+
const table = new Int32Array(layout.size);
|
|
126
|
+
for (let i = 0; i < table.length; i++) table[i] = io_index(i, endian);
|
|
127
|
+
for (const { byte, size, type } of compiled_of(layout).entries.values()) {
|
|
128
|
+
if (size !== 4 || type === "bool") continue;
|
|
129
|
+
for (let k = 0; k < 4; k++) {
|
|
130
|
+
switch (endian) {
|
|
131
|
+
case "LE": table[byte + k] = byte + 3 - k; break;
|
|
132
|
+
case "BEBS": table[byte + k] = byte + (k ^ 1); break;
|
|
133
|
+
case "LEBS": table[byte + k] = byte + ((k + 2) % 4); break;
|
|
134
|
+
default: table[byte + k] = byte + k;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return table;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Byte range [start, end) an entry occupies in a device-ordered IO buffer.
|
|
143
|
+
*
|
|
144
|
+
* @param {Layout_Entry} entry
|
|
145
|
+
* @param {Endian} endian - Device byte order.
|
|
146
|
+
* @returns {{start: number, end: number}}
|
|
147
|
+
*/
|
|
148
|
+
export function io_range(entry, endian) {
|
|
149
|
+
const start = entry.size === 1 ? io_index(entry.byte, endian) : entry.byte;
|
|
150
|
+
return { start, end: start + entry.size };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Copies an entry from a device-ordered IO buffer into a big-endian buffer.
|
|
155
|
+
* A Bool only changes its own bit. No change is detected here, and the byte
|
|
156
|
+
* order and the range in the IO buffer are not checked (Ldata does).
|
|
157
|
+
*
|
|
158
|
+
* @param {Layout_Entry} entry
|
|
159
|
+
* @param {Uint8Array} io_buffer - The device-ordered source.
|
|
160
|
+
* @param {Uint8Array} buffer - The big-endian destination.
|
|
161
|
+
* @param {Endian} endian - Device byte order.
|
|
162
|
+
*/
|
|
163
|
+
export function load_entry(entry, io_buffer, buffer, endian) {
|
|
164
|
+
const { byte, size, mask } = entry;
|
|
165
|
+
if (entry.type === "bool") {
|
|
166
|
+
const value = io_buffer[io_index(byte, endian)] & mask;
|
|
167
|
+
buffer[byte] = value ? buffer[byte] | mask : buffer[byte] & ~mask;
|
|
168
|
+
} else if (size === 1) {
|
|
169
|
+
buffer[byte] = io_buffer[io_index(byte, endian)];
|
|
170
|
+
} else if (size === 2) {
|
|
171
|
+
convert_endian_2byte(io_buffer, buffer, byte, endian);
|
|
172
|
+
} else {
|
|
173
|
+
convert_endian_4bytes(io_buffer, buffer, byte, endian);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Copies an entry from a big-endian buffer into a device-ordered IO buffer.
|
|
179
|
+
*
|
|
180
|
+
* @param {Layout_Entry} entry
|
|
181
|
+
* @param {Uint8Array} buffer - The big-endian source.
|
|
182
|
+
* @param {Uint8Array} io_buffer - The device-ordered destination.
|
|
183
|
+
* @param {Endian} endian - Device byte order.
|
|
184
|
+
*/
|
|
185
|
+
export function store_entry(entry, buffer, io_buffer, endian) {
|
|
186
|
+
const { byte, size, mask } = entry;
|
|
187
|
+
if (entry.type === "bool") {
|
|
188
|
+
const index = io_index(byte, endian);
|
|
189
|
+
io_buffer[index] = buffer[byte] & mask ? io_buffer[index] | mask : io_buffer[index] & ~mask;
|
|
190
|
+
} else if (size === 1) {
|
|
191
|
+
io_buffer[io_index(byte, endian)] = buffer[byte];
|
|
192
|
+
} else if (size === 2) {
|
|
193
|
+
convert_endian_2byte(buffer, io_buffer, byte, endian);
|
|
194
|
+
} else {
|
|
195
|
+
convert_endian_4bytes(buffer, io_buffer, byte, endian);
|
|
196
|
+
}
|
|
197
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Public interface of the package (doc/design.md §1).
|
|
2
|
+
export { Ldata } from "./Ldata.js";
|
|
3
|
+
export { Ltag } from "./Ltag.js";
|
|
4
|
+
export { register_layout } from "./layout.js";
|
|
5
|
+
export { get_layout } from "./registry.js";
|
|
6
|
+
export { io_index_table } from "./endian.js";
|
|
7
|
+
export { render_types } from "./types.js";
|
|
8
|
+
export * from "./typedefs.js";
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// Type definitions of the internals. This file has no run-time content, and
|
|
2
|
+
// index.js does not export it: the compiled data of a layout is not part of
|
|
3
|
+
// the public interface (doc/spec-data-api.md §6.6).
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Codec of one tag type on a big-endian buffer (doc/design.md §4).
|
|
7
|
+
*
|
|
8
|
+
* @typedef {object} Codec
|
|
9
|
+
* @property {number} size - Size in bytes.
|
|
10
|
+
* @property {(value: any) => string | null} check - The reason a value is refused, null if it can be written.
|
|
11
|
+
* @property {(buffer: Buffer, byte: number, mask: number) => number | boolean} get
|
|
12
|
+
* @property {(buffer: Buffer, byte: number, mask: number, value: number | boolean) => void} set
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* One tag of a compiled layout.
|
|
17
|
+
*
|
|
18
|
+
* @typedef {object} Layout_Entry
|
|
19
|
+
* @property {string} name
|
|
20
|
+
* @property {import("./typedefs.js").Tag_Type} type - Lower case type name.
|
|
21
|
+
* @property {number | boolean} init_value
|
|
22
|
+
* @property {number} byte - Byte offset.
|
|
23
|
+
* @property {number} index - Position among the entries of the layout.
|
|
24
|
+
* @property {number} size - Size in bytes.
|
|
25
|
+
* @property {number} mask - Bit mask of a Bool, 0xff otherwise.
|
|
26
|
+
* @property {Codec} codec
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The expanded structure of a data block (the result of build_structure).
|
|
31
|
+
*
|
|
32
|
+
* @typedef {object} Data_Struct
|
|
33
|
+
* @property {string} name
|
|
34
|
+
* @property {number} length - Total length in bits.
|
|
35
|
+
* @property {import("./typedefs.js").Layout_Item[]} items
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The compiled data of a layout, kept apart from the public Layout
|
|
40
|
+
* (doc/design.md §3).
|
|
41
|
+
*
|
|
42
|
+
* @typedef {object} Compiled_Layout
|
|
43
|
+
* @property {Map<string, Layout_Entry>} entries
|
|
44
|
+
* @property {Layout_Entry[][]} tags_at_byte - The entries covering each byte.
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
export {};
|