@reversense/dxc-struct 1.0.7

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/index.ts ADDED
@@ -0,0 +1,47 @@
1
+ /*
2
+ *
3
+ * Reversense platform / dxc-struct : Reversense is an automated reverse engineering and analysis platform
4
+ * focused on security, privacy, quality, accessibility and safety assessment of software, including mobile app and firmware.
5
+ * Copyright (C) 2026 Reversense SAS
6
+ *
7
+ * This program is free software: you can redistribute it and/or modify
8
+ * it under the terms of the GNU Affero General Public License as published
9
+ * by the Free Software Foundation, either version 3 of the License, or
10
+ * (at your option) any later version.
11
+ *
12
+ * This program is distributed in the hope that it will be useful,
13
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ * GNU Affero General Public License for more details.
16
+ *
17
+ * You should have received a copy of the GNU Affero General Public License
18
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
19
+ *
20
+ */
21
+
22
+ import {StructEncoder} from "./src/StructEncoder.js";
23
+ import {StructDecoder} from "./src/StructDecoder.js";
24
+ import { Struct } from "./src/Struct.js";
25
+
26
+ [
27
+ { token:'A', en:StructEncoder.byteArray, de:StructDecoder.byteArray, opts:{ size:1 }},
28
+ { token:'c', en:StructEncoder.chr, de:StructDecoder.chr, opts:{ size:1 }},
29
+ { token:'b', en:StructEncoder.int, de:StructDecoder.int, opts:{ size:1, bSigned: true, min: -Math.pow(2, 7), max: Math.pow(2, 7) - 1 }},
30
+ { token:'B', en:StructEncoder.int, de:StructDecoder.int, opts:{ size:1, bSigned: false, min: 0, max: Math.pow(2, 8) - 1 }},
31
+ { token:'h', en:StructEncoder.int, de:StructDecoder.int, opts:{ size:2, bSigned: true, min: -Math.pow(2, 15), max: Math.pow(2, 15) - 1 }},
32
+ { token:'H', en:StructEncoder.int, de:StructDecoder.int, opts:{ size:2, bSigned: false, min: 0, max: Math.pow(2, 16) - 1 }},
33
+ { token:'s', en:StructEncoder.str, de:StructDecoder.str, opts:{ size:1 }},
34
+ { token:'S', en:StructEncoder.str, de:StructDecoder.nullTerminatedStr, opts:{ size:1 }},
35
+ { token:'f', en:StructEncoder.ieee754_fp, de:StructDecoder.ieee754_fp, opts:{ size:4, mLen: 23, rt: Math.pow(2, -24) - Math.pow(2, -77) }},
36
+ { token:'d', en:StructEncoder.ieee754_fp, de:StructDecoder.ieee754_fp, opts:{ size:8, mLen: 52, rt: 0 }},
37
+ { token:'i', en:StructEncoder.int, de:StructDecoder.int, opts:{ size:4, bSigned: true, min: -Math.pow(2, 31), max: Math.pow(2, 31) - 1 }},
38
+ { token:'I', en:StructEncoder.int, de:StructDecoder.int, opts:{ size:4, bSigned: false, min: 0, max: Math.pow(2, 32) - 1 }},
39
+ { token:'l', en:StructEncoder.int, de:StructDecoder.int, opts:{ size:4, bSigned: true, min: -Math.pow(2, 31), max: Math.pow(2, 31) - 1 }},
40
+ { token:'L', en:StructEncoder.int, de:StructDecoder.int, opts:{ size:4, bSigned: false, min: 0, max: Math.pow(2, 32) - 1 }},
41
+ { token:'q', en:StructEncoder.bigintEncoder, de:StructDecoder.bigintDecoder, opts:{ size:8, bSigned: true, min: -Math.pow(2, 63), max: Math.pow(2, 63) - 1 }},
42
+ { token:'Q', en:StructEncoder.bigintEncoder, de:StructDecoder.bigintDecoder, opts:{ size:8, bSigned: false, min: 0, max: Math.pow(2, 64) - 1 }},
43
+ ].map((vOp)=>{
44
+ Struct.OP[vOp.token] = vOp;
45
+ });
46
+
47
+ export {Struct};
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@reversense/dxc-struct",
3
+ "version": "1.0.7",
4
+ "description": "Node equivalent of Python-based Struct library to parse and write binary data.",
5
+ "main": "dist/index.js",
6
+ "scripts": {
7
+ "build": "npx tsc",
8
+ "test": "DEXCALIBUR_TEST=1 mocha -r ts-node/register ./test/**/*.ts"
9
+ },
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "ssh://git@github.com:reversenseorg/dxc-struct.git"
13
+ },
14
+ "publishConfig": {
15
+ "@dexcalibur:registry": "https://gitlab.reversense.org/api/v4/projects/63/packages/npm/",
16
+ "@reversense:registry": "https://registry.npmjs.org/",
17
+ "@reversenseorg:registry": "https://npm.pkg.github.com/"
18
+ },
19
+ "keywords": [
20
+ "struct",
21
+ "binary",
22
+ "data",
23
+ "parser"
24
+ ],
25
+ "type": "module",
26
+ "author": "Georges-Bastien Michel <georges.michel@reversense.com>",
27
+ "license": "AGPL-3.0-only",
28
+ "devDependencies": {
29
+ "@types/chai": "^4.3.16",
30
+ "@types/mocha": "^10.0.6",
31
+ "@types/node": "^20.12.8",
32
+ "chai": "^5.1.0",
33
+ "mocha": "^10.4.0",
34
+ "ts-node": "^10.9.2",
35
+ "typescript": "^5.4.5"
36
+ }
37
+ }
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@dexcalibur/dxc-struct",
3
+ "version": "1.0.7",
4
+ "description": "Node equivalent of Python-based Struct library to parse and write binary data.",
5
+ "main": "dist/index.js",
6
+ "scripts": {
7
+ "build": "npx tsc",
8
+ "test": "DEXCALIBUR_TEST=1 mocha -r ts-node/register ./test/**/*.ts"
9
+ },
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "ssh://git@github.com:reversenseorg/dxc-struct.git"
13
+ },
14
+ "publishConfig": {
15
+ "@dexcalibur:registry": "https://gitlab.reversense.org/api/v4/projects/63/packages/npm/",
16
+ "@reversense:registry": "https://registry.npmjs.org/",
17
+ "@reversenseorg:registry": "https://npm.pkg.github.com/"
18
+ },
19
+ "keywords": [
20
+ "struct",
21
+ "binary",
22
+ "data",
23
+ "parser"
24
+ ],
25
+ "type": "module",
26
+ "author": "Georges-Bastien Michel <georges.michel@reversense.com>",
27
+ "license": "AGPL-3.0-only",
28
+ "devDependencies": {
29
+ "@types/chai": "^4.3.16",
30
+ "@types/mocha": "^10.0.6",
31
+ "@types/node": "^20.12.8",
32
+ "chai": "^5.1.0",
33
+ "mocha": "^10.4.0",
34
+ "ts-node": "^10.9.2",
35
+ "typescript": "^5.4.5"
36
+ }
37
+ }
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # Reversense platform / dxc-struct : Reversense is an automated reverse engineering and analysis platform
4
+ # focused on security, privacy, quality, accessibility and safety assessment of software, including mobile app and firmware.
5
+ # Copyright (C) 2026 Reversense SAS
6
+ #
7
+ # This program is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU Affero General Public License as published
9
+ # by the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # This program is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU Affero General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU Affero General Public License
18
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
19
+ #
20
+ #
21
+
22
+ set -e
23
+
24
+ # Publish npm package within @dexcalibur scope
25
+ npm publish # use @dexcalibur/<pkg> by default
26
+
27
+ # Switch scope to ublish npm package within @reversenseorg scope
28
+ cp package.json package.json.bak
29
+
30
+ npm pkg set name="@reversenseorg/dxc-struct"
31
+ npm publish --registry=https://npm.pkg.github.com/
32
+
33
+ npm pkg set name="@reversense/dxc-struct"
34
+ npm publish --registry=https://registry.npmjs.org/
35
+
36
+ mv package.json.bak package.json
package/src/Struct.ts ADDED
@@ -0,0 +1,380 @@
1
+ /*
2
+ *
3
+ * Reversense platform / dxc-struct : Reversense is an automated reverse engineering and analysis platform
4
+ * focused on security, privacy, quality, accessibility and safety assessment of software, including mobile app and firmware.
5
+ * Copyright (C) 2026 Reversense SAS
6
+ *
7
+ * This program is free software: you can redistribute it and/or modify
8
+ * it under the terms of the GNU Affero General Public License as published
9
+ * by the Free Software Foundation, either version 3 of the License, or
10
+ * (at your option) any later version.
11
+ *
12
+ * This program is distributed in the hope that it will be useful,
13
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ * GNU Affero General Public License for more details.
16
+ *
17
+ * You should have received a copy of the GNU Affero General Public License
18
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
19
+ *
20
+ */
21
+
22
+ import {RuntimeException} from "./error/RuntimeException.js";
23
+ import {Endianness} from "./common.js";
24
+
25
+ export type Token = string;
26
+
27
+ /**
28
+ * Defines the options available for configuring a transformer.
29
+ *
30
+ * @interface TransformerOpts
31
+ *
32
+ * @property {number} size
33
+ * Represents the type size.
34
+ *
35
+ * @property {number} [mLen]
36
+ * Represents the optional mantissa length.
37
+ *
38
+ * @property {number} [min]
39
+ * Specifies the optional minimum value.
40
+ *
41
+ * @property {number} [max]
42
+ * Specifies the optional maximum value.
43
+ *
44
+ * @property {number} [rt]
45
+ * Optional rounding threshold.
46
+ *
47
+ * @property {boolean} [bSigned]
48
+ * Indicates whether to use signed representation.
49
+ */
50
+ export interface TransformerOpts {
51
+ // type size
52
+ size: number,
53
+ mLen?:number,
54
+ min?:number,
55
+ max?:number,
56
+ rt?:number,
57
+ bSigned?: boolean
58
+ }
59
+
60
+
61
+ export interface Operation {
62
+ token:Token,
63
+ en: ((...args:any)=>any),
64
+ de: ((...args:any)=>any),
65
+ opts: TransformerOpts
66
+ }
67
+
68
+ const NOT_NULL_TERM_STRING = 'S';
69
+ export class Struct {
70
+
71
+ static NATIVE_ENDIANNESS = Endianness.LITTLE_ENDIAN;
72
+ /**
73
+ * Keep it as a global var to replace 'x' by '?'
74
+ */
75
+ static PADDING_TOK = 'x';
76
+ /**
77
+ * Big-Endian flag
78
+ *
79
+ */
80
+ static BE_FLAG = '<';
81
+ static OP:Record<Token, Operation> = {};
82
+
83
+ // Class data
84
+ /**
85
+ * This pattern is designed to match specific sequences of characters that conform
86
+ * to a set structure. The structure includes the following groups:
87
+ *
88
+ * 1. An optional numeric group `(\\d+)?` which captures one or more digits.
89
+ * 2. A mandatory character group `([AxcbBhHsSfdiIlLqQ])` which captures a single valid
90
+ * character from the set: A, x, c, b, B, h, H, s, S, f, d, i, I, l, L, q, Q.
91
+ * 3. An optional sub-pattern group `(\\(([a-zA-Z0-9]+)\\))?` which captures a
92
+ * parenthetical expression containing any combination of letters and digits.
93
+ *
94
+ * The FORMAT variable can be used to validate strings, parse strings, or extract
95
+ * the individual components that match this structure.
96
+ */
97
+ static FORMAT = '(\\d+)?([AxcbBhHsSfdiIlLqQ])(\\(([a-zA-Z0-9]+)\\))?';
98
+
99
+ /**
100
+ * A lookup table containing the lengths (in bytes) of various data types.
101
+ * The keys represent the data type identifiers and the values represent their respective lengths.
102
+ *
103
+ * Example Type Identifiers:
104
+ * - 'A', 'x', 'c', 'b', 'B', 's', 'S': Represent data types with a length of 1 byte.
105
+ * - 'h', 'H': Represent data types with a length of 2 bytes.
106
+ * - 'f', 'i', 'I', 'l', 'L': Represent data types with a length of 4 bytes.
107
+ * - 'd', 'q', 'Q': Represent data types with a length of 8 bytes.
108
+ */
109
+ static _lenLut = {'A': 1, 'x': 1, 'c': 1, 'b': 1, 'B': 1, 'h': 2, 'H': 2, 's': 1,
110
+ 'S': 1, 'f': 4, 'd': 8, 'i': 4, 'I': 4, 'l': 4, 'L': 4, 'q': 8, 'Q': 8};
111
+ /*static _elLut = {
112
+ 's': {en: m._EnString, de: m._DeString},
113
+ 'S': {en: m._EnString, de: m._DeNullString},
114
+ 'b': {en: m._EnInt, de: m._DeInt, len: 1, bSigned: true, min: -Math.pow(2, 7), max: Math.pow(2, 7) - 1},
115
+ 'B': {en: m._EnInt, de: m._DeInt, len: 1, bSigned: false, min: 0, max: Math.pow(2, 8) - 1},
116
+ 'h': {en: m._EnInt, de: m._DeInt, len: 2, bSigned: true, min: -Math.pow(2, 15), max: Math.pow(2, 15) - 1},
117
+ 'H': {en: m._EnInt, de: m._DeInt, len: 2, bSigned: false, min: 0, max: Math.pow(2, 16) - 1},
118
+ 'i': {en: m._EnInt, de: m._DeInt, len: 4, bSigned: true, min: -Math.pow(2, 31), max: Math.pow(2, 31) - 1},
119
+ 'I': {en: m._EnInt, de: m._DeInt, len: 4, bSigned: false, min: 0, max: Math.pow(2, 32) - 1},
120
+ 'l': {en: m._EnInt, de: m._DeInt, len: 4, bSigned: true, min: -Math.pow(2, 31), max: Math.pow(2, 31) - 1},
121
+ 'L': {en: m._EnInt, de: m._DeInt, len: 4, bSigned: false, min: 0, max: Math.pow(2, 32) - 1},
122
+ 'f': {en: m._En754, de: m._De754, len: 4, mLen: 23, rt: Math.pow(2, -24) - Math.pow(2, -77)},
123
+ 'd': {en: m._En754, de: m._De754, len: 8, mLen: 52, rt: 0}
124
+ };*/
125
+
126
+ // Module-level (private) variables
127
+ // var el, bBE = false, m = this;
128
+
129
+
130
+
131
+
132
+
133
+ // Unpack a series of n elements of size s from array a at offset p with fxn
134
+ /**
135
+ * Unpacks a series of elements from an array, buffer, or string using a specified operation and endian format.
136
+ *
137
+ * @param {Endianness} pEndian - The endian format to use when interpreting the data.
138
+ * @param {Operation} pOpe - The operation defining how elements are decoded.
139
+ * @param {number} pElCount - The number of elements to unpack.
140
+ * @param {number} pElSize - The size of each element in bytes.
141
+ * @param {any[]|Buffer|string} pArr - The input data source (can be an array, buffer, or string).
142
+ * @param {number} pOffset - The starting offset in the input data source.
143
+ * @return {any[]} A list of unpacked elements.
144
+ */
145
+ static _UnpackSeries(pEndian:Endianness, pOpe:Operation, pElCount:number, pElSize:number,
146
+ pArr:any[]|Buffer|string , pOffset:number):any[] {
147
+ let res:any[] = [];
148
+ for (let i = 0; i < pElCount; i++){
149
+ res.push(pOpe.de.apply(null, [pEndian, pOpe, pArr, pOffset+i*pElSize] ))
150
+ }
151
+ return res;
152
+ }
153
+
154
+ // Pack a series of n elements of size s from array v at offset i to array a at offset p with fxn
155
+ /**
156
+ * Packs a series of elements from a source array or buffer into a destination array or buffer based on the specified endianness, operation, and element size.
157
+ *
158
+ * @param {Endianness} pEndian - The endianness to be applied for the operation (e.g., little-endian or big-endian).
159
+ * @param {Operation} pOp - The operation containing the method to encode the data (e.g., `en` method within the operation object).
160
+ * @param {number} pElCount - The number of elements to be packed from the source array/buffer.
161
+ * @param {number} pElSize - The size of each element in bytes.
162
+ * @param {any[]|Buffer} pDestArr - The destination array or buffer where the packed data will be stored.
163
+ * @param {number} pOffset - The starting offset in the destination array or buffer where data packing begins.
164
+ * @param {any[]|Buffer} pSourceArr - The source array or buffer containing elements to be packed.
165
+ * @param {number} i - The starting index in the source array or buffer from which elements will be read.
166
+ * @return {void} This method does not return a value; it modifies the destination array or buffer in place.
167
+ */
168
+ static _PackSeries(pEndian:Endianness, pOp:Operation, pElCount:number, pElSize:number,
169
+ pDestArr:any[]|Buffer, pOffset:number, pSourceArr:any[]|Buffer, i) {
170
+ for (let o = 0; o < pElCount; o++){
171
+ pOp.en.apply(null, [pEndian, pOp, pDestArr, pOffset+o*pElSize, pSourceArr[i+o]])
172
+ }
173
+ };
174
+
175
+ /**
176
+ * Combines two arrays, `keys` and `values`, into an object where each key from the `keys` array
177
+ * is assigned the corresponding value from the `values` array.
178
+ *
179
+ * @param {Array} keys - An array containing the keys for the resulting object.
180
+ * @param {Array} values - An array containing the values corresponding to each key in the `keys` array.
181
+ * @return {Object} An object constructed by mapping the elements of the `keys` array to the elements of the `values` array.
182
+ */
183
+ static _zip(keys, values) {
184
+ let result:any = {};
185
+
186
+ for (var i = 0; i < keys.length; i++) {
187
+ result[keys[i]] = values[i];
188
+ }
189
+
190
+ return result;
191
+ }
192
+
193
+ // Unpack the octet array a, beginning at offset p, according to the fmt string
194
+ /**
195
+ * Unpacks binary data from a buffer, array, or string based on the specified format string.
196
+ *
197
+ * @param {string} pFmt The format string defining the structure of the binary data to unpack.
198
+ * It uses format specifiers to describe data types and endianness.
199
+ * @param {any[]|Buffer|string} pArr The binary data source to unpack. This can be an array, Buffer, or string.
200
+ * @param {number} [pOffset=0] The starting offset in the data source from where the unpacking should begin.
201
+ * @return {any} Returns the unpacked data. If keys are defined in the format string, it returns an object; otherwise, it returns an array.
202
+ */
203
+ static unpack(pFmt:string, pArr:any[]|Buffer|string, pOffset = 0):any {
204
+
205
+ // Set the private bBE flag based on the format string - assume big-endianness
206
+ let endian = (pFmt.charAt(0) != '<')? Endianness.BIG_ENDIAN:Struct.NATIVE_ENDIANNESS;
207
+
208
+ const re = new RegExp(Struct.FORMAT, 'g');
209
+ let m:RegExpExecArray;
210
+ let elCount;
211
+ let elSz = 0;
212
+ let rk:any[] = [];
213
+ let rv:any[] = [];
214
+
215
+ while (m = re.exec(pFmt)) {
216
+
217
+ elCount = ((m[1]==undefined)||(m[1]==''))?1:parseInt(m[1]);
218
+
219
+ if(m[2] === NOT_NULL_TERM_STRING) { // Null term string support
220
+ elCount = 0; // Need to deal with empty null term strings
221
+ while(pArr[pOffset + elCount] !== 0) {
222
+ elCount++;
223
+ }
224
+ elCount++; // Add one for null byte
225
+ }
226
+
227
+ elSz = Struct.OP[m[2]].opts.size;
228
+
229
+ if ((pOffset + elCount*elSz) > pArr.length) {
230
+ throw RuntimeException.READ_OOB()
231
+ }
232
+
233
+ if ('SAs'.indexOf(m[2]) != -1) {
234
+ rv.push(Struct.OP[m[2]].de(pArr, pOffset, elCount));
235
+ }
236
+ else {
237
+ rv.push(Struct._UnpackSeries(endian, Struct.OP[m[2]], elCount, elSz, pArr, pOffset));
238
+ }
239
+ rk.push(m[4]); // Push key on to array
240
+
241
+ pOffset += elCount*elSz;
242
+ }
243
+
244
+ rv = Array.prototype.concat.apply([], rv)
245
+
246
+ if(rk.indexOf(undefined) !== -1) {
247
+ return rv;
248
+ } else {
249
+ return Struct._zip(rk, rv);
250
+ }
251
+ };
252
+
253
+ // Pack the supplied values into the octet array a, beginning at offset p, according to the fmt string
254
+ /**
255
+ * Packs data from the source array into the target array or buffer based on the format string.
256
+ * The format string specifies the data types and layout, controlling how data is serialized
257
+ * into the provided array or buffer starting from a specific offset.
258
+ *
259
+ * @param {string} pFmt - The format string specifying the data layout and types to encode.
260
+ * Data types are represented as tokens (e.g., 's' for string).
261
+ * It may also include prefixes for fixed-size values (e.g., '10s' for a 10-char string).
262
+ * The character '<' at the start specifies little-endian byte order, default is big-endian.
263
+ * @param {any[]|Buffer} pArr - The target array or buffer where the serialized data will be packed.
264
+ * @param {number} pStartAt - The offset (in bytes) into the target array or buffer to start packing the data.
265
+ * @param {any[]} pSource - The source array of values to be packed. The data in this array will follow the layout
266
+ * specified in the format string.
267
+ * @return {any[]|Buffer} - The modified target array or buffer after packing the serialized data.
268
+ * @throws {RuntimeException} - Throws if:
269
+ * 1. There is not enough space in the target array or buffer to write the data
270
+ * (buffer overflow).
271
+ * 2. Attempted to read data outside the bounds of the source array
272
+ * (read out of bounds).
273
+ */
274
+ static packTo(pFmt:string, pArr:any[]|Buffer, pStartAt:number, pSource:any[]) {
275
+
276
+ // Set the private bBE flag based on the format string - assume big-endianness
277
+ let bigEndian = (pFmt.charAt(0) != '<')? Endianness.BIG_ENDIAN:Struct.NATIVE_ENDIANNESS;
278
+
279
+ const re = new RegExp(Struct.FORMAT, 'g');
280
+ let offset = pStartAt;
281
+ let m:RegExpExecArray;
282
+ let sz:number, dataLen:number=0, i = 0, j=0;
283
+ let opType:string;
284
+
285
+ while (m = re.exec(pFmt)) {
286
+
287
+ // type of data to encode
288
+ opType = m[2];
289
+
290
+ // if data type is prefixed by a counter, such as '10s' (which means a string of 10 chars)
291
+ dataLen = ((m[1]==undefined)||(m[1]==''))?1:parseInt(m[1]);
292
+
293
+ // Null term string support
294
+ if(opType === NOT_NULL_TERM_STRING) {
295
+ // todo : check
296
+ dataLen = pSource[i].length + 1; // Add one for null byte
297
+ }
298
+
299
+ sz = Struct.OP[m[2]].opts.size;
300
+
301
+ // detect buffer overflow (write out of bound)
302
+ if ((offset + dataLen*sz) > pArr.length) {
303
+ throw RuntimeException.WRITE_OOB();
304
+ }
305
+
306
+ switch (opType) {
307
+ // byate array, fixed-length string, null-terminated string
308
+ case 'A': case 's': case 'S':
309
+ if ((i + 1) > pSource.length) {
310
+ throw RuntimeException.READ_OOB();
311
+ }
312
+ Struct.OP[opType].en(pArr, offset, dataLen, pSource[i]);
313
+ i += 1;
314
+ break;
315
+ case Struct.PADDING_TOK:
316
+ for (j = 0; j < dataLen; j++) { pArr[offset+j] = 0; }
317
+ break;
318
+ default:
319
+ if ((i + dataLen) > pSource.length) {
320
+ throw RuntimeException.READ_OOB();
321
+ }
322
+ Struct._PackSeries(bigEndian, Struct.OP[opType], dataLen, sz, pArr, offset, pSource, i);
323
+ i += dataLen;
324
+ break;
325
+ }
326
+ offset += dataLen*sz;
327
+ }
328
+
329
+ return pArr;
330
+ };
331
+
332
+ // Pack the supplied values into a new octet array, according to the fmt string
333
+ /**
334
+ *
335
+ * @param {string} pFmt Format
336
+ * @param {string} pBinaryData Binary data as string
337
+ * @return {any}
338
+ * @method
339
+ */
340
+ static pack(pFmt:string, pBinaryData:any[]):any {
341
+ return Struct.packTo(pFmt, new Buffer(this.calcLength(pFmt, pBinaryData)), 0, pBinaryData);
342
+ }
343
+
344
+ // Determine the number of bytes represented by the format string
345
+ /**
346
+ * Calculates the length of a formatted structure based on the provided format string and values.
347
+ *
348
+ * @param {string} pFmt - The format string representing the structure layout.
349
+ * It defines how the values map to the structure.
350
+ * @param {any} pValues - An array or object containing the values to be mapped to the structure format.
351
+ * @return {number} The total calculated length of the structure in bytes.
352
+ */
353
+ static calcLength(pFmt:string, pValues:any):number {
354
+ const re = new RegExp(Struct.FORMAT, 'g');
355
+ let m:any,n:number, sum = 0, i = 0;
356
+
357
+ while (m = re.exec(pFmt)) {
358
+ if((m[1]==undefined)||(m[1]=='')){
359
+ n = Struct.OP[m[2]].opts.size; //1
360
+ }else{
361
+ n = parseInt(m[1]) * Struct.OP[m[2]].opts.size;
362
+ }
363
+
364
+ // case of null terminated string => unknow length
365
+ if(m[2] === NOT_NULL_TERM_STRING) {
366
+ n = pValues[i].length + 1; // Add one for null byte
367
+ }
368
+
369
+ // byte counter
370
+ sum += n;
371
+
372
+ // ?
373
+ if(m[2] !== Struct.PADDING_TOK) {
374
+ i++;
375
+ }
376
+ }
377
+ return sum;
378
+ };
379
+
380
+ }