@chr33s/pdf-restructure 5.0.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/LICENSE.md +21 -0
- package/README.md +323 -0
- package/dist/array.d.ts +15 -0
- package/dist/array.js +95 -0
- package/dist/array.js.map +1 -0
- package/dist/base.d.ts +4 -0
- package/dist/base.js +16 -0
- package/dist/base.js.map +1 -0
- package/dist/bitfield.d.ts +11 -0
- package/dist/bitfield.js +37 -0
- package/dist/bitfield.js.map +1 -0
- package/dist/boolean.d.ts +10 -0
- package/dist/boolean.js +18 -0
- package/dist/boolean.js.map +1 -0
- package/dist/buffer.d.ts +11 -0
- package/dist/buffer.js +31 -0
- package/dist/buffer.js.map +1 -0
- package/dist/decode-stream.d.ts +26 -0
- package/dist/decode-stream.js +84 -0
- package/dist/decode-stream.js.map +1 -0
- package/dist/encode-stream.d.ts +19 -0
- package/dist/encode-stream.js +137 -0
- package/dist/encode-stream.js.map +1 -0
- package/dist/enum.d.ts +11 -0
- package/dist/enum.js +25 -0
- package/dist/enum.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/lazy-array.d.ts +22 -0
- package/dist/lazy-array.js +75 -0
- package/dist/lazy-array.js.map +1 -0
- package/dist/number.d.ts +51 -0
- package/dist/number.js +76 -0
- package/dist/number.js.map +1 -0
- package/dist/optional.d.ts +14 -0
- package/dist/optional.js +34 -0
- package/dist/optional.js.map +1 -0
- package/dist/pointer.d.ts +28 -0
- package/dist/pointer.js +160 -0
- package/dist/pointer.js.map +1 -0
- package/dist/reserved.d.ts +12 -0
- package/dist/reserved.js +23 -0
- package/dist/reserved.js.map +1 -0
- package/dist/string.d.ts +14 -0
- package/dist/string.js +123 -0
- package/dist/string.js.map +1 -0
- package/dist/struct.d.ts +15 -0
- package/dist/struct.js +93 -0
- package/dist/struct.js.map +1 -0
- package/dist/utils.d.ts +13 -0
- package/dist/utils.js +45 -0
- package/dist/utils.js.map +1 -0
- package/dist/versioned-struct.d.ts +18 -0
- package/dist/versioned-struct.js +129 -0
- package/dist/versioned-struct.js.map +1 -0
- package/package.json +47 -0
- package/src/array.ts +113 -0
- package/src/base.ts +17 -0
- package/src/bitfield.ts +46 -0
- package/src/boolean.ts +24 -0
- package/src/buffer.ts +40 -0
- package/src/decode-stream.ts +97 -0
- package/src/encode-stream.ts +161 -0
- package/src/enum.ts +32 -0
- package/src/index.ts +17 -0
- package/src/lazy-array.ts +91 -0
- package/src/number.ts +88 -0
- package/src/optional.ts +46 -0
- package/src/pointer.ts +204 -0
- package/src/reserved.ts +29 -0
- package/src/string.ts +154 -0
- package/src/struct.ts +127 -0
- package/src/utils.ts +55 -0
- package/src/versioned-struct.ts +174 -0
- package/test/array.test.ts +95 -0
- package/test/bitfield.test.ts +52 -0
- package/test/boolean.test.ts +35 -0
- package/test/buffer.test.ts +49 -0
- package/test/decode-stream.test.ts +104 -0
- package/test/encode-stream.test.ts +111 -0
- package/test/enum.test.ts +30 -0
- package/test/lazy-array.test.ts +70 -0
- package/test/number.test.ts +222 -0
- package/test/optional.test.ts +105 -0
- package/test/pointer.test.ts +248 -0
- package/test/reserved.test.ts +28 -0
- package/test/string.test.ts +114 -0
- package/test/struct.test.ts +164 -0
- package/test/versioned-struct.test.ts +462 -0
- package/tsconfig.json +9 -0
- package/tsconfig.typecheck.json +14 -0
- package/vitest.config.ts +12 -0
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import Struct from "./struct.js";
|
|
2
|
+
export default class VersionedStruct extends Struct {
|
|
3
|
+
type;
|
|
4
|
+
versions;
|
|
5
|
+
constructor(type, versions = {}) {
|
|
6
|
+
super();
|
|
7
|
+
this.type = type;
|
|
8
|
+
this.versions = versions;
|
|
9
|
+
}
|
|
10
|
+
#versionGetter(parent) {
|
|
11
|
+
if (typeof this.type === "string") {
|
|
12
|
+
return this.type
|
|
13
|
+
.split(".")
|
|
14
|
+
.reduce((obj, prop) => (typeof obj !== "undefined" ? obj[prop] : undefined), parent);
|
|
15
|
+
}
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
decode(stream, parent, length = 0) {
|
|
19
|
+
const result = this._setup(stream, parent, length);
|
|
20
|
+
if (typeof this.type === "string") {
|
|
21
|
+
result.version = this.#versionGetter(parent);
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
result.version = this.type.decode(stream);
|
|
25
|
+
}
|
|
26
|
+
if (this.versions.header) {
|
|
27
|
+
this._parseFields(stream, result, this.versions.header);
|
|
28
|
+
}
|
|
29
|
+
const fields = this.versions[result.version];
|
|
30
|
+
if (!fields) {
|
|
31
|
+
throw new Error(`Unknown version ${result.version}`);
|
|
32
|
+
}
|
|
33
|
+
if (fields instanceof VersionedStruct) {
|
|
34
|
+
return fields.decode(stream, parent);
|
|
35
|
+
}
|
|
36
|
+
this._parseFields(stream, result, fields);
|
|
37
|
+
if (this.process) {
|
|
38
|
+
this.process.call(result, stream);
|
|
39
|
+
}
|
|
40
|
+
return result;
|
|
41
|
+
}
|
|
42
|
+
size(val, parent, includePointers = true) {
|
|
43
|
+
if (!val) {
|
|
44
|
+
throw new Error("Not a fixed size");
|
|
45
|
+
}
|
|
46
|
+
if (this.preEncode) {
|
|
47
|
+
this.preEncode.call(val);
|
|
48
|
+
}
|
|
49
|
+
const ctx = {
|
|
50
|
+
parent,
|
|
51
|
+
val,
|
|
52
|
+
pointerSize: 0,
|
|
53
|
+
};
|
|
54
|
+
let total = 0;
|
|
55
|
+
if (typeof this.type !== "string") {
|
|
56
|
+
total += this.type.size(val.version, ctx);
|
|
57
|
+
}
|
|
58
|
+
if (this.versions.header) {
|
|
59
|
+
for (const [key, type] of Object.entries(this.versions.header)) {
|
|
60
|
+
if (type?.size) {
|
|
61
|
+
total += type.size(val[key], ctx);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const fields = this.versions[val.version];
|
|
66
|
+
if (!fields) {
|
|
67
|
+
throw new Error(`Unknown version ${val.version}`);
|
|
68
|
+
}
|
|
69
|
+
if (fields instanceof VersionedStruct) {
|
|
70
|
+
total += fields.size(val, parent, includePointers);
|
|
71
|
+
return total;
|
|
72
|
+
}
|
|
73
|
+
for (const [key, type] of Object.entries(fields)) {
|
|
74
|
+
if (type?.size) {
|
|
75
|
+
const fieldSize = type.size(val[key], ctx);
|
|
76
|
+
total += fieldSize;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (includePointers) {
|
|
80
|
+
total += ctx.pointerSize;
|
|
81
|
+
}
|
|
82
|
+
return total;
|
|
83
|
+
}
|
|
84
|
+
encode(stream, val, parent) {
|
|
85
|
+
if (this.preEncode) {
|
|
86
|
+
this.preEncode.call(val, stream);
|
|
87
|
+
}
|
|
88
|
+
const ctx = {
|
|
89
|
+
pointers: [],
|
|
90
|
+
startOffset: stream.pos,
|
|
91
|
+
pointerOffset: 0,
|
|
92
|
+
pointerSize: 0,
|
|
93
|
+
parent,
|
|
94
|
+
val,
|
|
95
|
+
};
|
|
96
|
+
const sizeWithoutPointers = this.size(val, ctx, false);
|
|
97
|
+
ctx.pointerOffset = stream.pos + sizeWithoutPointers;
|
|
98
|
+
if (typeof this.type !== "string") {
|
|
99
|
+
this.type.encode(stream, val.version);
|
|
100
|
+
}
|
|
101
|
+
if (this.versions.header) {
|
|
102
|
+
for (const [key, type] of Object.entries(this.versions.header)) {
|
|
103
|
+
if (type?.encode) {
|
|
104
|
+
type.encode(stream, val[key], ctx);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
const fields = this.versions[val.version];
|
|
109
|
+
if (!fields) {
|
|
110
|
+
throw new Error(`Unknown version ${val.version}`);
|
|
111
|
+
}
|
|
112
|
+
if (fields instanceof VersionedStruct) {
|
|
113
|
+
fields.encode(stream, val, parent);
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
for (const [key, type] of Object.entries(fields)) {
|
|
117
|
+
if (type?.encode) {
|
|
118
|
+
type.encode(stream, val[key], ctx);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
let i = 0;
|
|
123
|
+
while (i < ctx.pointers.length) {
|
|
124
|
+
const ptr = ctx.pointers[i++];
|
|
125
|
+
ptr.type.encode(stream, ptr.val, ptr.parent);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=versioned-struct.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"versioned-struct.js","sourceRoot":"","sources":["../src/versioned-struct.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@chr33s/pdf-restructure",
|
|
3
|
+
"version": "5.0.0",
|
|
4
|
+
"description": "Declaratively encode and decode binary data",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"default": "./dist/index.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc",
|
|
15
|
+
"clean": "rm -rf dist",
|
|
16
|
+
"test": "vitest run",
|
|
17
|
+
"typecheck": "tsc --project tsconfig.typecheck.json"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@types/node": "24.10.1",
|
|
21
|
+
"@vitest/coverage-v8": "4.0.15",
|
|
22
|
+
"typescript": "5.9.3",
|
|
23
|
+
"vitest": "4.0.15"
|
|
24
|
+
},
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "https://github.com/chr33s/pdf.git"
|
|
28
|
+
},
|
|
29
|
+
"keywords": [
|
|
30
|
+
"binary",
|
|
31
|
+
"struct",
|
|
32
|
+
"encode",
|
|
33
|
+
"decode"
|
|
34
|
+
],
|
|
35
|
+
"author": "Andrew Dillon <andrew.dillon.j@gmail.com>",
|
|
36
|
+
"contributors": [
|
|
37
|
+
"Devon Govett <devongovett@gmail.com> (http://badassjs.com/)"
|
|
38
|
+
],
|
|
39
|
+
"bugs": {
|
|
40
|
+
"url": "https://github.com/chr33s/pdf/issues"
|
|
41
|
+
},
|
|
42
|
+
"license": "MIT",
|
|
43
|
+
"homepage": "https://github.com/chr33s/pdf",
|
|
44
|
+
"directories": {
|
|
45
|
+
"test": "test"
|
|
46
|
+
}
|
|
47
|
+
}
|
package/src/array.ts
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import Base from "./base.js";
|
|
2
|
+
import type DecodeStream from "./decode-stream.js";
|
|
3
|
+
import type EncodeStream from "./encode-stream.js";
|
|
4
|
+
import { Number as NumberT } from "./number.js";
|
|
5
|
+
import { resolveLength, type LengthLike } from "./utils.js";
|
|
6
|
+
|
|
7
|
+
type LengthType = "count" | "bytes";
|
|
8
|
+
|
|
9
|
+
export default class ArrayT<T = unknown, TResult = T[]> extends Base<TResult> {
|
|
10
|
+
public type: any;
|
|
11
|
+
public length?: LengthLike;
|
|
12
|
+
public lengthType: LengthType;
|
|
13
|
+
|
|
14
|
+
constructor(type: any, length?: LengthLike, lengthType: LengthType = "count") {
|
|
15
|
+
super();
|
|
16
|
+
this.type = type;
|
|
17
|
+
this.length = length;
|
|
18
|
+
this.lengthType = lengthType;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
decode(stream: DecodeStream, parent?: any): TResult {
|
|
22
|
+
const pos = stream.pos;
|
|
23
|
+
const result: any[] = [];
|
|
24
|
+
let ctx = parent;
|
|
25
|
+
let length: number | undefined;
|
|
26
|
+
|
|
27
|
+
if (typeof this.length !== "undefined") {
|
|
28
|
+
length = resolveLength(this.length, stream, parent);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (this.length instanceof NumberT) {
|
|
32
|
+
Object.defineProperties(result, {
|
|
33
|
+
parent: { value: parent },
|
|
34
|
+
_startOffset: { value: pos },
|
|
35
|
+
_currentOffset: { value: 0, writable: true },
|
|
36
|
+
_length: { value: length },
|
|
37
|
+
});
|
|
38
|
+
ctx = result;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (typeof length === "undefined" || this.lengthType === "bytes") {
|
|
42
|
+
let target: number;
|
|
43
|
+
if (typeof length === "number") {
|
|
44
|
+
target = stream.pos + length;
|
|
45
|
+
} else if (parent?._length) {
|
|
46
|
+
target = parent._startOffset + parent._length;
|
|
47
|
+
} else {
|
|
48
|
+
target = stream.length;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
while (stream.pos < target) {
|
|
52
|
+
result.push(this.type.decode(stream, ctx));
|
|
53
|
+
}
|
|
54
|
+
} else {
|
|
55
|
+
for (let i = 0; i < length; i += 1) {
|
|
56
|
+
result.push(this.type.decode(stream, ctx));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return result as unknown as TResult;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
size(array?: T[], ctx?: any, includePointers = true): number {
|
|
64
|
+
if (!array) {
|
|
65
|
+
return this.type.size(null, ctx) * resolveLength(this.length, undefined, ctx);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
let total = 0;
|
|
69
|
+
let context = ctx;
|
|
70
|
+
|
|
71
|
+
if (this.length instanceof NumberT) {
|
|
72
|
+
total += this.length.size();
|
|
73
|
+
context = { parent: ctx, pointerSize: 0 };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
for (const item of array) {
|
|
77
|
+
total += this.type.size(item, context);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (includePointers && this.length instanceof NumberT && context) {
|
|
81
|
+
total += context.pointerSize ?? 0;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return total;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
encode(stream: EncodeStream, array: T[], parent?: any): void {
|
|
88
|
+
let ctx = parent;
|
|
89
|
+
|
|
90
|
+
if (this.length instanceof NumberT) {
|
|
91
|
+
ctx = {
|
|
92
|
+
pointers: [],
|
|
93
|
+
startOffset: stream.pos,
|
|
94
|
+
parent,
|
|
95
|
+
pointerSize: 0,
|
|
96
|
+
};
|
|
97
|
+
ctx.pointerOffset = stream.pos + this.size(array, ctx, false);
|
|
98
|
+
this.length.encode(stream, array.length);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
for (const item of array) {
|
|
102
|
+
this.type.encode(stream, item, ctx);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (this.length instanceof NumberT) {
|
|
106
|
+
let i = 0;
|
|
107
|
+
while (i < ctx.pointers.length) {
|
|
108
|
+
const ptr = ctx.pointers[i++];
|
|
109
|
+
ptr.type.encode(stream, ptr.val, ptr.parent);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
package/src/base.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import DecodeStream from "./decode-stream.js";
|
|
2
|
+
import EncodeStream from "./encode-stream.js";
|
|
3
|
+
|
|
4
|
+
export default abstract class Base<T = unknown> {
|
|
5
|
+
fromBuffer(buffer: Uint8Array): T {
|
|
6
|
+
const stream = new DecodeStream(buffer);
|
|
7
|
+
return (this as any).decode(stream);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
toBuffer(value: T): Uint8Array {
|
|
11
|
+
const size = typeof (this as any).size === "function" ? (this as any).size(value) : 0;
|
|
12
|
+
const buffer = new Uint8Array(size);
|
|
13
|
+
const stream = new EncodeStream(buffer);
|
|
14
|
+
(this as any).encode(stream, value);
|
|
15
|
+
return buffer;
|
|
16
|
+
}
|
|
17
|
+
}
|
package/src/bitfield.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import Base from "./base.js";
|
|
2
|
+
import type DecodeStream from "./decode-stream.js";
|
|
3
|
+
import type EncodeStream from "./encode-stream.js";
|
|
4
|
+
|
|
5
|
+
export default class Bitfield extends Base<Record<string, boolean>> {
|
|
6
|
+
public type: any;
|
|
7
|
+
public flags: Array<string | null | undefined>;
|
|
8
|
+
|
|
9
|
+
constructor(type: any, flags: Array<string | null | undefined> = []) {
|
|
10
|
+
super();
|
|
11
|
+
this.type = type;
|
|
12
|
+
this.flags = flags;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
decode(stream: DecodeStream): Record<string, boolean> {
|
|
16
|
+
const value = this.type.decode(stream);
|
|
17
|
+
const result: Record<string, boolean> = {};
|
|
18
|
+
|
|
19
|
+
this.flags.forEach((flag, index) => {
|
|
20
|
+
if (!flag) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
result[flag] = Boolean(value & (1 << index));
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
size(): number {
|
|
30
|
+
return this.type.size();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
encode(stream: EncodeStream, keys: Record<string, boolean>): void {
|
|
34
|
+
let value = 0;
|
|
35
|
+
this.flags.forEach((flag, index) => {
|
|
36
|
+
if (!flag) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (keys[flag]) {
|
|
40
|
+
value |= 1 << index;
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
this.type.encode(stream, value);
|
|
45
|
+
}
|
|
46
|
+
}
|
package/src/boolean.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import Base from "./base.js";
|
|
2
|
+
import type DecodeStream from "./decode-stream.js";
|
|
3
|
+
import type EncodeStream from "./encode-stream.js";
|
|
4
|
+
|
|
5
|
+
export default class BooleanT extends Base<boolean> {
|
|
6
|
+
public type: any;
|
|
7
|
+
|
|
8
|
+
constructor(type: any) {
|
|
9
|
+
super();
|
|
10
|
+
this.type = type;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
decode(stream: DecodeStream, parent?: any): boolean {
|
|
14
|
+
return Boolean(this.type.decode(stream, parent));
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
size(value?: any, parent?: any): number {
|
|
18
|
+
return this.type.size(value, parent);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
encode(stream: EncodeStream, value: boolean, parent?: any): void {
|
|
22
|
+
this.type.encode(stream, Number(value), parent);
|
|
23
|
+
}
|
|
24
|
+
}
|
package/src/buffer.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import Base from "./base.js";
|
|
2
|
+
import type DecodeStream from "./decode-stream.js";
|
|
3
|
+
import type EncodeStream from "./encode-stream.js";
|
|
4
|
+
import { Number as NumberT } from "./number.js";
|
|
5
|
+
import { resolveLength, type LengthLike } from "./utils.js";
|
|
6
|
+
|
|
7
|
+
export default class BufferT extends Base<Uint8Array> {
|
|
8
|
+
public length: LengthLike;
|
|
9
|
+
|
|
10
|
+
constructor(length: LengthLike) {
|
|
11
|
+
super();
|
|
12
|
+
this.length = length;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
decode(stream: DecodeStream, parent?: any): Uint8Array {
|
|
16
|
+
const length = resolveLength(this.length, stream, parent);
|
|
17
|
+
return stream.readBuffer(length);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
size(value?: Uint8Array, parent?: any): number {
|
|
21
|
+
if (value == null) {
|
|
22
|
+
return resolveLength(this.length, undefined, parent);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
let length = value.length;
|
|
26
|
+
if (this.length instanceof NumberT) {
|
|
27
|
+
length += this.length.size();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return length;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
encode(stream: EncodeStream, value: Uint8Array, _parent?: any): void {
|
|
34
|
+
if (this.length instanceof NumberT) {
|
|
35
|
+
this.length.encode(stream, value.length);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
stream.writeBuffer(value);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// Node back-compat.
|
|
2
|
+
const ENCODING_MAPPING: Record<string, string> = {
|
|
3
|
+
utf16le: "utf-16le",
|
|
4
|
+
ucs2: "utf-16le",
|
|
5
|
+
utf16be: "utf-16be",
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export default class DecodeStream {
|
|
9
|
+
public static readonly TYPES = {
|
|
10
|
+
UInt8: 1,
|
|
11
|
+
UInt16: 2,
|
|
12
|
+
UInt24: 3,
|
|
13
|
+
UInt32: 4,
|
|
14
|
+
Int8: 1,
|
|
15
|
+
Int16: 2,
|
|
16
|
+
Int24: 3,
|
|
17
|
+
Int32: 4,
|
|
18
|
+
Float: 4,
|
|
19
|
+
Double: 8,
|
|
20
|
+
} as const;
|
|
21
|
+
|
|
22
|
+
public pos = 0;
|
|
23
|
+
public length: number;
|
|
24
|
+
public buffer: Uint8Array;
|
|
25
|
+
public view: DataView;
|
|
26
|
+
[key: string]: any;
|
|
27
|
+
|
|
28
|
+
constructor(buffer: Uint8Array | Buffer) {
|
|
29
|
+
this.buffer = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);
|
|
30
|
+
this.view = new DataView(this.buffer.buffer, this.buffer.byteOffset, this.buffer.byteLength);
|
|
31
|
+
this.length = this.buffer.length;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
readString(length: number, encoding: string = "ascii"): string | Uint8Array {
|
|
35
|
+
encoding = ENCODING_MAPPING[encoding] || encoding;
|
|
36
|
+
|
|
37
|
+
const buf = this.readBuffer(length);
|
|
38
|
+
try {
|
|
39
|
+
const decoder = new TextDecoder(encoding);
|
|
40
|
+
return decoder.decode(buf);
|
|
41
|
+
} catch {
|
|
42
|
+
return buf;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
readBuffer(length: number): Uint8Array {
|
|
47
|
+
return this.buffer.slice(this.pos, (this.pos += length));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
readUInt24BE(): number {
|
|
51
|
+
return (this.readUInt16BE() << 8) + this.readUInt8();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
readUInt24LE(): number {
|
|
55
|
+
return this.readUInt16LE() + (this.readUInt8() << 16);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
readInt24BE(): number {
|
|
59
|
+
return (this.readInt16BE() << 8) + this.readUInt8();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
readInt24LE(): number {
|
|
63
|
+
return this.readUInt16LE() + (this.readInt8() << 16);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Generate read methods from DataView prototype
|
|
68
|
+
for (const key of Object.getOwnPropertyNames(DataView.prototype)) {
|
|
69
|
+
if (key.slice(0, 3) === "get") {
|
|
70
|
+
let type = key.slice(3).replace("Ui", "UI");
|
|
71
|
+
if (type === "Float32") {
|
|
72
|
+
type = "Float";
|
|
73
|
+
} else if (type === "Float64") {
|
|
74
|
+
type = "Double";
|
|
75
|
+
}
|
|
76
|
+
const bytes = DecodeStream.TYPES[type as keyof typeof DecodeStream.TYPES];
|
|
77
|
+
if (!bytes) {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
(DecodeStream.prototype as any)["read" + type + (bytes === 1 ? "" : "BE")] = function (
|
|
82
|
+
this: DecodeStream,
|
|
83
|
+
) {
|
|
84
|
+
const ret = (this.view as any)[key](this.pos, false);
|
|
85
|
+
this.pos += bytes;
|
|
86
|
+
return ret;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
if (bytes !== 1) {
|
|
90
|
+
(DecodeStream.prototype as any)["read" + type + "LE"] = function (this: DecodeStream) {
|
|
91
|
+
const ret = (this.view as any)[key](this.pos, true);
|
|
92
|
+
this.pos += bytes;
|
|
93
|
+
return ret;
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import DecodeStream from "./decode-stream.js";
|
|
2
|
+
|
|
3
|
+
const textEncoder = new TextEncoder();
|
|
4
|
+
const isBigEndian = new Uint8Array(new Uint16Array([0x1234]).buffer)[0] === 0x12;
|
|
5
|
+
|
|
6
|
+
export default class EncodeStream {
|
|
7
|
+
public buffer: Uint8Array;
|
|
8
|
+
public view: DataView;
|
|
9
|
+
public pos = 0;
|
|
10
|
+
[key: string]: any;
|
|
11
|
+
|
|
12
|
+
constructor(buffer: Uint8Array) {
|
|
13
|
+
this.buffer = buffer;
|
|
14
|
+
this.view = new DataView(this.buffer.buffer, this.buffer.byteOffset, this.buffer.byteLength);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Ensure the buffer has enough space for the given number of bytes.
|
|
19
|
+
* If not, grow the buffer.
|
|
20
|
+
*/
|
|
21
|
+
ensureCapacity(bytes: number): void {
|
|
22
|
+
const needed = this.pos + bytes;
|
|
23
|
+
if (needed <= this.buffer.length) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
// Grow by at least 2x or to the needed size, whichever is larger
|
|
27
|
+
const newSize = Math.max(this.buffer.length * 2, needed);
|
|
28
|
+
const newBuffer = new Uint8Array(newSize);
|
|
29
|
+
newBuffer.set(this.buffer);
|
|
30
|
+
this.buffer = newBuffer;
|
|
31
|
+
this.view = new DataView(this.buffer.buffer, this.buffer.byteOffset, this.buffer.byteLength);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
writeBuffer(buffer: Uint8Array): void {
|
|
35
|
+
this.ensureCapacity(buffer.length);
|
|
36
|
+
this.buffer.set(buffer, this.pos);
|
|
37
|
+
this.pos += buffer.length;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
writeString(string: string, encoding: string = "ascii"): void {
|
|
41
|
+
let buf: Uint8Array;
|
|
42
|
+
switch (encoding) {
|
|
43
|
+
case "utf16le":
|
|
44
|
+
case "utf16-le":
|
|
45
|
+
case "ucs2": // node treats this the same as utf16.
|
|
46
|
+
buf = stringToUtf16(string, isBigEndian);
|
|
47
|
+
break;
|
|
48
|
+
|
|
49
|
+
case "utf16be":
|
|
50
|
+
case "utf16-be":
|
|
51
|
+
buf = stringToUtf16(string, !isBigEndian);
|
|
52
|
+
break;
|
|
53
|
+
|
|
54
|
+
case "utf8":
|
|
55
|
+
buf = textEncoder.encode(string);
|
|
56
|
+
break;
|
|
57
|
+
|
|
58
|
+
case "ascii":
|
|
59
|
+
buf = stringToAscii(string);
|
|
60
|
+
break;
|
|
61
|
+
|
|
62
|
+
default:
|
|
63
|
+
throw new Error(`Unsupported encoding: ${encoding}`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
this.writeBuffer(buf);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
writeUInt24BE(val: number): void {
|
|
70
|
+
this.ensureCapacity(3);
|
|
71
|
+
this.buffer[this.pos++] = (val >>> 16) & 0xff;
|
|
72
|
+
this.buffer[this.pos++] = (val >>> 8) & 0xff;
|
|
73
|
+
this.buffer[this.pos++] = val & 0xff;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
writeUInt24LE(val: number): void {
|
|
77
|
+
this.ensureCapacity(3);
|
|
78
|
+
this.buffer[this.pos++] = val & 0xff;
|
|
79
|
+
this.buffer[this.pos++] = (val >>> 8) & 0xff;
|
|
80
|
+
this.buffer[this.pos++] = (val >>> 16) & 0xff;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
writeInt24BE(val: number): void {
|
|
84
|
+
if (val >= 0) {
|
|
85
|
+
this.writeUInt24BE(val);
|
|
86
|
+
} else {
|
|
87
|
+
this.writeUInt24BE(val + 0xffffff + 1);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
writeInt24LE(val: number): void {
|
|
92
|
+
if (val >= 0) {
|
|
93
|
+
this.writeUInt24LE(val);
|
|
94
|
+
} else {
|
|
95
|
+
this.writeUInt24LE(val + 0xffffff + 1);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
fill(val: number, length: number): void {
|
|
100
|
+
this.ensureCapacity(length);
|
|
101
|
+
this.buffer.fill(val, this.pos, this.pos + length);
|
|
102
|
+
this.pos += length;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function stringToUtf16(string: string, swap: boolean): Uint8Array {
|
|
107
|
+
const buf = new Uint16Array(string.length);
|
|
108
|
+
for (let i = 0; i < string.length; i++) {
|
|
109
|
+
let code = string.charCodeAt(i);
|
|
110
|
+
if (swap) {
|
|
111
|
+
code = (code >> 8) | ((code & 0xff) << 8);
|
|
112
|
+
}
|
|
113
|
+
buf[i] = code;
|
|
114
|
+
}
|
|
115
|
+
return new Uint8Array(buf.buffer);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function stringToAscii(string: string): Uint8Array {
|
|
119
|
+
const buf = new Uint8Array(string.length);
|
|
120
|
+
for (let i = 0; i < string.length; i++) {
|
|
121
|
+
// Match node.js behavior - encoding allows 8-bit rather than 7-bit.
|
|
122
|
+
buf[i] = string.charCodeAt(i);
|
|
123
|
+
}
|
|
124
|
+
return buf;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Generate write methods from DataView prototype
|
|
128
|
+
for (const key of Object.getOwnPropertyNames(DataView.prototype)) {
|
|
129
|
+
if (key.slice(0, 3) === "set") {
|
|
130
|
+
let type = key.slice(3).replace("Ui", "UI");
|
|
131
|
+
if (type === "Float32") {
|
|
132
|
+
type = "Float";
|
|
133
|
+
} else if (type === "Float64") {
|
|
134
|
+
type = "Double";
|
|
135
|
+
}
|
|
136
|
+
const bytes = DecodeStream.TYPES[type as keyof typeof DecodeStream.TYPES];
|
|
137
|
+
if (!bytes) {
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
(EncodeStream.prototype as any)["write" + type + (bytes === 1 ? "" : "BE")] = function (
|
|
142
|
+
this: EncodeStream,
|
|
143
|
+
value: number,
|
|
144
|
+
) {
|
|
145
|
+
this.ensureCapacity(bytes);
|
|
146
|
+
(this.view as any)[key](this.pos, value, false);
|
|
147
|
+
this.pos += bytes;
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
if (bytes !== 1) {
|
|
151
|
+
(EncodeStream.prototype as any)["write" + type + "LE"] = function (
|
|
152
|
+
this: EncodeStream,
|
|
153
|
+
value: number,
|
|
154
|
+
) {
|
|
155
|
+
this.ensureCapacity(bytes);
|
|
156
|
+
(this.view as any)[key](this.pos, value, true);
|
|
157
|
+
this.pos += bytes;
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|