@robotical/raftjs 2.0.3 → 2.0.4

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/src/RaftStruct.ts CHANGED
@@ -8,77 +8,124 @@
8
8
  //
9
9
  /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
10
10
 
11
+ type FormatInstruction =
12
+ | { kind: "endian"; littleEndian: boolean }
13
+ | { kind: "spec"; code: string; repeat: number };
14
+
15
+ function parseFormatInstructions(format: string): FormatInstruction[] {
16
+ const instructions: FormatInstruction[] = [];
17
+ let idx = 0;
18
+
19
+ while (idx < format.length) {
20
+
21
+ const char = format[idx];
22
+
23
+ // Endianness specifiers
24
+ if (char === "<" || char === ">") {
25
+ instructions.push({ kind: "endian", littleEndian: char === "<" });
26
+ idx++;
27
+ continue;
28
+ }
29
+
30
+ // Ignore whitespace
31
+ if (/\s/.test(char)) {
32
+ idx++;
33
+ continue;
34
+ }
35
+
36
+ // Attribute code
37
+ const code = char;
38
+ idx++;
39
+
40
+ // Check for repeat count using [N] syntax
41
+ let repeat = 1;
42
+ if (idx < format.length && format[idx] === "[") {
43
+ const endIdx = format.indexOf("]", idx + 1);
44
+ if (endIdx === -1) {
45
+ throw new Error(`Invalid format string: missing closing ] in "${format}"`);
46
+ }
47
+ const repeatStr = format.slice(idx + 1, endIdx);
48
+ repeat = parseInt(repeatStr, 10);
49
+ if (!Number.isFinite(repeat) || repeat <= 0) {
50
+ throw new Error(`Invalid repeat count "${repeatStr}" in format string "${format}"`);
51
+ }
52
+ idx = endIdx + 1;
53
+ }
54
+
55
+ instructions.push({ kind: "spec", code, repeat });
56
+ }
57
+
58
+ return instructions;
59
+ }
60
+
11
61
  export function structUnpack(format: string, data: Uint8Array): number[] {
12
62
  const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
13
63
  const results: number[] = [];
14
64
  let offset = 0;
15
65
  let littleEndian = false;
16
66
 
17
- for (const char of format) {
18
- switch (char) {
19
- case "<":
20
- littleEndian = true;
21
- break;
22
- case ">":
23
- littleEndian = false;
24
- break;
25
- case "x": // Padding byte
26
- offset += 1;
27
- break;
28
- case "c": // Char
29
- results.push(view.getUint8(offset));
30
- offset += 1;
31
- break;
32
- case "b": // Signed 8-bit integer
33
- results.push(view.getInt8(offset));
34
- offset += 1;
35
- break;
36
- case "B": // Unsigned 8-bit integer
37
- results.push(view.getUint8(offset));
38
- offset += 1;
39
- break;
40
- case "h": // Signed 16-bit integer
41
- results.push(view.getInt16(offset, littleEndian));
42
- offset += 2;
43
- break;
44
- case "H": // Unsigned 16-bit integer (big-endian)
45
- results.push(view.getUint16(offset, littleEndian));
46
- offset += 2;
47
- break;
48
- case "i": // Signed 32-bit integer (big-endian)
49
- results.push(view.getInt32(offset, littleEndian));
50
- offset += 4;
51
- break;
52
- case "I": // Unsigned 32-bit integer (big-endian)
53
- results.push(view.getUint32(offset, littleEndian));
54
- offset += 4;
55
- break;
56
- case "l": // Signed 32-bit integer (big-endian)
57
- results.push(view.getInt32(offset, littleEndian));
58
- offset += 4;
59
- break;
60
- case "L": // Unsigned 32-bit integer (big-endian)
61
- results.push(view.getUint32(offset, littleEndian));
62
- offset += 4;
63
- break;
64
- // case "q": // Signed 64-bit integer (big-endian)
65
- // results.push(view.getBigInt64(offset, littleEndian));
66
- // offset += 8;
67
- // break;
68
- // case "Q": // Unsigned 64-bit integer (big-endian)
69
- // results.push(view.getBigUint64(offset, littleEndian));
70
- // offset += 8;
71
- // break;
72
- case "f": // 32-bit float (big-endian)
73
- results.push(view.getFloat32(offset, littleEndian));
74
- offset += 4;
75
- break;
76
- case "d": // 64-bit float (big-endian)
77
- results.push(view.getFloat64(offset, littleEndian));
78
- offset += 8;
79
- break;
80
- default:
81
- throw new Error(`Unknown format character: ${char}`);
67
+ const instructions = parseFormatInstructions(format);
68
+
69
+ for (const instruction of instructions) {
70
+ if (instruction.kind === "endian") {
71
+ littleEndian = instruction.littleEndian;
72
+ continue;
73
+ }
74
+
75
+ const { code, repeat } = instruction;
76
+
77
+ for (let count = 0; count < repeat; count++) {
78
+ switch (code) {
79
+ case "x": // Padding byte
80
+ offset += 1;
81
+ break;
82
+ case "c": // Char
83
+ case "B": // Unsigned 8-bit integer
84
+ case "?": // Boolean (stored as uint8)
85
+ results.push(view.getUint8(offset));
86
+ offset += 1;
87
+ break;
88
+ case "b": // Signed 8-bit integer
89
+ results.push(view.getInt8(offset));
90
+ offset += 1;
91
+ break;
92
+ case "h": // Signed 16-bit integer
93
+ results.push(view.getInt16(offset, littleEndian));
94
+ offset += 2;
95
+ break;
96
+ case "H": // Unsigned 16-bit integer
97
+ results.push(view.getUint16(offset, littleEndian));
98
+ offset += 2;
99
+ break;
100
+ case "i": // Signed 32-bit integer
101
+ case "l": // Signed 32-bit integer
102
+ results.push(view.getInt32(offset, littleEndian));
103
+ offset += 4;
104
+ break;
105
+ case "I": // Unsigned 32-bit integer
106
+ case "L": // Unsigned 32-bit integer
107
+ results.push(view.getUint32(offset, littleEndian));
108
+ offset += 4;
109
+ break;
110
+ // case "q": // Signed 64-bit integer
111
+ // results.push(Number(view.getBigInt64(offset, littleEndian)));
112
+ // offset += 8;
113
+ // break;
114
+ // case "Q": // Unsigned 64-bit integer
115
+ // results.push(Number(view.getBigUint64(offset, littleEndian)));
116
+ // offset += 8;
117
+ // break;
118
+ case "f": // 32-bit float
119
+ results.push(view.getFloat32(offset, littleEndian));
120
+ offset += 4;
121
+ break;
122
+ case "d": // 64-bit float
123
+ results.push(view.getFloat64(offset, littleEndian));
124
+ offset += 8;
125
+ break;
126
+ default:
127
+ throw new Error(`Unknown format character: ${code}`);
128
+ }
82
129
  }
83
130
  }
84
131
 
@@ -87,42 +134,49 @@ export function structUnpack(format: string, data: Uint8Array): number[] {
87
134
 
88
135
  export function structSizeOf(format: string): number {
89
136
  let size = 0;
90
- for (const char of format) {
91
- switch (char) {
92
- case "<":
93
- case ">":
94
- break;
137
+ const instructions = parseFormatInstructions(format);
138
+
139
+ for (const instruction of instructions) {
140
+ if (instruction.kind === "endian") {
141
+ continue;
142
+ }
143
+
144
+ const { code, repeat } = instruction;
145
+ let unitSize: number;
146
+
147
+ switch (code) {
95
148
  case "x": // Padding byte
96
- size += 1;
97
- break;
98
149
  case "c": // Char
99
150
  case "b": // Signed 8-bit integer
100
151
  case "B": // Unsigned 8-bit integer
101
- size += 1;
152
+ case "?": // Boolean (uint8)
153
+ unitSize = 1;
102
154
  break;
103
155
  case "h": // Signed 16-bit integer
104
156
  case "H": // Unsigned 16-bit integer
105
- size += 2;
157
+ unitSize = 2;
106
158
  break;
107
159
  case "i": // Signed 32-bit integer
108
160
  case "I": // Unsigned 32-bit integer
109
161
  case "l": // Signed 32-bit integer
110
162
  case "L": // Unsigned 32-bit integer
111
- size += 4;
163
+ unitSize = 4;
112
164
  break;
113
165
  // case "q": // Signed 64-bit integer
114
166
  // case "Q": // Unsigned 64-bit integer
115
- // size += 8;
167
+ // unitSize = 8;
116
168
  // break;
117
169
  case "f": // 32-bit float
118
- size += 4;
170
+ unitSize = 4;
119
171
  break;
120
172
  case "d": // 64-bit float
121
- size += 8;
173
+ unitSize = 8;
122
174
  break;
123
175
  default:
124
- throw new Error(`Unknown format character: ${char}`);
176
+ throw new Error(`Unknown format character: ${code}`);
125
177
  }
178
+
179
+ size += unitSize * repeat;
126
180
  }
127
181
  return size;
128
182
  }
@@ -134,74 +188,93 @@ export function structPack(format: string, values: number[]): Uint8Array {
134
188
  let offset = 0;
135
189
  let littleEndian = false;
136
190
 
137
- const valIdx = 0;
138
- for (let i = 0; i < format.length; i++) {
139
- const char = format[i];
140
- const value = values[valIdx];
141
- switch (char) {
142
- case "<":
143
- littleEndian = true;
144
- break;
145
- case ">":
146
- littleEndian = false;
147
- break;
148
- case "x": // Padding byte
149
- offset += 1;
150
- break;
151
- case "c": // Char
152
- view.setInt8(offset, value);
153
- offset += 1;
154
- break;
155
- case "b": // Signed 8-bit integer
156
- view.setInt8(offset, value);
157
- offset += 1;
158
- break;
159
- case "B": // Unsigned 8-bit integer
160
- view.setUint8(offset, value);
161
- offset += 1;
162
- break;
163
- case "h": // Signed 16-bit integer
164
- view.setInt16(offset, value, littleEndian);
165
- offset += 2;
166
- break;
167
- case "H": // Unsigned 16-bit integer
168
- view.setUint16(offset, value, littleEndian);
169
- offset += 2;
170
- break;
171
- case "i": // Signed 32-bit integer
172
- view.setInt32(offset, value, littleEndian);
173
- offset += 4;
174
- break;
175
- case "I": // Unsigned 32-bit integer
176
- view.setUint32(offset, value, littleEndian);
177
- offset += 4;
178
- break;
179
- case "l": // Signed 32-bit integer
180
- view.setInt32(offset, value, littleEndian);
181
- offset += 4;
182
- break;
183
- case "L": // Unsigned 32-bit integer
184
- view.setUint32(offset, value, littleEndian);
185
- offset += 4;
186
- break;
187
- // case "q": // Signed 64-bit integer
188
- // view.setBigInt64(offset, BigInt(value), littleEndian);
189
- // offset += 8;
190
- // break;
191
- // case "Q": // Unsigned 64-bit integer
192
- // view.setBigUint64(offset, BigInt(value), littleEndian);
193
- // offset += 8;
194
- // break;
195
- case "f": // 32-bit float
196
- view.setFloat32(offset, value, littleEndian);
197
- offset += 4;
198
- break;
199
- case "d": // 64-bit float
200
- view.setFloat64(offset, value, littleEndian);
201
- offset += 8;
202
- break;
203
- default:
204
- throw new Error(`Unknown format character: ${char}`);
191
+ const instructions = parseFormatInstructions(format);
192
+ let valueIdx = 0;
193
+
194
+ for (const instruction of instructions) {
195
+ if (instruction.kind === "endian") {
196
+ littleEndian = instruction.littleEndian;
197
+ continue;
198
+ }
199
+
200
+ const { code, repeat } = instruction;
201
+
202
+ for (let count = 0; count < repeat; count++) {
203
+ switch (code) {
204
+ case "x": // Padding byte
205
+ offset += 1;
206
+ break;
207
+ case "c": // Char
208
+ case "b": // Signed 8-bit integer
209
+ if (valueIdx >= values.length) {
210
+ throw new Error("Insufficient values provided for structPack");
211
+ }
212
+ view.setInt8(offset, values[valueIdx++]);
213
+ offset += 1;
214
+ break;
215
+ case "B": // Unsigned 8-bit integer
216
+ case "?": // Boolean (uint8)
217
+ if (valueIdx >= values.length) {
218
+ throw new Error("Insufficient values provided for structPack");
219
+ }
220
+ view.setUint8(offset, values[valueIdx++]);
221
+ offset += 1;
222
+ break;
223
+ case "h": // Signed 16-bit integer
224
+ if (valueIdx >= values.length) {
225
+ throw new Error("Insufficient values provided for structPack");
226
+ }
227
+ view.setInt16(offset, values[valueIdx++], littleEndian);
228
+ offset += 2;
229
+ break;
230
+ case "H": // Unsigned 16-bit integer
231
+ if (valueIdx >= values.length) {
232
+ throw new Error("Insufficient values provided for structPack");
233
+ }
234
+ view.setUint16(offset, values[valueIdx++], littleEndian);
235
+ offset += 2;
236
+ break;
237
+ case "i": // Signed 32-bit integer
238
+ case "l": // Signed 32-bit integer
239
+ if (valueIdx >= values.length) {
240
+ throw new Error("Insufficient values provided for structPack");
241
+ }
242
+ view.setInt32(offset, values[valueIdx++], littleEndian);
243
+ offset += 4;
244
+ break;
245
+ case "I": // Unsigned 32-bit integer
246
+ case "L": // Unsigned 32-bit integer
247
+ if (valueIdx >= values.length) {
248
+ throw new Error("Insufficient values provided for structPack");
249
+ }
250
+ view.setUint32(offset, values[valueIdx++], littleEndian);
251
+ offset += 4;
252
+ break;
253
+ // case "q": // Signed 64-bit integer
254
+ // view.setBigInt64(offset, BigInt(values[valueIdx++]), littleEndian);
255
+ // offset += 8;
256
+ // break;
257
+ // case "Q": // Unsigned 64-bit integer
258
+ // view.setBigUint64(offset, BigInt(values[valueIdx++]), littleEndian);
259
+ // offset += 8;
260
+ // break;
261
+ case "f": // 32-bit float
262
+ if (valueIdx >= values.length) {
263
+ throw new Error("Insufficient values provided for structPack");
264
+ }
265
+ view.setFloat32(offset, values[valueIdx++], littleEndian);
266
+ offset += 4;
267
+ break;
268
+ case "d": // 64-bit float
269
+ if (valueIdx >= values.length) {
270
+ throw new Error("Insufficient values provided for structPack");
271
+ }
272
+ view.setFloat64(offset, values[valueIdx++], littleEndian);
273
+ offset += 8;
274
+ break;
275
+ default:
276
+ throw new Error(`Unknown format character: ${code}`);
277
+ }
205
278
  }
206
279
  }
207
280