@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/.idea/modules.xml +8 -0
- package/.idea/project.iml +8 -0
- package/.idea/vcs.xml +6 -0
- package/CODEOWNERS +24 -0
- package/LICENSE +661 -0
- package/README.md +169 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +24 -0
- package/dist/src/Struct.d.ts +49 -0
- package/dist/src/Struct.js +140 -0
- package/dist/src/StructDecoder.d.ts +11 -0
- package/dist/src/StructDecoder.js +90 -0
- package/dist/src/StructEncoder.d.ts +10 -0
- package/dist/src/StructEncoder.js +92 -0
- package/dist/src/common.d.ts +4 -0
- package/dist/src/common.js +5 -0
- package/dist/src/error/MonitoredError.d.ts +14 -0
- package/dist/src/error/MonitoredError.js +33 -0
- package/dist/src/error/RuntimeException.d.ts +7 -0
- package/dist/src/error/RuntimeException.js +15 -0
- package/index.ts +47 -0
- package/package.json +37 -0
- package/package.json.bak +37 -0
- package/scripts/publish.sh +36 -0
- package/src/Struct.ts +380 -0
- package/src/StructDecoder.ts +199 -0
- package/src/StructEncoder.ts +185 -0
- package/src/common.ts +25 -0
- package/src/error/MonitoredError.ts +90 -0
- package/src/error/RuntimeException.ts +41 -0
- package/test/HelloWorld.test.ts +0 -0
- package/test/Struct.test.ts +251 -0
- package/test/StructDecoder-int.test.ts +133 -0
- package/test/StructDecoder-uint.test.ts +104 -0
- package/test/StructEncoder.test.ts +233 -0
- package/test/res/elf32_lib.so +0 -0
- package/test/res/elf64_ssh.bin +0 -0
- package/test/res/resources.arsc +0 -0
package/README.md
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# dxc-struct
|
|
2
|
+
|
|
3
|
+
Node.js equivalent of Python's Struct library for parsing and writing binary data.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm i @dexcalibur/dxc-struct
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Description
|
|
12
|
+
|
|
13
|
+
`dxc-struct` provides a TypeScript/JavaScript implementation for converting between binary data and JavaScript values, similar to Python's `struct` module. It supports encoding and decoding various data types including integers, floats, strings, and byte arrays with configurable endianness.
|
|
14
|
+
|
|
15
|
+
## Features
|
|
16
|
+
|
|
17
|
+
- **Binary data parsing** and encoding
|
|
18
|
+
- **Multiple data types**: integers (signed/unsigned), floats, doubles, strings, byte arrays
|
|
19
|
+
- **Endianness control**: Big-endian and little-endian support
|
|
20
|
+
- **Format strings**: Python-like format strings for structured data
|
|
21
|
+
- **TypeScript support**: Fully typed for type safety
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
### Basic Example
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
import { Struct } from '@dexcalibur/dxc-struct';
|
|
29
|
+
|
|
30
|
+
// Pack data into binary format
|
|
31
|
+
const data = Struct.pack('<IHB', [0x12345678, 0xABCD, 0xFF]);
|
|
32
|
+
|
|
33
|
+
// Unpack binary data
|
|
34
|
+
const values = Struct.unpack('<IHB', data);
|
|
35
|
+
console.log(values); // [305419896, 43981, 255]
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Format Strings
|
|
39
|
+
|
|
40
|
+
Format strings define the data structure with the following syntax:
|
|
41
|
+
|
|
42
|
+
#### Endianness
|
|
43
|
+
- `<` - Little-endian (default for native)
|
|
44
|
+
- No prefix - Big-endian
|
|
45
|
+
|
|
46
|
+
#### Data Types
|
|
47
|
+
|
|
48
|
+
| Format | Type | Size (bytes) | Description |
|
|
49
|
+
|--------|------|--------------|-------------|
|
|
50
|
+
| `c` | char | 1 | Single character |
|
|
51
|
+
| `b` | signed byte | 1 | Signed integer (-128 to 127) |
|
|
52
|
+
| `B` | unsigned byte | 1 | Unsigned integer (0 to 255) |
|
|
53
|
+
| `h` | short | 2 | Signed integer (-32768 to 32767) |
|
|
54
|
+
| `H` | unsigned short | 2 | Unsigned integer (0 to 65535) |
|
|
55
|
+
| `i` / `l` | int/long | 4 | Signed integer (-2³¹ to 2³¹-1) |
|
|
56
|
+
| `I` / `L` | unsigned int/long | 4 | Unsigned integer (0 to 2³²-1) |
|
|
57
|
+
| `q` | long long | 8 | Signed 64-bit integer (BigInt) |
|
|
58
|
+
| `Q` | unsigned long long | 8 | Unsigned 64-bit integer (BigInt) |
|
|
59
|
+
| `f` | float | 4 | IEEE 754 single-precision |
|
|
60
|
+
| `d` | double | 8 | IEEE 754 double-precision |
|
|
61
|
+
| `s` | string | variable | Fixed-length string |
|
|
62
|
+
| `S` | string | variable | Null-terminated string |
|
|
63
|
+
| `A` | byte array | variable | Byte array |
|
|
64
|
+
| `x` | padding | 1 | Padding byte |
|
|
65
|
+
|
|
66
|
+
#### Count Prefix
|
|
67
|
+
Add a number before the format character to specify count: `4I` = four integers
|
|
68
|
+
|
|
69
|
+
#### Named Fields
|
|
70
|
+
Add field names in parentheses: `I(id)H(value)` returns an object with named fields
|
|
71
|
+
|
|
72
|
+
### Examples
|
|
73
|
+
|
|
74
|
+
#### Unpacking with Named Fields
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
const buffer = Buffer.from([0x78, 0x56, 0x34, 0x12, 0xCD, 0xAB]);
|
|
78
|
+
const result = Struct.unpack('<I(id)H(value)', buffer);
|
|
79
|
+
console.log(result); // { id: 305419896, value: 43981 }
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
#### Packing Multiple Values
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
const buffer = Struct.pack('<3H', [0x1234, 0x5678, 0x9ABC]);
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
#### Working with Strings
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
// Fixed-length string
|
|
92
|
+
const data = Struct.pack('<5s', ['hello']);
|
|
93
|
+
|
|
94
|
+
// Null-terminated string
|
|
95
|
+
const nullTerm = Struct.pack('<S', ['hello']);
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
#### 64-bit Integers (BigInt)
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
const data = Struct.pack('<Q', [BigInt('0xFFFFFFFFFFFFFFFF')]);
|
|
102
|
+
const values = Struct.unpack('<Q', data);
|
|
103
|
+
console.log(values[0]); // 18446744073709551615n
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## API Reference
|
|
107
|
+
|
|
108
|
+
### `Struct.pack(format, values)`
|
|
109
|
+
Packs values into a new Buffer according to the format string.
|
|
110
|
+
|
|
111
|
+
**Parameters:**
|
|
112
|
+
- `format` (string): Format string defining data layout
|
|
113
|
+
- `values` (any[]): Array of values to pack
|
|
114
|
+
|
|
115
|
+
**Returns:** Buffer containing packed binary data
|
|
116
|
+
|
|
117
|
+
### `Struct.packTo(format, buffer, offset, values)`
|
|
118
|
+
Packs values into an existing Buffer at a specific offset.
|
|
119
|
+
|
|
120
|
+
**Parameters:**
|
|
121
|
+
- `format` (string): Format string defining data layout
|
|
122
|
+
- `buffer` (Buffer | any[]): Target buffer
|
|
123
|
+
- `offset` (number): Starting offset in buffer
|
|
124
|
+
- `values` (any[]): Array of values to pack
|
|
125
|
+
|
|
126
|
+
**Returns:** Modified buffer
|
|
127
|
+
|
|
128
|
+
### `Struct.unpack(format, data, offset?)`
|
|
129
|
+
Unpacks binary data according to the format string.
|
|
130
|
+
|
|
131
|
+
**Parameters:**
|
|
132
|
+
- `format` (string): Format string defining data layout
|
|
133
|
+
- `data` (Buffer | any[] | string): Binary data to unpack
|
|
134
|
+
- `offset` (number, optional): Starting offset (default: 0)
|
|
135
|
+
|
|
136
|
+
**Returns:** Array of unpacked values or object with named fields
|
|
137
|
+
|
|
138
|
+
### `Struct.calcLength(format, values)`
|
|
139
|
+
Calculates the byte length required for packing the given values.
|
|
140
|
+
|
|
141
|
+
**Parameters:**
|
|
142
|
+
- `format` (string): Format string
|
|
143
|
+
- `values` (any): Values to be packed
|
|
144
|
+
|
|
145
|
+
**Returns:** Number of bytes required
|
|
146
|
+
|
|
147
|
+
## Development
|
|
148
|
+
|
|
149
|
+
### Build
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
npm run build
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Test
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
npm test
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## License
|
|
162
|
+
|
|
163
|
+
AGPL-3.0-only
|
|
164
|
+
|
|
165
|
+
Copyright (C) 2026 Reversense SAS
|
|
166
|
+
|
|
167
|
+
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
|
168
|
+
|
|
169
|
+
See [LICENSE](LICENSE) for more details.
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { StructEncoder } from "./src/StructEncoder.js";
|
|
2
|
+
import { StructDecoder } from "./src/StructDecoder.js";
|
|
3
|
+
import { Struct } from "./src/Struct.js";
|
|
4
|
+
[
|
|
5
|
+
{ token: 'A', en: StructEncoder.byteArray, de: StructDecoder.byteArray, opts: { size: 1 } },
|
|
6
|
+
{ token: 'c', en: StructEncoder.chr, de: StructDecoder.chr, opts: { size: 1 } },
|
|
7
|
+
{ token: 'b', en: StructEncoder.int, de: StructDecoder.int, opts: { size: 1, bSigned: true, min: -Math.pow(2, 7), max: Math.pow(2, 7) - 1 } },
|
|
8
|
+
{ token: 'B', en: StructEncoder.int, de: StructDecoder.int, opts: { size: 1, bSigned: false, min: 0, max: Math.pow(2, 8) - 1 } },
|
|
9
|
+
{ token: 'h', en: StructEncoder.int, de: StructDecoder.int, opts: { size: 2, bSigned: true, min: -Math.pow(2, 15), max: Math.pow(2, 15) - 1 } },
|
|
10
|
+
{ token: 'H', en: StructEncoder.int, de: StructDecoder.int, opts: { size: 2, bSigned: false, min: 0, max: Math.pow(2, 16) - 1 } },
|
|
11
|
+
{ token: 's', en: StructEncoder.str, de: StructDecoder.str, opts: { size: 1 } },
|
|
12
|
+
{ token: 'S', en: StructEncoder.str, de: StructDecoder.nullTerminatedStr, opts: { size: 1 } },
|
|
13
|
+
{ token: 'f', en: StructEncoder.ieee754_fp, de: StructDecoder.ieee754_fp, opts: { size: 4, mLen: 23, rt: Math.pow(2, -24) - Math.pow(2, -77) } },
|
|
14
|
+
{ token: 'd', en: StructEncoder.ieee754_fp, de: StructDecoder.ieee754_fp, opts: { size: 8, mLen: 52, rt: 0 } },
|
|
15
|
+
{ token: 'i', en: StructEncoder.int, de: StructDecoder.int, opts: { size: 4, bSigned: true, min: -Math.pow(2, 31), max: Math.pow(2, 31) - 1 } },
|
|
16
|
+
{ token: 'I', en: StructEncoder.int, de: StructDecoder.int, opts: { size: 4, bSigned: false, min: 0, max: Math.pow(2, 32) - 1 } },
|
|
17
|
+
{ token: 'l', en: StructEncoder.int, de: StructDecoder.int, opts: { size: 4, bSigned: true, min: -Math.pow(2, 31), max: Math.pow(2, 31) - 1 } },
|
|
18
|
+
{ token: 'L', en: StructEncoder.int, de: StructDecoder.int, opts: { size: 4, bSigned: false, min: 0, max: Math.pow(2, 32) - 1 } },
|
|
19
|
+
{ token: 'q', en: StructEncoder.bigintEncoder, de: StructDecoder.bigintDecoder, opts: { size: 8, bSigned: true, min: -Math.pow(2, 63), max: Math.pow(2, 63) - 1 } },
|
|
20
|
+
{ token: 'Q', en: StructEncoder.bigintEncoder, de: StructDecoder.bigintDecoder, opts: { size: 8, bSigned: false, min: 0, max: Math.pow(2, 64) - 1 } },
|
|
21
|
+
].map((vOp) => {
|
|
22
|
+
Struct.OP[vOp.token] = vOp;
|
|
23
|
+
});
|
|
24
|
+
export { Struct };
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { Endianness } from "./common.js";
|
|
2
|
+
export type Token = string;
|
|
3
|
+
export interface TransformerOpts {
|
|
4
|
+
size: number;
|
|
5
|
+
mLen?: number;
|
|
6
|
+
min?: number;
|
|
7
|
+
max?: number;
|
|
8
|
+
rt?: number;
|
|
9
|
+
bSigned?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export interface Operation {
|
|
12
|
+
token: Token;
|
|
13
|
+
en: ((...args: any) => any);
|
|
14
|
+
de: ((...args: any) => any);
|
|
15
|
+
opts: TransformerOpts;
|
|
16
|
+
}
|
|
17
|
+
export declare class Struct {
|
|
18
|
+
static NATIVE_ENDIANNESS: Endianness;
|
|
19
|
+
static PADDING_TOK: string;
|
|
20
|
+
static BE_FLAG: string;
|
|
21
|
+
static OP: Record<Token, Operation>;
|
|
22
|
+
static FORMAT: string;
|
|
23
|
+
static _lenLut: {
|
|
24
|
+
A: number;
|
|
25
|
+
x: number;
|
|
26
|
+
c: number;
|
|
27
|
+
b: number;
|
|
28
|
+
B: number;
|
|
29
|
+
h: number;
|
|
30
|
+
H: number;
|
|
31
|
+
s: number;
|
|
32
|
+
S: number;
|
|
33
|
+
f: number;
|
|
34
|
+
d: number;
|
|
35
|
+
i: number;
|
|
36
|
+
I: number;
|
|
37
|
+
l: number;
|
|
38
|
+
L: number;
|
|
39
|
+
q: number;
|
|
40
|
+
Q: number;
|
|
41
|
+
};
|
|
42
|
+
static _UnpackSeries(pEndian: Endianness, pOpe: Operation, pElCount: number, pElSize: number, pArr: any[] | Buffer | string, pOffset: number): any[];
|
|
43
|
+
static _PackSeries(pEndian: Endianness, pOp: Operation, pElCount: number, pElSize: number, pDestArr: any[] | Buffer, pOffset: number, pSourceArr: any[] | Buffer, i: any): void;
|
|
44
|
+
static _zip(keys: any, values: any): any;
|
|
45
|
+
static unpack(pFmt: string, pArr: any[] | Buffer | string, pOffset?: number): any;
|
|
46
|
+
static packTo(pFmt: string, pArr: any[] | Buffer, pStartAt: number, pSource: any[]): any[] | Buffer<ArrayBufferLike>;
|
|
47
|
+
static pack(pFmt: string, pBinaryData: any[]): any;
|
|
48
|
+
static calcLength(pFmt: string, pValues: any): number;
|
|
49
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { RuntimeException } from "./error/RuntimeException.js";
|
|
2
|
+
import { Endianness } from "./common.js";
|
|
3
|
+
const NOT_NULL_TERM_STRING = 'S';
|
|
4
|
+
export class Struct {
|
|
5
|
+
static _UnpackSeries(pEndian, pOpe, pElCount, pElSize, pArr, pOffset) {
|
|
6
|
+
let res = [];
|
|
7
|
+
for (let i = 0; i < pElCount; i++) {
|
|
8
|
+
res.push(pOpe.de.apply(null, [pEndian, pOpe, pArr, pOffset + i * pElSize]));
|
|
9
|
+
}
|
|
10
|
+
return res;
|
|
11
|
+
}
|
|
12
|
+
static _PackSeries(pEndian, pOp, pElCount, pElSize, pDestArr, pOffset, pSourceArr, i) {
|
|
13
|
+
for (let o = 0; o < pElCount; o++) {
|
|
14
|
+
pOp.en.apply(null, [pEndian, pOp, pDestArr, pOffset + o * pElSize, pSourceArr[i + o]]);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
;
|
|
18
|
+
static _zip(keys, values) {
|
|
19
|
+
let result = {};
|
|
20
|
+
for (var i = 0; i < keys.length; i++) {
|
|
21
|
+
result[keys[i]] = values[i];
|
|
22
|
+
}
|
|
23
|
+
return result;
|
|
24
|
+
}
|
|
25
|
+
static unpack(pFmt, pArr, pOffset = 0) {
|
|
26
|
+
let endian = (pFmt.charAt(0) != '<') ? Endianness.BIG_ENDIAN : Struct.NATIVE_ENDIANNESS;
|
|
27
|
+
const re = new RegExp(Struct.FORMAT, 'g');
|
|
28
|
+
let m;
|
|
29
|
+
let elCount;
|
|
30
|
+
let elSz = 0;
|
|
31
|
+
let rk = [];
|
|
32
|
+
let rv = [];
|
|
33
|
+
while (m = re.exec(pFmt)) {
|
|
34
|
+
elCount = ((m[1] == undefined) || (m[1] == '')) ? 1 : parseInt(m[1]);
|
|
35
|
+
if (m[2] === NOT_NULL_TERM_STRING) {
|
|
36
|
+
elCount = 0;
|
|
37
|
+
while (pArr[pOffset + elCount] !== 0) {
|
|
38
|
+
elCount++;
|
|
39
|
+
}
|
|
40
|
+
elCount++;
|
|
41
|
+
}
|
|
42
|
+
elSz = Struct.OP[m[2]].opts.size;
|
|
43
|
+
if ((pOffset + elCount * elSz) > pArr.length) {
|
|
44
|
+
throw RuntimeException.READ_OOB();
|
|
45
|
+
}
|
|
46
|
+
if ('SAs'.indexOf(m[2]) != -1) {
|
|
47
|
+
rv.push(Struct.OP[m[2]].de(pArr, pOffset, elCount));
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
rv.push(Struct._UnpackSeries(endian, Struct.OP[m[2]], elCount, elSz, pArr, pOffset));
|
|
51
|
+
}
|
|
52
|
+
rk.push(m[4]);
|
|
53
|
+
pOffset += elCount * elSz;
|
|
54
|
+
}
|
|
55
|
+
rv = Array.prototype.concat.apply([], rv);
|
|
56
|
+
if (rk.indexOf(undefined) !== -1) {
|
|
57
|
+
return rv;
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
return Struct._zip(rk, rv);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
;
|
|
64
|
+
static packTo(pFmt, pArr, pStartAt, pSource) {
|
|
65
|
+
let bigEndian = (pFmt.charAt(0) != '<') ? Endianness.BIG_ENDIAN : Struct.NATIVE_ENDIANNESS;
|
|
66
|
+
const re = new RegExp(Struct.FORMAT, 'g');
|
|
67
|
+
let offset = pStartAt;
|
|
68
|
+
let m;
|
|
69
|
+
let sz, dataLen = 0, i = 0, j = 0;
|
|
70
|
+
let opType;
|
|
71
|
+
while (m = re.exec(pFmt)) {
|
|
72
|
+
opType = m[2];
|
|
73
|
+
dataLen = ((m[1] == undefined) || (m[1] == '')) ? 1 : parseInt(m[1]);
|
|
74
|
+
if (opType === NOT_NULL_TERM_STRING) {
|
|
75
|
+
dataLen = pSource[i].length + 1;
|
|
76
|
+
}
|
|
77
|
+
sz = Struct.OP[m[2]].opts.size;
|
|
78
|
+
if ((offset + dataLen * sz) > pArr.length) {
|
|
79
|
+
throw RuntimeException.WRITE_OOB();
|
|
80
|
+
}
|
|
81
|
+
switch (opType) {
|
|
82
|
+
case 'A':
|
|
83
|
+
case 's':
|
|
84
|
+
case 'S':
|
|
85
|
+
if ((i + 1) > pSource.length) {
|
|
86
|
+
throw RuntimeException.READ_OOB();
|
|
87
|
+
}
|
|
88
|
+
Struct.OP[opType].en(pArr, offset, dataLen, pSource[i]);
|
|
89
|
+
i += 1;
|
|
90
|
+
break;
|
|
91
|
+
case Struct.PADDING_TOK:
|
|
92
|
+
for (j = 0; j < dataLen; j++) {
|
|
93
|
+
pArr[offset + j] = 0;
|
|
94
|
+
}
|
|
95
|
+
break;
|
|
96
|
+
default:
|
|
97
|
+
if ((i + dataLen) > pSource.length) {
|
|
98
|
+
throw RuntimeException.READ_OOB();
|
|
99
|
+
}
|
|
100
|
+
Struct._PackSeries(bigEndian, Struct.OP[opType], dataLen, sz, pArr, offset, pSource, i);
|
|
101
|
+
i += dataLen;
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
offset += dataLen * sz;
|
|
105
|
+
}
|
|
106
|
+
return pArr;
|
|
107
|
+
}
|
|
108
|
+
;
|
|
109
|
+
static pack(pFmt, pBinaryData) {
|
|
110
|
+
return Struct.packTo(pFmt, new Buffer(this.calcLength(pFmt, pBinaryData)), 0, pBinaryData);
|
|
111
|
+
}
|
|
112
|
+
static calcLength(pFmt, pValues) {
|
|
113
|
+
const re = new RegExp(Struct.FORMAT, 'g');
|
|
114
|
+
let m, n, sum = 0, i = 0;
|
|
115
|
+
while (m = re.exec(pFmt)) {
|
|
116
|
+
if ((m[1] == undefined) || (m[1] == '')) {
|
|
117
|
+
n = Struct.OP[m[2]].opts.size;
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
n = parseInt(m[1]) * Struct.OP[m[2]].opts.size;
|
|
121
|
+
}
|
|
122
|
+
if (m[2] === NOT_NULL_TERM_STRING) {
|
|
123
|
+
n = pValues[i].length + 1;
|
|
124
|
+
}
|
|
125
|
+
sum += n;
|
|
126
|
+
if (m[2] !== Struct.PADDING_TOK) {
|
|
127
|
+
i++;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return sum;
|
|
131
|
+
}
|
|
132
|
+
;
|
|
133
|
+
}
|
|
134
|
+
Struct.NATIVE_ENDIANNESS = Endianness.LITTLE_ENDIAN;
|
|
135
|
+
Struct.PADDING_TOK = 'x';
|
|
136
|
+
Struct.BE_FLAG = '<';
|
|
137
|
+
Struct.OP = {};
|
|
138
|
+
Struct.FORMAT = '(\\d+)?([AxcbBhHsSfdiIlLqQ])(\\(([a-zA-Z0-9]+)\\))?';
|
|
139
|
+
Struct._lenLut = { 'A': 1, 'x': 1, 'c': 1, 'b': 1, 'B': 1, 'h': 2, 'H': 2, 's': 1,
|
|
140
|
+
'S': 1, 'f': 4, 'd': 8, 'i': 4, 'I': 4, 'l': 4, 'L': 4, 'q': 8, 'Q': 8 };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Endianness } from "./common.js";
|
|
2
|
+
import { Operation } from "./Struct.js";
|
|
3
|
+
export declare class StructDecoder {
|
|
4
|
+
static byteArray(pArr: any[], pOffset: number, pLength: number): any[];
|
|
5
|
+
static chr(pArr: number[], pOffset: number): string;
|
|
6
|
+
static int(pEndianness: Endianness, pOp: Operation, pArr: any, pOffset: number): number;
|
|
7
|
+
static bigintDecoder(pEndianness: Endianness, pOp: Operation, pArr: any, pOffset: number): bigint;
|
|
8
|
+
static str(pArr: number[], pOffset: number, pLen: number): string;
|
|
9
|
+
static nullTerminatedStr(pArr: number[], pOffset: number, pLen: number): string;
|
|
10
|
+
static ieee754_fp(pEndianness: Endianness, pOp: Operation, pArr: any[], pOffset: number): number;
|
|
11
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { Endianness } from "./common.js";
|
|
2
|
+
export class StructDecoder {
|
|
3
|
+
static byteArray(pArr, pOffset, pLength) {
|
|
4
|
+
return [pArr.slice(pOffset, pOffset + pLength)];
|
|
5
|
+
}
|
|
6
|
+
static chr(pArr, pOffset) {
|
|
7
|
+
return String.fromCharCode(pArr[pOffset]);
|
|
8
|
+
}
|
|
9
|
+
static int(pEndianness, pOp, pArr, pOffset) {
|
|
10
|
+
let lsb, nsb;
|
|
11
|
+
if (pEndianness == Endianness.BIG_ENDIAN) {
|
|
12
|
+
lsb = pOp.opts.size - 1;
|
|
13
|
+
nsb = -1;
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
lsb = 0;
|
|
17
|
+
nsb = 1;
|
|
18
|
+
}
|
|
19
|
+
let stop = lsb + nsb * pOp.opts.size, rv, i, f;
|
|
20
|
+
for (rv = 0, i = lsb, f = 1; i != stop; rv += (pArr[pOffset + i] * f), i += nsb, f *= 256)
|
|
21
|
+
;
|
|
22
|
+
if (pOp.opts.bSigned && (rv & Math.pow(2, pOp.opts.size * 8 - 1))) {
|
|
23
|
+
rv -= Math.pow(2, pOp.opts.size * 8);
|
|
24
|
+
}
|
|
25
|
+
return rv;
|
|
26
|
+
}
|
|
27
|
+
;
|
|
28
|
+
static bigintDecoder(pEndianness, pOp, pArr, pOffset) {
|
|
29
|
+
let lsb, nsb;
|
|
30
|
+
if (pEndianness == Endianness.BIG_ENDIAN) {
|
|
31
|
+
lsb = pOp.opts.size - 1;
|
|
32
|
+
nsb = -1;
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
lsb = 0;
|
|
36
|
+
nsb = 1;
|
|
37
|
+
}
|
|
38
|
+
let stop = lsb + nsb * pOp.opts.size;
|
|
39
|
+
let i;
|
|
40
|
+
let rv = BigInt(0);
|
|
41
|
+
let f = BigInt(1);
|
|
42
|
+
for (i = lsb; i != stop; i += nsb) {
|
|
43
|
+
rv += BigInt(pArr[pOffset + i]) * f;
|
|
44
|
+
f *= BigInt(256);
|
|
45
|
+
}
|
|
46
|
+
if (pOp.opts.bSigned && (rv >> BigInt(pOp.opts.size * 8 - 1)) == BigInt(1)) {
|
|
47
|
+
rv -= BigInt(1) << BigInt(pOp.opts.size * 8);
|
|
48
|
+
}
|
|
49
|
+
return rv;
|
|
50
|
+
}
|
|
51
|
+
;
|
|
52
|
+
static str(pArr, pOffset, pLen) {
|
|
53
|
+
let s = new Array(pLen);
|
|
54
|
+
for (let i = 0; i < pLen; s[i] = String.fromCharCode(pArr[pOffset + i]), i++)
|
|
55
|
+
;
|
|
56
|
+
return s.join('');
|
|
57
|
+
}
|
|
58
|
+
;
|
|
59
|
+
static nullTerminatedStr(pArr, pOffset, pLen) {
|
|
60
|
+
let str = StructDecoder.str(pArr, pOffset, pLen);
|
|
61
|
+
return str.substring(0, str.length - 1);
|
|
62
|
+
}
|
|
63
|
+
;
|
|
64
|
+
static ieee754_fp(pEndianness, pOp, pArr, pOffset) {
|
|
65
|
+
let s, e, m, i, d, nBits, mLen, eLen, eBias, eMax;
|
|
66
|
+
mLen = pOp.opts.mLen, eLen = pOp.opts.size * 8 - pOp.opts.mLen - 1, eMax = (1 << eLen) - 1, eBias = eMax >> 1;
|
|
67
|
+
i = (pEndianness == Endianness.BIG_ENDIAN) ? 0 : (pOp.opts.size - 1);
|
|
68
|
+
d = (pEndianness == Endianness.BIG_ENDIAN) ? 1 : -1;
|
|
69
|
+
s = pArr[pOffset + i];
|
|
70
|
+
i += d;
|
|
71
|
+
nBits = -7;
|
|
72
|
+
for (e = s & ((1 << (-nBits)) - 1), s >>= (-nBits), nBits += eLen; nBits > 0; e = e * 256 + pArr[pOffset + i], i += d, nBits -= 8)
|
|
73
|
+
;
|
|
74
|
+
for (m = e & ((1 << (-nBits)) - 1), e >>= (-nBits), nBits += mLen; nBits > 0; m = m * 256 + pArr[pOffset + i], i += d, nBits -= 8)
|
|
75
|
+
;
|
|
76
|
+
switch (e) {
|
|
77
|
+
case 0:
|
|
78
|
+
e = 1 - eBias;
|
|
79
|
+
break;
|
|
80
|
+
case eMax:
|
|
81
|
+
return m ? NaN : ((s ? -1 : 1) * Infinity);
|
|
82
|
+
default:
|
|
83
|
+
m = m + Math.pow(2, mLen);
|
|
84
|
+
e = e - eBias;
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
return (s ? -1 : 1) * m * Math.pow(2, e - mLen);
|
|
88
|
+
}
|
|
89
|
+
;
|
|
90
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Endianness } from "./common.js";
|
|
2
|
+
import { Operation } from "./Struct.js";
|
|
3
|
+
export declare class StructEncoder {
|
|
4
|
+
static byteArray(pDestArr: number[], pOffset: number, pLen: number, pSourceArr: number[]): void;
|
|
5
|
+
static chr(pDestArr: number[], pOffset: number, pChar: string): void;
|
|
6
|
+
static str(pDestArr: number[], pOffset: number, pLen: number, pSourceStr: string): void;
|
|
7
|
+
static int(pEndianness: Endianness, pOp: Operation, pArr: any[], pOffset: number, v: number): void;
|
|
8
|
+
static bigintEncoder(pEndianness: Endianness, pOp: Operation, pArr: any[], pOffset: number, v: bigint): void;
|
|
9
|
+
static ieee754_fp(pEndianness: Endianness, pOp: Operation, pDestArr: any[], pOffset: number, pValue: number): void;
|
|
10
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { Endianness } from "./common.js";
|
|
2
|
+
export class StructEncoder {
|
|
3
|
+
static byteArray(pDestArr, pOffset, pLen, pSourceArr) {
|
|
4
|
+
for (let i = 0; i < pLen; pDestArr[pOffset + i] = pSourceArr[i] ? pSourceArr[i] : 0, i++)
|
|
5
|
+
;
|
|
6
|
+
}
|
|
7
|
+
static chr(pDestArr, pOffset, pChar) {
|
|
8
|
+
pDestArr[pOffset] = pChar.charCodeAt(0);
|
|
9
|
+
}
|
|
10
|
+
;
|
|
11
|
+
static str(pDestArr, pOffset, pLen, pSourceStr) {
|
|
12
|
+
for (let t, i = 0; i < pLen; pDestArr[pOffset + i] = (t = pSourceStr.charCodeAt(i)) ? t : 0, i++)
|
|
13
|
+
;
|
|
14
|
+
}
|
|
15
|
+
;
|
|
16
|
+
static int(pEndianness, pOp, pArr, pOffset, v) {
|
|
17
|
+
let lsb, nsb;
|
|
18
|
+
if (pEndianness == Endianness.BIG_ENDIAN) {
|
|
19
|
+
lsb = pOp.opts.size - 1;
|
|
20
|
+
nsb = -1;
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
lsb = 0;
|
|
24
|
+
nsb = 1;
|
|
25
|
+
}
|
|
26
|
+
let stop = lsb + nsb * pOp.opts.size, i;
|
|
27
|
+
v = (v < pOp.opts.min) ? pOp.opts.min : (v > pOp.opts.max) ? pOp.opts.max : v;
|
|
28
|
+
for (i = lsb; i != stop; pArr[pOffset + i] = v & 0xff, i += nsb, v >>= 8)
|
|
29
|
+
;
|
|
30
|
+
}
|
|
31
|
+
;
|
|
32
|
+
static bigintEncoder(pEndianness, pOp, pArr, pOffset, v) {
|
|
33
|
+
let lsb, nsb;
|
|
34
|
+
if (pEndianness == Endianness.BIG_ENDIAN) {
|
|
35
|
+
lsb = pOp.opts.size - 1;
|
|
36
|
+
nsb = -1;
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
lsb = 0;
|
|
40
|
+
nsb = 1;
|
|
41
|
+
}
|
|
42
|
+
let stop = lsb + nsb * pOp.opts.size, i;
|
|
43
|
+
v = BigInt((v < pOp.opts.min) ? pOp.opts.min : (v > pOp.opts.max) ? pOp.opts.max : v);
|
|
44
|
+
for (i = lsb; i != stop; pArr[pOffset + i] = v & BigInt(0xff), i += nsb, v >>= BigInt(8))
|
|
45
|
+
;
|
|
46
|
+
}
|
|
47
|
+
static ieee754_fp(pEndianness, pOp, pDestArr, pOffset, pValue) {
|
|
48
|
+
let s, e, m, i, d, c, mLen, eLen, eBias, eMax, abs;
|
|
49
|
+
mLen = pOp.opts.mLen, eLen = pOp.opts.size * 8 - pOp.opts.mLen - 1, eMax = (1 << eLen) - 1, eBias = eMax >> 1;
|
|
50
|
+
s = pValue < 0 ? 1 : 0;
|
|
51
|
+
abs = Math.abs(pValue);
|
|
52
|
+
if (isNaN(abs) || (abs == Infinity)) {
|
|
53
|
+
m = isNaN(abs) ? 1 : 0;
|
|
54
|
+
e = eMax;
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
e = Math.floor(Math.log(abs) / Math.LN2);
|
|
58
|
+
if (abs * (c = Math.pow(2, -e)) < 1) {
|
|
59
|
+
e--;
|
|
60
|
+
c *= 2;
|
|
61
|
+
}
|
|
62
|
+
if (e + eBias >= 1) {
|
|
63
|
+
abs += pOp.opts.rt / c;
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
abs += pOp.opts.rt * Math.pow(2, 1 - eBias);
|
|
67
|
+
}
|
|
68
|
+
if (abs * c >= 2) {
|
|
69
|
+
e++;
|
|
70
|
+
c /= 2;
|
|
71
|
+
}
|
|
72
|
+
if (e + eBias >= eMax) {
|
|
73
|
+
m = 0;
|
|
74
|
+
e = eMax;
|
|
75
|
+
}
|
|
76
|
+
else if (e + eBias >= 1) {
|
|
77
|
+
m = (abs * c - 1) * Math.pow(2, mLen);
|
|
78
|
+
e = e + eBias;
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
m = abs * Math.pow(2, eBias - 1) * Math.pow(2, mLen);
|
|
82
|
+
e = 0;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
for (i = (pEndianness == Endianness.BIG_ENDIAN) ? (pOp.opts.size - 1) : 0, d = (pEndianness == Endianness.BIG_ENDIAN) ? -1 : 1; mLen >= 8; pDestArr[pOffset + i] = m & 0xff, i += d, m /= 256, mLen -= 8)
|
|
86
|
+
;
|
|
87
|
+
for (e = (e << mLen) | m, eLen += mLen; eLen > 0; pDestArr[pOffset + i] = e & 0xff, i += d, e /= 256, eLen -= 8)
|
|
88
|
+
;
|
|
89
|
+
pDestArr[pOffset + i - d] |= s * 128;
|
|
90
|
+
}
|
|
91
|
+
;
|
|
92
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare class MonitoredError extends Error {
|
|
2
|
+
static ERR_CODE_BASE: number;
|
|
3
|
+
static NEW_HOOK: ((pErr: MonitoredError | null) => void);
|
|
4
|
+
cmp: string;
|
|
5
|
+
code: number;
|
|
6
|
+
extra: any;
|
|
7
|
+
constructor(pCmp: string, pMsg: string, pCode?: number, pExtra?: any);
|
|
8
|
+
getCode(): number;
|
|
9
|
+
getMessage(): string;
|
|
10
|
+
getExtra(): any;
|
|
11
|
+
toString(): string;
|
|
12
|
+
protected _triggerNewHook(): void;
|
|
13
|
+
toObject(pIncludeExtra?: boolean): any;
|
|
14
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export class MonitoredError extends Error {
|
|
2
|
+
constructor(pCmp, pMsg, pCode = -1, pExtra = null) {
|
|
3
|
+
super(pMsg);
|
|
4
|
+
this.cmp = pCmp;
|
|
5
|
+
this.code = pCode;
|
|
6
|
+
this.extra = pExtra;
|
|
7
|
+
}
|
|
8
|
+
getCode() {
|
|
9
|
+
return this.code;
|
|
10
|
+
}
|
|
11
|
+
getMessage() {
|
|
12
|
+
return `[${this.cmp}][#${this.code}] ${this.message} `;
|
|
13
|
+
}
|
|
14
|
+
getExtra() {
|
|
15
|
+
return this.extra;
|
|
16
|
+
}
|
|
17
|
+
toString() {
|
|
18
|
+
return `[${this.cmp}] [#${this.code != null ? this.code : "<null>"} ${this.message}`;
|
|
19
|
+
}
|
|
20
|
+
_triggerNewHook() {
|
|
21
|
+
MonitoredError.NEW_HOOK.apply(null, [this]);
|
|
22
|
+
}
|
|
23
|
+
toObject(pIncludeExtra = false) {
|
|
24
|
+
return {
|
|
25
|
+
cmp: this.cmp,
|
|
26
|
+
code: this.code,
|
|
27
|
+
msg: this.message,
|
|
28
|
+
extra: pIncludeExtra ? this.extra : null
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
MonitoredError.ERR_CODE_BASE = 1000;
|
|
33
|
+
MonitoredError.NEW_HOOK = (() => { });
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { MonitoredError } from "./MonitoredError.js";
|
|
2
|
+
export declare class RuntimeException extends MonitoredError {
|
|
3
|
+
static WRITE_OOB: () => RuntimeException;
|
|
4
|
+
static READ_OOB: () => RuntimeException;
|
|
5
|
+
static UNKNOWN_FORMAT: (pToken: string) => RuntimeException;
|
|
6
|
+
constructor(pMsg: string, pCode?: number, pExtra?: any);
|
|
7
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { MonitoredError } from "./MonitoredError.js";
|
|
2
|
+
export class RuntimeException extends MonitoredError {
|
|
3
|
+
constructor(pMsg, pCode = null, pExtra = null) {
|
|
4
|
+
super('DXC-STRUCT', pMsg, pCode, pExtra);
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
RuntimeException.WRITE_OOB = () => {
|
|
8
|
+
return new RuntimeException("Write Out-of-Bound", MonitoredError.ERR_CODE_BASE + 1);
|
|
9
|
+
};
|
|
10
|
+
RuntimeException.READ_OOB = () => {
|
|
11
|
+
return new RuntimeException("Read Out-of-Bound", MonitoredError.ERR_CODE_BASE + 2);
|
|
12
|
+
};
|
|
13
|
+
RuntimeException.UNKNOWN_FORMAT = (pToken) => {
|
|
14
|
+
return new RuntimeException("Format is invalid. Following token is not recognized [token=" + pToken + "]", MonitoredError.ERR_CODE_BASE + 3);
|
|
15
|
+
};
|