@andrew_l/tl-pack 0.0.3 → 0.0.5

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.
@@ -1,13 +1,16 @@
1
1
  /// <reference types="node" resolution-mode="require"/>
2
+ import { Dictionary } from './dictionary.js';
2
3
  import { TLExtension } from './extension.js';
3
4
  export interface BinaryReaderOptions {
5
+ dictionary?: string[] | Dictionary;
4
6
  extensions?: TLExtension[];
5
7
  }
6
8
  export declare class BinaryReader {
7
9
  private target;
8
10
  private targetView;
9
11
  private _last?;
10
- private _dict;
12
+ private dictionary?;
13
+ private dictionaryExtended;
11
14
  private extensions;
12
15
  offset: number;
13
16
  length: number;
@@ -42,6 +45,7 @@ export declare class BinaryReader {
42
45
  getBuffer(): Uint8Array | Buffer;
43
46
  readNull(): null;
44
47
  readLength(): number;
48
+ readAll(): any[];
45
49
  /**
46
50
  * @returns {Uint8Array | Buffer}
47
51
  */
@@ -66,7 +70,10 @@ export declare class BinaryReader {
66
70
  * Reads a object.
67
71
  */
68
72
  readObject(): any;
73
+ readObjectGzip(): any;
74
+ readGzip(): Uint8Array;
69
75
  private readCore;
76
+ getDictionaryValue(index: number): string | undefined;
70
77
  readDictionary(): string | null;
71
78
  readMap(checkConstructor?: boolean): Record<string, any>;
72
79
  decode(value: Buffer | Uint8Array): any;
@@ -1,10 +1,13 @@
1
+ import pako from 'pako';
1
2
  import { CORE_TYPES } from './constants.js';
3
+ import { Dictionary } from './dictionary.js';
2
4
  import { bytesToUtf8 } from './helpers.js';
3
5
  export class BinaryReader {
4
6
  target;
5
7
  targetView;
6
8
  _last;
7
- _dict;
9
+ dictionary;
10
+ dictionaryExtended;
8
11
  extensions;
9
12
  offset;
10
13
  length;
@@ -16,7 +19,6 @@ export class BinaryReader {
16
19
  this.target = data;
17
20
  this.targetView = new DataView(data.buffer, 0, data.length);
18
21
  this._last = undefined;
19
- this._dict = new Map();
20
22
  this.offset = 0;
21
23
  this.length = data.length;
22
24
  this.extensions = new Map();
@@ -25,6 +27,19 @@ export class BinaryReader {
25
27
  this.extensions.set(ext.token, ext);
26
28
  });
27
29
  }
30
+ if (!options) {
31
+ this.dictionary = new Dictionary();
32
+ }
33
+ else if (options.dictionary instanceof Dictionary) {
34
+ this.dictionary = options.dictionary;
35
+ }
36
+ else if (Array.isArray(options.dictionary)) {
37
+ this.dictionary = new Dictionary(options.dictionary);
38
+ }
39
+ else {
40
+ this.dictionary = new Dictionary();
41
+ }
42
+ this.dictionaryExtended = new Dictionary(undefined, this.dictionary.size);
28
43
  }
29
44
  readByte() {
30
45
  this.assertRead(1);
@@ -59,7 +74,7 @@ export class BinaryReader {
59
74
  this._last = this.targetView.getInt8(this.offset);
60
75
  }
61
76
  else {
62
- this._last = this.targetView.getInt8(this.offset);
77
+ this._last = this.targetView.getUint8(this.offset);
63
78
  }
64
79
  this.offset += 1;
65
80
  return this._last;
@@ -119,6 +134,13 @@ export class BinaryReader {
119
134
  }
120
135
  return firstByte;
121
136
  }
137
+ readAll() {
138
+ const result = [];
139
+ while (this.length > this.offset) {
140
+ result.push(this.readObject());
141
+ }
142
+ return result;
143
+ }
122
144
  /**
123
145
  * @returns {Uint8Array | Buffer}
124
146
  */
@@ -138,6 +160,7 @@ export class BinaryReader {
138
160
  this.assertRead(length);
139
161
  const bytes = this.target.subarray(this.offset, this.offset + length);
140
162
  this.offset += bytes.length;
163
+ // return pako.inflateRaw(bytes, { to: 'string' });
141
164
  return bytesToUtf8(bytes);
142
165
  }
143
166
  /**
@@ -169,7 +192,6 @@ export class BinaryReader {
169
192
  * Reads a object.
170
193
  */
171
194
  readObject() {
172
- const offset = this.offset;
173
195
  const constructorId = this.readByte();
174
196
  const ext = this.extensions.get(constructorId);
175
197
  let value;
@@ -182,10 +204,23 @@ export class BinaryReader {
182
204
  }
183
205
  return value;
184
206
  }
207
+ readObjectGzip() {
208
+ const bytes = this.readGzip();
209
+ const reader = new BinaryReader(bytes);
210
+ reader.extensions = this.extensions;
211
+ reader.dictionary = this.dictionary;
212
+ reader.dictionaryExtended = this.dictionaryExtended;
213
+ return reader.readObject();
214
+ }
215
+ readGzip() {
216
+ return pako.inflateRaw(this.readBytes());
217
+ }
185
218
  readCore(constructorId) {
186
219
  switch (constructorId) {
187
220
  case CORE_TYPES.None:
188
221
  return this.readObject();
222
+ case CORE_TYPES.GZIP:
223
+ return this.readObjectGzip();
189
224
  case CORE_TYPES.BoolTrue:
190
225
  return true;
191
226
  case CORE_TYPES.BoolFalse:
@@ -221,18 +256,28 @@ export class BinaryReader {
221
256
  }
222
257
  throw new Error(`Invalid constructor = ${CORE_TYPES[constructorId] || constructorId}, offset = ${this.offset - 1}`);
223
258
  }
259
+ getDictionaryValue(index) {
260
+ let value;
261
+ if (this.dictionary) {
262
+ value = this.dictionary.getValue(index);
263
+ }
264
+ if (value === undefined) {
265
+ value = this.dictionaryExtended.getValue(index);
266
+ }
267
+ return value;
268
+ }
224
269
  readDictionary() {
225
270
  const constructorId = this.readByte();
226
271
  let key = null;
227
272
  switch (constructorId) {
228
273
  case CORE_TYPES.DictIndex: {
229
274
  const idx = this.readLength();
230
- key = this._dict.get(idx);
275
+ key = this.getDictionaryValue(idx);
231
276
  break;
232
277
  }
233
278
  case CORE_TYPES.DictValue: {
234
279
  key = this.readString();
235
- this._dict.set(this._dict.size + 1, key);
280
+ this.dictionaryExtended.maybeInsert(key);
236
281
  break;
237
282
  }
238
283
  case CORE_TYPES.None: {
@@ -1,12 +1,17 @@
1
1
  /// <reference types="node" resolution-mode="require"/>
2
+ import { Dictionary } from './dictionary.js';
2
3
  import { TLExtension } from './extension.js';
3
4
  export interface BinaryWriterOptions {
5
+ gzip?: boolean;
6
+ dictionary?: string[] | Dictionary;
4
7
  extensions?: TLExtension[];
5
8
  }
6
9
  export declare class BinaryWriter {
10
+ private withGzip;
7
11
  private target;
8
12
  private targetView;
9
- private dict;
13
+ private dictionary?;
14
+ private dictionaryExtended;
10
15
  private extensions;
11
16
  offset: number;
12
17
  constructor(options?: BinaryWriterOptions);
@@ -29,8 +34,10 @@ export declare class BinaryWriter {
29
34
  writeVector(value: Array<any>): void;
30
35
  writeMap(object: Record<string, any>): void;
31
36
  wireDictionary(value: string): void;
37
+ writeGzip(value: any): void;
32
38
  encode(value: any): Uint8Array | Buffer;
33
39
  private _writeCustom;
34
40
  writeObject(value: any): void;
41
+ writeObjectGzip(value: any): void;
35
42
  private writeCore;
36
43
  }
@@ -1,4 +1,6 @@
1
+ import pako from 'pako';
1
2
  import { CORE_TYPES } from './constants.js';
3
+ import { Dictionary } from './dictionary.js';
2
4
  import { coreType } from './helpers.js';
3
5
  const hasNodeBuffer = typeof Buffer !== 'undefined';
4
6
  const MAX_BUFFER_SIZE = hasNodeBuffer ? 0x100000000 : 0x7fd00000;
@@ -17,23 +19,40 @@ function byteArrayAllocate(length) {
17
19
  }
18
20
  return new Uint8Array(length);
19
21
  }
22
+ const NO_CONSTRUCTOR = new Set([CORE_TYPES.BoolFalse, CORE_TYPES.BoolTrue, CORE_TYPES.Null]);
23
+ const SUPPORT_COMPRESSION = new Set([CORE_TYPES.String]);
20
24
  export class BinaryWriter {
25
+ withGzip;
21
26
  target;
22
27
  targetView;
23
- dict;
28
+ dictionary;
29
+ dictionaryExtended;
24
30
  extensions;
25
31
  offset;
26
32
  constructor(options) {
27
- this.dict = new Map();
28
33
  this.offset = 0;
29
34
  this.extensions = new Map();
35
+ this.withGzip = !!options && !!options.gzip;
36
+ this.target = byteArrayAllocate(8192);
37
+ this.targetView = new DataView(this.target.buffer, 0, this.target.length);
30
38
  if (options && options.extensions) {
31
39
  options.extensions.forEach((ext) => {
32
40
  this.extensions.set(ext.token, ext);
33
41
  });
34
42
  }
35
- this.target = byteArrayAllocate(8192);
36
- this.targetView = new DataView(this.target.buffer, 0, this.target.length);
43
+ if (!options) {
44
+ this.dictionary = new Dictionary();
45
+ }
46
+ else if (options.dictionary instanceof Dictionary) {
47
+ this.dictionary = options.dictionary;
48
+ }
49
+ else if (Array.isArray(options.dictionary)) {
50
+ this.dictionary = new Dictionary(options.dictionary);
51
+ }
52
+ else {
53
+ this.dictionary = new Dictionary();
54
+ }
55
+ this.dictionaryExtended = new Dictionary(undefined, this.dictionary.size);
37
56
  }
38
57
  allocate(size) {
39
58
  const position = this.offset + size;
@@ -111,7 +130,12 @@ export class BinaryWriter {
111
130
  }
112
131
  writeInt8(value, signed = true) {
113
132
  this.allocate(1);
114
- this.target[this.offset++] = value;
133
+ if (signed) {
134
+ this.target[this.offset++] = value;
135
+ }
136
+ else {
137
+ this.targetView.setUint8(this.offset++, value);
138
+ }
115
139
  }
116
140
  writeFloat(value) {
117
141
  this.allocate(4);
@@ -134,6 +158,8 @@ export class BinaryWriter {
134
158
  this.writeDouble(timestamp);
135
159
  }
136
160
  writeString(value) {
161
+ // const compressed = pako.deflateRaw(value, { level: 9 });
162
+ // this.writeBytes(compressed);
137
163
  let start = this.offset;
138
164
  let require = value.length << 2;
139
165
  if (require < 254) {
@@ -199,22 +225,31 @@ export class BinaryWriter {
199
225
  this.writeByte(CORE_TYPES.None);
200
226
  }
201
227
  wireDictionary(value) {
202
- if (this.dict.has(value)) {
203
- const idx = this.dict.get(value);
204
- this.writeCore(CORE_TYPES.DictIndex, idx);
228
+ let idx;
229
+ if (this.dictionary) {
230
+ idx = this.dictionary.getIndex(value);
205
231
  }
206
- else {
207
- const newIndex = this.dict.size + 1;
208
- this.dict.set(value, newIndex);
232
+ if (idx === undefined) {
233
+ idx = this.dictionaryExtended.getIndex(value);
234
+ }
235
+ if (idx === undefined) {
236
+ this.dictionaryExtended.maybeInsert(value);
209
237
  this.writeCore(CORE_TYPES.DictValue, value);
210
238
  }
239
+ else {
240
+ this.writeCore(CORE_TYPES.DictIndex, idx);
241
+ }
242
+ }
243
+ writeGzip(value) {
244
+ const compressed = pako.deflateRaw(value, { level: 9 });
245
+ this.writeBytes(compressed);
211
246
  }
212
247
  encode(value) {
213
- const start = this.offset;
248
+ this.offset = 0;
249
+ this.target = byteArrayAllocate(256);
250
+ this.targetView = new DataView(this.target.buffer, 0, this.target.length);
214
251
  this.writeObject(value);
215
- const end = this.offset;
216
- this.offset = start;
217
- return this.target.subarray(start, end);
252
+ return this.getBuffer();
218
253
  }
219
254
  _writeCustom(value) {
220
255
  for (const ext of this.extensions.values()) {
@@ -250,11 +285,27 @@ export class BinaryWriter {
250
285
  }
251
286
  this.writeCore(constructorId, value);
252
287
  }
288
+ writeObjectGzip(value) {
289
+ const writer = new BinaryWriter();
290
+ writer.extensions = this.extensions;
291
+ writer.dictionary = this.dictionary;
292
+ writer.dictionaryExtended = this.dictionaryExtended;
293
+ writer.writeObject(value);
294
+ this.writeCore(CORE_TYPES.GZIP, writer.getBuffer());
295
+ }
253
296
  writeCore(constructorId, value) {
254
- if (![CORE_TYPES.BoolFalse, CORE_TYPES.BoolTrue, CORE_TYPES.Null].includes(constructorId)) {
297
+ // console.log('write', { constructorId: CORE_TYPES[constructorId] || constructorId, value });
298
+ if (this.withGzip && SUPPORT_COMPRESSION.has(constructorId)) {
299
+ this.writeObjectGzip(value);
300
+ return;
301
+ }
302
+ else if (!NO_CONSTRUCTOR.has(constructorId)) {
255
303
  this.writeByte(constructorId);
256
304
  }
257
305
  switch (constructorId) {
306
+ case CORE_TYPES.GZIP: {
307
+ return this.writeGzip(value);
308
+ }
258
309
  case CORE_TYPES.DictIndex: {
259
310
  return this.writeLength(value);
260
311
  }
@@ -17,5 +17,6 @@ export declare enum CORE_TYPES {
17
17
  Map = 15,
18
18
  DictValue = 16,
19
19
  DictIndex = 17,
20
- String = 18
20
+ String = 18,
21
+ GZIP = 19
21
22
  }
package/dist/constants.js CHANGED
@@ -19,5 +19,5 @@ export var CORE_TYPES;
19
19
  CORE_TYPES[CORE_TYPES["DictValue"] = 16] = "DictValue";
20
20
  CORE_TYPES[CORE_TYPES["DictIndex"] = 17] = "DictIndex";
21
21
  CORE_TYPES[CORE_TYPES["String"] = 18] = "String";
22
+ CORE_TYPES[CORE_TYPES["GZIP"] = 19] = "GZIP";
22
23
  })(CORE_TYPES || (CORE_TYPES = {}));
23
- ;
@@ -0,0 +1,17 @@
1
+ export declare function createDictionary(values?: string[]): Dictionary;
2
+ export declare class Dictionary {
3
+ private _count;
4
+ private _map;
5
+ private _index;
6
+ private _offset;
7
+ constructor(values?: string[], offset?: number);
8
+ get size(): number;
9
+ /**
10
+ * Returns inserted index or nothing
11
+ */
12
+ maybeInsert(word: string): number | undefined;
13
+ getValue(index: number): string | undefined;
14
+ getIndex(value: string): number | undefined;
15
+ hasValue(value: string): boolean;
16
+ hasIndex(index: number): boolean;
17
+ }
@@ -0,0 +1,51 @@
1
+ export function createDictionary(values) {
2
+ return new Dictionary(values);
3
+ }
4
+ export class Dictionary {
5
+ _count = 0;
6
+ _map;
7
+ _index;
8
+ _offset;
9
+ constructor(values, offset = 0) {
10
+ this._index = [];
11
+ this._map = new Map();
12
+ this._offset = offset;
13
+ if (Array.isArray(values) && values.length) {
14
+ values.forEach((word) => {
15
+ if (this._map.has(word))
16
+ return;
17
+ this._map.set(word, this._count++);
18
+ this._index.push(word);
19
+ });
20
+ }
21
+ }
22
+ get size() {
23
+ return this._count;
24
+ }
25
+ /**
26
+ * Returns inserted index or nothing
27
+ */
28
+ maybeInsert(word) {
29
+ if (this._map.has(word))
30
+ return;
31
+ this._map.set(word, this._count++);
32
+ this._index.push(word);
33
+ return this._count + this._offset;
34
+ }
35
+ getValue(index) {
36
+ return this._index[index - this._offset];
37
+ }
38
+ getIndex(value) {
39
+ const idx = this._map.get(value);
40
+ if (idx === undefined) {
41
+ return idx;
42
+ }
43
+ return idx + this._offset;
44
+ }
45
+ hasValue(value) {
46
+ return this._map.has(value);
47
+ }
48
+ hasIndex(index) {
49
+ return this._index[index - this._offset] !== undefined;
50
+ }
51
+ }
package/dist/index.d.ts CHANGED
@@ -2,3 +2,4 @@ export * from './BinaryWriter.js';
2
2
  export * from './BinaryReader.js';
3
3
  export * from './constants.js';
4
4
  export * from './extension.js';
5
+ export * from './dictionary.js';
package/dist/index.js CHANGED
@@ -2,3 +2,4 @@ export * from './BinaryWriter.js';
2
2
  export * from './BinaryReader.js';
3
3
  export * from './constants.js';
4
4
  export * from './extension.js';
5
+ export * from './dictionary.js';
package/dist/stream.d.ts CHANGED
@@ -1,13 +1,20 @@
1
1
  /// <reference types="node" resolution-mode="require"/>
2
2
  /// <reference types="node" resolution-mode="require"/>
3
3
  import { Transform, type TransformCallback, type TransformOptions } from 'stream';
4
+ import { BinaryWriter, BinaryWriterOptions } from './BinaryWriter.js';
5
+ import { BinaryReader } from './BinaryReader.js';
6
+ export interface TLEncodeOptions extends BinaryWriterOptions {
7
+ streamOptions?: TransformOptions;
8
+ writeVectorWhenEmpty?: boolean;
9
+ }
4
10
  export declare class TLEncode extends Transform {
5
- private writer;
6
- constructor(options?: TransformOptions);
11
+ writer: BinaryWriter;
12
+ count: number;
13
+ constructor(options?: TLEncodeOptions);
7
14
  _transform(chunk: any, encoding: BufferEncoding, callback: TransformCallback): void;
8
15
  }
9
16
  export declare class TLDecode extends Transform {
10
- private reader;
17
+ reader: BinaryReader;
11
18
  private incompleteBuffer;
12
19
  constructor(options?: TransformOptions);
13
20
  _transform(chunk: any, encoding: BufferEncoding, callback: TransformCallback): void;
package/dist/stream.js CHANGED
@@ -3,16 +3,31 @@ import { BinaryWriter } from './BinaryWriter.js';
3
3
  import { BinaryReader } from './BinaryReader.js';
4
4
  export class TLEncode extends Transform {
5
5
  writer;
6
+ count;
6
7
  constructor(options) {
7
- if (!options)
8
- options = {};
9
- options.writableObjectMode = true;
10
- super(options);
11
- this.writer = new BinaryWriter();
8
+ const opts = options || {};
9
+ opts.streamOptions = { writableObjectMode: true, ...(opts.streamOptions || {}) };
10
+ super(opts.streamOptions);
11
+ const writer = new BinaryWriter(options);
12
+ const customFlush = opts.streamOptions.flush;
13
+ this._flush = (callback) => {
14
+ if (this.count === 0 && opts.writeVectorWhenEmpty) {
15
+ this.push(writer.encode([]));
16
+ }
17
+ if (customFlush) {
18
+ customFlush.call(this, callback);
19
+ }
20
+ else {
21
+ callback();
22
+ }
23
+ };
24
+ this.writer = writer;
25
+ this.count = 0;
12
26
  }
13
27
  _transform(chunk, encoding, callback) {
14
28
  const buff = this.writer.encode(chunk);
15
29
  this.push(buff);
30
+ this.count++;
16
31
  callback();
17
32
  }
18
33
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@andrew_l/tl-pack",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -26,10 +26,14 @@
26
26
  },
27
27
  "devDependencies": {
28
28
  "@types/node": "^20.3.2",
29
+ "@types/pako": "^2.0.0",
29
30
  "eslint-config-prettier": "^8.8.0",
30
31
  "nodemon": "^2.0.22",
31
32
  "prettier": "^2.8.8",
32
33
  "ts-node": "^10.9.1",
33
34
  "typescript": "^5.1.5"
35
+ },
36
+ "dependencies": {
37
+ "pako": "^2.1.0"
34
38
  }
35
39
  }