@andrew_l/tl-pack 0.0.2

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.
@@ -0,0 +1,88 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ export declare class BinaryReader {
3
+ private target;
4
+ private targetView;
5
+ private _last?;
6
+ private _dict;
7
+ offset: number;
8
+ length: number;
9
+ /**
10
+ * Small utility class to read binary data.
11
+ * @param data {Buffer}
12
+ */
13
+ constructor(data: Buffer | Uint8Array);
14
+ readByte(): number;
15
+ readInt32(signed?: boolean): number;
16
+ readInt16(signed?: boolean): number;
17
+ readInt8(signed?: boolean): number;
18
+ /**
19
+ * Reads a real floating point (4 bytes) value.
20
+ * @returns {number}
21
+ */
22
+ readFloat(): number;
23
+ /**
24
+ * Reads a real floating point (8 bytes) value.
25
+ * @returns {BigInteger}
26
+ */
27
+ readDouble(): number;
28
+ /**
29
+ * Read the given amount of bytes, or -1 to read all remaining.
30
+ * @param length {number}
31
+ */
32
+ assertRead(length: number): void;
33
+ /**
34
+ * Gets the byte array representing the current buffer as a whole.
35
+ * @returns {Buffer}
36
+ */
37
+ getBuffer(): Uint8Array | Buffer;
38
+ readNull(): null;
39
+ readLength(): number;
40
+ /**
41
+ * @returns {Uint8Array | Buffer}
42
+ */
43
+ readBytes(): Uint8Array | Buffer;
44
+ /**
45
+ * Reads encoded string.
46
+ * @returns {string}
47
+ */
48
+ readString(): string;
49
+ /**
50
+ * Reads a boolean value.
51
+ * @returns {boolean}
52
+ */
53
+ readBool(): boolean;
54
+ /**
55
+ * Reads and converts Unix time
56
+ * into a Javascript {Date} object.
57
+ * @returns {Date}
58
+ */
59
+ readDate(): Date;
60
+ /**
61
+ * Reads a object.
62
+ */
63
+ readObject(): any;
64
+ readDictionary(): string | null;
65
+ readMap(checkConstructor?: boolean): Record<string, any>;
66
+ decode(value: Buffer | Uint8Array): any;
67
+ /**
68
+ * Reads a vector (a list) of objects.
69
+ * @returns {any[]}
70
+ */
71
+ readVector(checkConstructor?: boolean): any[];
72
+ /**
73
+ * Tells the current position on the stream.
74
+ * @returns {number}
75
+ */
76
+ tellPosition(): number;
77
+ /**
78
+ * Sets the current position on the stream.
79
+ * @param position
80
+ */
81
+ setPosition(position: number): void;
82
+ /**
83
+ * Seeks the stream position given an offset from the current position.
84
+ * The offset may be negative.
85
+ * @param offset
86
+ */
87
+ seek(offset: number): void;
88
+ }
@@ -0,0 +1,280 @@
1
+ import { CORE_TYPES } from './constants.js';
2
+ import { bytesToUtf8 } from './helpers.js';
3
+ export class BinaryReader {
4
+ target;
5
+ targetView;
6
+ _last;
7
+ _dict;
8
+ offset;
9
+ length;
10
+ /**
11
+ * Small utility class to read binary data.
12
+ * @param data {Buffer}
13
+ */
14
+ constructor(data) {
15
+ this.target = data;
16
+ this.targetView = new DataView(data.buffer, 0, data.length);
17
+ this._last = undefined;
18
+ this._dict = new Map();
19
+ this.offset = 0;
20
+ this.length = data.length;
21
+ }
22
+ readByte() {
23
+ this.assertRead(1);
24
+ this._last = this.target[this.offset++];
25
+ return this._last;
26
+ }
27
+ readInt32(signed = true) {
28
+ this.assertRead(4);
29
+ if (signed) {
30
+ this._last = this.targetView.getInt32(this.offset, true);
31
+ }
32
+ else {
33
+ this._last = this.targetView.getUint32(this.offset, true);
34
+ }
35
+ this.offset += 4;
36
+ return this._last;
37
+ }
38
+ readInt16(signed = true) {
39
+ this.assertRead(2);
40
+ if (signed) {
41
+ this._last = this.targetView.getInt16(this.offset, true);
42
+ }
43
+ else {
44
+ this._last = this.targetView.getUint16(this.offset);
45
+ }
46
+ this.offset += 2;
47
+ return this._last;
48
+ }
49
+ readInt8(signed = true) {
50
+ this.assertRead(1);
51
+ if (signed) {
52
+ this._last = this.targetView.getInt8(this.offset);
53
+ }
54
+ else {
55
+ this._last = this.targetView.getInt8(this.offset);
56
+ }
57
+ this.offset += 1;
58
+ return this._last;
59
+ }
60
+ /**
61
+ * Reads a real floating point (4 bytes) value.
62
+ * @returns {number}
63
+ */
64
+ readFloat() {
65
+ this.assertRead(4);
66
+ this._last = this.targetView.getFloat32(this.offset, true);
67
+ this.offset += 4;
68
+ return this._last;
69
+ }
70
+ /**
71
+ * Reads a real floating point (8 bytes) value.
72
+ * @returns {BigInteger}
73
+ */
74
+ readDouble() {
75
+ this.assertRead(8);
76
+ this._last = this.targetView.getFloat64(this.offset, true);
77
+ this.offset += 8;
78
+ return this._last;
79
+ }
80
+ /**
81
+ * Read the given amount of bytes, or -1 to read all remaining.
82
+ * @param length {number}
83
+ */
84
+ assertRead(length) {
85
+ if (this.length < this.offset + +length) {
86
+ const left = this.target.length - this.offset;
87
+ const result = this.target.subarray(this.offset, this.offset + left);
88
+ const err = new Error(`No more data left to read (need ${length}, got ${left}: ${result}); last read ${this._last}`);
89
+ err.incomplete = true;
90
+ Error.captureStackTrace(err, this.assertRead);
91
+ throw err;
92
+ }
93
+ }
94
+ /**
95
+ * Gets the byte array representing the current buffer as a whole.
96
+ * @returns {Buffer}
97
+ */
98
+ getBuffer() {
99
+ return this.target;
100
+ }
101
+ readNull() {
102
+ const value = this.readByte();
103
+ if (value === CORE_TYPES.Null) {
104
+ return null;
105
+ }
106
+ throw new Error(`Invalid boolean code ${value.toString(16)}`);
107
+ }
108
+ readLength() {
109
+ const firstByte = this.readByte();
110
+ if (firstByte === 254) {
111
+ return this.readByte() | (this.readByte() << 8) | (this.readByte() << 16);
112
+ }
113
+ return firstByte;
114
+ }
115
+ /**
116
+ * @returns {Uint8Array | Buffer}
117
+ */
118
+ readBytes() {
119
+ const length = this.readLength();
120
+ this.assertRead(length);
121
+ const bytes = this.target.subarray(this.offset, this.offset + length);
122
+ this.offset += bytes.length;
123
+ return bytes;
124
+ }
125
+ /**
126
+ * Reads encoded string.
127
+ * @returns {string}
128
+ */
129
+ readString() {
130
+ const length = this.readLength();
131
+ this.assertRead(length);
132
+ const bytes = this.target.subarray(this.offset, this.offset + length);
133
+ this.offset += bytes.length;
134
+ return bytesToUtf8(bytes);
135
+ }
136
+ /**
137
+ * Reads a boolean value.
138
+ * @returns {boolean}
139
+ */
140
+ readBool() {
141
+ const value = this.readByte();
142
+ if (value === CORE_TYPES.BoolTrue) {
143
+ return true;
144
+ }
145
+ else if (value === CORE_TYPES.BoolFalse) {
146
+ return false;
147
+ }
148
+ else {
149
+ throw new Error(`Invalid boolean code ${value.toString(16)}`);
150
+ }
151
+ }
152
+ /**
153
+ * Reads and converts Unix time
154
+ * into a Javascript {Date} object.
155
+ * @returns {Date}
156
+ */
157
+ readDate() {
158
+ const value = this.readDouble();
159
+ return new Date(value * 1000);
160
+ }
161
+ /**
162
+ * Reads a object.
163
+ */
164
+ readObject() {
165
+ const constructorId = this.readByte();
166
+ switch (constructorId) {
167
+ case CORE_TYPES.None:
168
+ return this.readObject();
169
+ case CORE_TYPES.BoolTrue:
170
+ return true;
171
+ case CORE_TYPES.BoolFalse:
172
+ return false;
173
+ case CORE_TYPES.Vector:
174
+ return this.readVector(false);
175
+ case CORE_TYPES.Null:
176
+ return null;
177
+ case CORE_TYPES.Binary:
178
+ return this.readBytes();
179
+ case CORE_TYPES.String:
180
+ return this.readString();
181
+ case CORE_TYPES.Date:
182
+ return this.readDate();
183
+ case CORE_TYPES.Int32:
184
+ return this.readInt32();
185
+ case CORE_TYPES.Int16:
186
+ return this.readInt16();
187
+ case CORE_TYPES.Int8:
188
+ return this.readInt8();
189
+ case CORE_TYPES.UInt32:
190
+ return this.readInt32(false);
191
+ case CORE_TYPES.UInt16:
192
+ return this.readInt16(false);
193
+ case CORE_TYPES.UInt8:
194
+ return this.readInt8(false);
195
+ case CORE_TYPES.Float:
196
+ return this.readFloat();
197
+ case CORE_TYPES.Double:
198
+ return this.readDouble();
199
+ case CORE_TYPES.Map:
200
+ return this.readMap(false);
201
+ }
202
+ throw new Error(`Invalid constructor = ${CORE_TYPES[constructorId] || constructorId}, offset = ${this.offset - 1}`);
203
+ }
204
+ readDictionary() {
205
+ const constructorId = this.readByte();
206
+ switch (constructorId) {
207
+ case CORE_TYPES.DictIndex: {
208
+ const idx = this.readLength();
209
+ return this._dict.get(idx);
210
+ }
211
+ case CORE_TYPES.DictValue: {
212
+ const key = this.readString();
213
+ this._dict.set(this._dict.size + 1, key);
214
+ return key;
215
+ }
216
+ case CORE_TYPES.None: {
217
+ return null;
218
+ }
219
+ }
220
+ this.seek(-1);
221
+ return null;
222
+ }
223
+ readMap(checkConstructor = true) {
224
+ if (checkConstructor && this.readByte() !== CORE_TYPES.Map) {
225
+ throw new Error('Invalid constructor code, map was expected');
226
+ }
227
+ const temp = {};
228
+ let key = this.readDictionary();
229
+ while (key !== null) {
230
+ temp[key] = this.readObject();
231
+ key = this.readDictionary();
232
+ }
233
+ return temp;
234
+ }
235
+ decode(value) {
236
+ this.target = value;
237
+ this.targetView = new DataView(value.buffer, 0, value.length);
238
+ this._last = undefined;
239
+ this.offset = 0;
240
+ this.length = value.length;
241
+ return this.readObject();
242
+ }
243
+ /**
244
+ * Reads a vector (a list) of objects.
245
+ * @returns {any[]}
246
+ */
247
+ readVector(checkConstructor = true) {
248
+ if (checkConstructor && this.readByte() !== CORE_TYPES.Vector) {
249
+ throw new Error('Invalid constructor code, vector was expected');
250
+ }
251
+ const count = this.readLength();
252
+ const temp = [];
253
+ for (let i = 0; i < count; i++) {
254
+ temp.push(this.readObject());
255
+ }
256
+ return temp;
257
+ }
258
+ /**
259
+ * Tells the current position on the stream.
260
+ * @returns {number}
261
+ */
262
+ tellPosition() {
263
+ return this.offset;
264
+ }
265
+ /**
266
+ * Sets the current position on the stream.
267
+ * @param position
268
+ */
269
+ setPosition(position) {
270
+ this.offset = position;
271
+ }
272
+ /**
273
+ * Seeks the stream position given an offset from the current position.
274
+ * The offset may be negative.
275
+ * @param offset
276
+ */
277
+ seek(offset) {
278
+ this.offset += offset;
279
+ }
280
+ }
@@ -0,0 +1,29 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ export declare class BinaryWriter {
3
+ private target;
4
+ private targetView;
5
+ private dict;
6
+ offset: number;
7
+ constructor();
8
+ allocate(size: number): void;
9
+ makeRoom(end: number): void;
10
+ get safeEnd(): number;
11
+ getBuffer(): Uint8Array | Buffer;
12
+ writeByte(value: number): void;
13
+ writeBool(value: boolean): void;
14
+ writeNull(): void;
15
+ writeInt32(value: number, signed?: boolean): void;
16
+ writeInt16(value: number, signed?: boolean): void;
17
+ writeInt8(value: number, signed?: boolean): void;
18
+ writeFloat(value: number): void;
19
+ writeDouble(value: number): void;
20
+ writeDate(value: number | Date): void;
21
+ writeString(value: string): void;
22
+ writeBytes(value: Buffer | Uint8Array): void;
23
+ writeLength(value: number): void;
24
+ writeVector(value: Array<any>): void;
25
+ writeMap(object: Record<string, any>): void;
26
+ wireDictionary(value: string): void;
27
+ encode(value: any): Uint8Array | Buffer;
28
+ writeObject(value: any): void;
29
+ }
@@ -0,0 +1,266 @@
1
+ import { CORE_TYPES } from './constants.js';
2
+ import { coreType } from './helpers.js';
3
+ const hasNodeBuffer = typeof Buffer !== 'undefined';
4
+ const MAX_BUFFER_SIZE = hasNodeBuffer ? 0x100000000 : 0x7fd00000;
5
+ const textEncoder = new TextEncoder();
6
+ const writeUtf8 = hasNodeBuffer
7
+ ? function (target, value, offset) {
8
+ const length = target.utf8Write(value, offset, 0xffffffff);
9
+ return length;
10
+ }
11
+ : function (target, value, offset) {
12
+ return textEncoder.encodeInto(value, target.subarray(offset)).written;
13
+ };
14
+ function byteArrayAllocate(length) {
15
+ if (hasNodeBuffer) {
16
+ return Buffer.allocUnsafeSlow(length);
17
+ }
18
+ return new Uint8Array(length);
19
+ }
20
+ export class BinaryWriter {
21
+ target;
22
+ targetView;
23
+ dict;
24
+ offset;
25
+ constructor() {
26
+ this.target = Buffer.alloc(0);
27
+ this.dict = new Map();
28
+ this.offset = 0;
29
+ this.target = byteArrayAllocate(8192);
30
+ this.targetView = new DataView(this.target.buffer, 0, this.target.length);
31
+ }
32
+ allocate(size) {
33
+ const position = this.offset + size;
34
+ if (this.safeEnd < position) {
35
+ this.makeRoom(position);
36
+ }
37
+ }
38
+ makeRoom(end) {
39
+ let start = 0;
40
+ let newSize = 0;
41
+ let target = this.target;
42
+ if (end > 0x1000000) {
43
+ // special handling for really large buffers
44
+ if (end - start > MAX_BUFFER_SIZE)
45
+ throw new Error('Packed buffer would be larger than maximum buffer size');
46
+ newSize = Math.min(MAX_BUFFER_SIZE, Math.round(Math.max((end - start) * (end > 0x4000000 ? 1.25 : 2), 0x400000) / 0x1000) *
47
+ 0x1000);
48
+ }
49
+ else {
50
+ // faster handling for smaller buffers
51
+ newSize = ((Math.max((end - start) << 2, target.length - 1) >> 12) + 1) << 12;
52
+ }
53
+ const newBuffer = byteArrayAllocate(newSize);
54
+ const newView = new DataView(newBuffer.buffer, 0, newSize);
55
+ end = Math.min(end, target.length);
56
+ if ('copy' in target) {
57
+ target.copy(newBuffer, 0, start, end);
58
+ }
59
+ else {
60
+ newBuffer.set(target.slice(start, end));
61
+ }
62
+ this.target = newBuffer;
63
+ this.targetView = newView;
64
+ }
65
+ get safeEnd() {
66
+ return this.target.length - 10;
67
+ }
68
+ getBuffer() {
69
+ return this.target.subarray(0, this.offset);
70
+ }
71
+ writeByte(value) {
72
+ this.allocate(1);
73
+ this.target[this.offset++] = value;
74
+ }
75
+ writeBool(value) {
76
+ if (value) {
77
+ this.writeByte(CORE_TYPES.BoolTrue);
78
+ }
79
+ else {
80
+ this.writeByte(CORE_TYPES.BoolFalse);
81
+ }
82
+ }
83
+ writeNull() {
84
+ this.writeByte(CORE_TYPES.Null);
85
+ }
86
+ writeInt32(value, signed = true) {
87
+ this.allocate(4);
88
+ if (signed) {
89
+ this.targetView.setInt32(this.offset, value, true);
90
+ }
91
+ else {
92
+ this.targetView.setUint32(this.offset, value, true);
93
+ }
94
+ this.offset += 4;
95
+ }
96
+ writeInt16(value, signed = true) {
97
+ this.allocate(2);
98
+ if (signed) {
99
+ this.targetView.setInt16(this.offset, value, true);
100
+ }
101
+ else {
102
+ this.targetView.setUint16(this.offset, value, true);
103
+ }
104
+ this.offset += 2;
105
+ }
106
+ writeInt8(value, signed = true) {
107
+ this.allocate(1);
108
+ this.target[this.offset++] = value;
109
+ }
110
+ writeFloat(value) {
111
+ this.allocate(4);
112
+ this.targetView.setFloat32(this.offset, value, true);
113
+ this.offset += 4;
114
+ }
115
+ writeDouble(value) {
116
+ this.allocate(8);
117
+ this.targetView.setFloat64(this.offset, value, true);
118
+ this.offset += 8;
119
+ }
120
+ writeDate(value) {
121
+ let timestamp = 0;
122
+ if (value instanceof Date) {
123
+ timestamp = Math.floor(value.getTime() / 1000);
124
+ }
125
+ else if (typeof value === 'number') {
126
+ timestamp = value;
127
+ }
128
+ this.writeDouble(timestamp);
129
+ }
130
+ writeString(value) {
131
+ let start = this.offset;
132
+ let require = value.length << 2;
133
+ if (require < 254) {
134
+ require += 1;
135
+ this.offset += 1;
136
+ }
137
+ else {
138
+ require += 4;
139
+ this.offset += 4;
140
+ }
141
+ this.allocate(require);
142
+ const bytes = writeUtf8(this.target, value, this.offset);
143
+ if (require < 254) {
144
+ this.target[start++] = bytes;
145
+ }
146
+ else {
147
+ this.target[start++] = 254;
148
+ this.target[start++] = bytes % 256;
149
+ this.target[start++] = (bytes >> 8) % 256;
150
+ this.target[start++] = (bytes >> 16) % 256;
151
+ }
152
+ this.offset += bytes;
153
+ // this.target.set(bytes, this.offset);
154
+ // this.offset += bytes.length;
155
+ }
156
+ writeBytes(value) {
157
+ const length = value.length;
158
+ this.writeLength(length);
159
+ this.allocate(length);
160
+ this.target.set(value, this.offset);
161
+ this.offset += length;
162
+ }
163
+ writeLength(value) {
164
+ if (value < 254) {
165
+ this.allocate(1);
166
+ this.target[this.offset++] = value;
167
+ }
168
+ else {
169
+ this.allocate(4);
170
+ this.target[this.offset++] = 254;
171
+ this.target[this.offset++] = value % 256;
172
+ this.target[this.offset++] = (value >> 8) % 256;
173
+ this.target[this.offset++] = (value >> 16) % 256;
174
+ }
175
+ }
176
+ writeVector(value) {
177
+ const length = value.length;
178
+ this.writeLength(length);
179
+ for (let i = 0; i < length; i++) {
180
+ this.writeObject(value[i]);
181
+ }
182
+ }
183
+ writeMap(object) {
184
+ for (const key in object) {
185
+ this.wireDictionary(key);
186
+ this.writeObject(object[key]);
187
+ }
188
+ this.writeByte(CORE_TYPES.None);
189
+ }
190
+ wireDictionary(value) {
191
+ if (this.dict.has(value)) {
192
+ const idx = this.dict.get(value);
193
+ this.writeByte(CORE_TYPES.DictIndex);
194
+ this.writeLength(idx);
195
+ }
196
+ else {
197
+ const newIndex = this.dict.size + 1;
198
+ this.dict.set(value, newIndex);
199
+ this.writeByte(CORE_TYPES.DictValue);
200
+ this.writeString(value);
201
+ }
202
+ }
203
+ encode(value) {
204
+ const start = this.offset;
205
+ this.writeObject(value);
206
+ const end = this.offset;
207
+ this.offset = start;
208
+ return this.target.subarray(start, end);
209
+ }
210
+ writeObject(value) {
211
+ if (value === undefined)
212
+ return;
213
+ const constructorId = coreType(value);
214
+ if (constructorId === CORE_TYPES.None) {
215
+ throw new TypeError(`Invalid core type of ${typeof value}`);
216
+ }
217
+ if (![CORE_TYPES.BoolFalse, CORE_TYPES.BoolTrue, CORE_TYPES.Null].includes(constructorId)) {
218
+ this.writeByte(constructorId);
219
+ }
220
+ switch (constructorId) {
221
+ case CORE_TYPES.BoolFalse:
222
+ case CORE_TYPES.BoolTrue: {
223
+ return this.writeBool(value);
224
+ }
225
+ case CORE_TYPES.Date: {
226
+ return this.writeDate(value);
227
+ }
228
+ case CORE_TYPES.Int32: {
229
+ return this.writeInt32(value);
230
+ }
231
+ case CORE_TYPES.Int16: {
232
+ return this.writeInt16(value);
233
+ }
234
+ case CORE_TYPES.Int8: {
235
+ return this.writeInt8(value);
236
+ }
237
+ case CORE_TYPES.UInt32: {
238
+ return this.writeInt32(value, false);
239
+ }
240
+ case CORE_TYPES.UInt16: {
241
+ return this.writeInt16(value, false);
242
+ }
243
+ case CORE_TYPES.UInt8: {
244
+ return this.writeInt8(value, false);
245
+ }
246
+ case CORE_TYPES.Double: {
247
+ return this.writeDouble(value);
248
+ }
249
+ case CORE_TYPES.Float: {
250
+ return this.writeFloat(value);
251
+ }
252
+ case CORE_TYPES.Null: {
253
+ return this.writeNull();
254
+ }
255
+ case CORE_TYPES.String: {
256
+ return this.writeString(value);
257
+ }
258
+ case CORE_TYPES.Vector: {
259
+ return this.writeVector(value);
260
+ }
261
+ case CORE_TYPES.Map: {
262
+ return this.writeMap(value);
263
+ }
264
+ }
265
+ }
266
+ }
@@ -0,0 +1,21 @@
1
+ export declare enum CORE_TYPES {
2
+ None = 0,
3
+ Binary = 1,
4
+ BoolFalse = 2,
5
+ BoolTrue = 3,
6
+ Null = 4,
7
+ Date = 5,
8
+ Vector = 6,
9
+ Int32 = 7,
10
+ Int16 = 8,
11
+ Int8 = 9,
12
+ UInt32 = 10,
13
+ UInt16 = 11,
14
+ UInt8 = 12,
15
+ Float = 13,
16
+ Double = 14,
17
+ Map = 15,
18
+ DictValue = 16,
19
+ DictIndex = 17,
20
+ String = 18
21
+ }
@@ -0,0 +1,23 @@
1
+ export var CORE_TYPES;
2
+ (function (CORE_TYPES) {
3
+ CORE_TYPES[CORE_TYPES["None"] = 0] = "None";
4
+ CORE_TYPES[CORE_TYPES["Binary"] = 1] = "Binary";
5
+ CORE_TYPES[CORE_TYPES["BoolFalse"] = 2] = "BoolFalse";
6
+ CORE_TYPES[CORE_TYPES["BoolTrue"] = 3] = "BoolTrue";
7
+ CORE_TYPES[CORE_TYPES["Null"] = 4] = "Null";
8
+ CORE_TYPES[CORE_TYPES["Date"] = 5] = "Date";
9
+ CORE_TYPES[CORE_TYPES["Vector"] = 6] = "Vector";
10
+ CORE_TYPES[CORE_TYPES["Int32"] = 7] = "Int32";
11
+ CORE_TYPES[CORE_TYPES["Int16"] = 8] = "Int16";
12
+ CORE_TYPES[CORE_TYPES["Int8"] = 9] = "Int8";
13
+ CORE_TYPES[CORE_TYPES["UInt32"] = 10] = "UInt32";
14
+ CORE_TYPES[CORE_TYPES["UInt16"] = 11] = "UInt16";
15
+ CORE_TYPES[CORE_TYPES["UInt8"] = 12] = "UInt8";
16
+ CORE_TYPES[CORE_TYPES["Float"] = 13] = "Float";
17
+ CORE_TYPES[CORE_TYPES["Double"] = 14] = "Double";
18
+ CORE_TYPES[CORE_TYPES["Map"] = 15] = "Map";
19
+ CORE_TYPES[CORE_TYPES["DictValue"] = 16] = "DictValue";
20
+ CORE_TYPES[CORE_TYPES["DictIndex"] = 17] = "DictIndex";
21
+ CORE_TYPES[CORE_TYPES["String"] = 18] = "String";
22
+ })(CORE_TYPES || (CORE_TYPES = {}));
23
+ ;
@@ -0,0 +1,8 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ import { CORE_TYPES } from './constants.js';
3
+ export declare function concatUint8Arrays(arrays: ArrayLike<number>[]): Uint8Array;
4
+ export declare function bytesToUtf8(bytes: Buffer | Uint8Array): string;
5
+ export declare function utf8ToBytes(string: string): Uint8Array;
6
+ export declare function serializeBytes(value: Buffer | Uint8Array | string | any): Uint8Array;
7
+ export declare function serializeLength(value: number): Buffer;
8
+ export declare function coreType(value: any): CORE_TYPES;
@@ -0,0 +1,105 @@
1
+ import { CORE_TYPES } from './constants.js';
2
+ const textEncoder = new TextEncoder();
3
+ const textDecoder = new TextDecoder();
4
+ export function concatUint8Arrays(arrays) {
5
+ let totalLength = 0;
6
+ for (const array of arrays) {
7
+ totalLength += array.length;
8
+ }
9
+ const result = new Uint8Array(totalLength);
10
+ let offset = 0;
11
+ for (const array of arrays) {
12
+ result.set(array, offset);
13
+ offset += array.length;
14
+ }
15
+ return result;
16
+ }
17
+ export function bytesToUtf8(bytes) {
18
+ return textDecoder.decode(bytes);
19
+ }
20
+ export function utf8ToBytes(string) {
21
+ const buff = new Uint8Array(string.length << 2);
22
+ const length = textEncoder.encodeInto(string, buff).written;
23
+ return buff.subarray(0, length);
24
+ }
25
+ export function serializeBytes(value) {
26
+ let data;
27
+ if (typeof value === 'string') {
28
+ data = utf8ToBytes(value);
29
+ }
30
+ else if (value instanceof Buffer) {
31
+ data = value;
32
+ }
33
+ else if (value instanceof Uint8Array) {
34
+ data = value;
35
+ }
36
+ else {
37
+ throw Error(`Bytes or str expected, not ${value.constructor.name}`);
38
+ }
39
+ let length = data.length;
40
+ let header;
41
+ if (length < 254) {
42
+ header = new Uint8Array(1);
43
+ header[0] = length;
44
+ }
45
+ else {
46
+ header = new Uint8Array(4);
47
+ header[0] = 254;
48
+ header[1] = length % 256;
49
+ header[2] = (length >> 8) % 256;
50
+ header[3] = (length >> 16) % 256;
51
+ }
52
+ return concatUint8Arrays([header, data]);
53
+ }
54
+ export function serializeLength(value) {
55
+ if (value < 254) {
56
+ return Buffer.from([value]);
57
+ }
58
+ return Buffer.from([254, value % 256, (value >> 8) % 256, (value >> 16) % 256]);
59
+ }
60
+ export function coreType(value) {
61
+ switch (typeof value) {
62
+ case 'string': {
63
+ return CORE_TYPES.String;
64
+ }
65
+ case 'boolean': {
66
+ return value ? CORE_TYPES.BoolTrue : CORE_TYPES.BoolFalse;
67
+ }
68
+ case 'number': {
69
+ if (value >= 0 && value <= 255) {
70
+ return CORE_TYPES.UInt8;
71
+ }
72
+ else if (value >= 0 && value <= 65535) {
73
+ return CORE_TYPES.UInt16;
74
+ }
75
+ else if (value >= 0 && value <= 4294967295) {
76
+ return CORE_TYPES.UInt32;
77
+ }
78
+ else if (value >= -128 && value <= 127) {
79
+ return CORE_TYPES.Int8;
80
+ }
81
+ else if (value >= -32768 && value <= 32767) {
82
+ return CORE_TYPES.Int16;
83
+ }
84
+ else if (value >= -2147483648 && value <= 2147483647) {
85
+ return CORE_TYPES.Int32;
86
+ }
87
+ return CORE_TYPES.Double;
88
+ }
89
+ case 'object': {
90
+ if (value === null)
91
+ return CORE_TYPES.Null;
92
+ if (value instanceof Date) {
93
+ return CORE_TYPES.Date;
94
+ }
95
+ if (Array.isArray(value)) {
96
+ return CORE_TYPES.Vector;
97
+ }
98
+ // @ts-ignore
99
+ if (toString.call(value) === '[object Object]') {
100
+ return CORE_TYPES.Map;
101
+ }
102
+ }
103
+ }
104
+ return CORE_TYPES.None;
105
+ }
@@ -0,0 +1,3 @@
1
+ export { BinaryWriter } from './BinaryWriter.js';
2
+ export { BinaryReader } from './BinaryReader.js';
3
+ export * from './constants.js';
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { BinaryWriter } from './BinaryWriter.js';
2
+ export { BinaryReader } from './BinaryReader.js';
3
+ export * from './constants.js';
@@ -0,0 +1,14 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ /// <reference types="node" resolution-mode="require"/>
3
+ import { Transform, type TransformCallback, type TransformOptions } from 'stream';
4
+ export declare class TLEncode extends Transform {
5
+ private writer;
6
+ constructor(options?: TransformOptions);
7
+ _transform(chunk: any, encoding: BufferEncoding, callback: TransformCallback): void;
8
+ }
9
+ export declare class TLDecode extends Transform {
10
+ private reader;
11
+ private incompleteBuffer;
12
+ constructor(options?: TransformOptions);
13
+ _transform(chunk: any, encoding: BufferEncoding, callback: TransformCallback): void;
14
+ }
package/dist/stream.js ADDED
@@ -0,0 +1,47 @@
1
+ import { Transform } from 'stream';
2
+ import { BinaryWriter } from './BinaryWriter.js';
3
+ import { BinaryReader } from './BinaryReader.js';
4
+ export class TLEncode extends Transform {
5
+ writer;
6
+ constructor(options) {
7
+ if (!options)
8
+ options = {};
9
+ options.writableObjectMode = true;
10
+ super(options);
11
+ this.writer = new BinaryWriter();
12
+ }
13
+ _transform(chunk, encoding, callback) {
14
+ const buff = this.writer.encode(chunk);
15
+ this.push(buff);
16
+ callback();
17
+ }
18
+ }
19
+ export class TLDecode extends Transform {
20
+ reader;
21
+ incompleteBuffer;
22
+ constructor(options) {
23
+ if (!options)
24
+ options = {};
25
+ options.objectMode = true;
26
+ super(options);
27
+ this.incompleteBuffer = null;
28
+ this.reader = new BinaryReader(new Uint8Array(8192));
29
+ }
30
+ _transform(chunk, encoding, callback) {
31
+ if (this.incompleteBuffer) {
32
+ chunk = Buffer.concat([this.incompleteBuffer, chunk]);
33
+ this.incompleteBuffer = null;
34
+ }
35
+ try {
36
+ const value = this.reader.decode(chunk);
37
+ return callback(null, value);
38
+ }
39
+ catch (err) {
40
+ if (err?.incomplete) {
41
+ this.incompleteBuffer = chunk;
42
+ return callback();
43
+ }
44
+ return callback(err);
45
+ }
46
+ }
47
+ }
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@andrew_l/tl-pack",
3
+ "version": "0.0.2",
4
+ "description": "",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "nodemon src/playground.ts",
8
+ "build": "rm -rf dist && npx tsc"
9
+ },
10
+ "keywords": [],
11
+ "author": "Andrew L.",
12
+ "license": "ISC",
13
+ "types": "./dist",
14
+ "files": [
15
+ "dist/**"
16
+ ],
17
+ "exports": {
18
+ ".": {
19
+ "import": "./dist/index.js",
20
+ "types": "./dist/index.d.ts"
21
+ },
22
+ "./stream": {
23
+ "import": "./dist/stream.js",
24
+ "types": "./dist/stream.d.ts"
25
+ }
26
+ },
27
+ "devDependencies": {
28
+ "@types/node": "^20.3.2",
29
+ "eslint-config-prettier": "^8.8.0",
30
+ "nodemon": "^2.0.22",
31
+ "prettier": "^2.8.8",
32
+ "ts-node": "^10.9.1",
33
+ "typescript": "^5.1.5"
34
+ }
35
+ }