pw-buffer 2.1.0 → 3.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/README.md CHANGED
@@ -1,18 +1,263 @@
1
- # node-pw-buffer
2
- PW Buffer
1
+ # Perfect World binary utils
3
2
 
4
- ```typescript
5
- import { ExtendedBuffer, ExtendedBufferOptions } from 'extended-buffer';
6
- export interface PwBufferOptions extends ExtendedBufferOptions {
7
- }
8
- export declare class PwBuffer extends ExtendedBuffer {
9
- isReadableCUInt(): boolean;
10
- readCUInt(noAssert?: boolean): number;
11
- _writeCUIntToBuffer(buffer: this, value: number, noAssert?: boolean): this;
12
- writeCUInt(value: number, unshift?: boolean, noAssert?: boolean): this;
13
- readPwString(noAssert?: boolean): string;
14
- writePwString(string: string, unshift?: boolean, noAssert?: boolean): this;
15
- readPwOctets(noAssert?: boolean): this;
16
- writePwOctets(octets: this | Buffer, unshift?: boolean, noAssert?: boolean): this;
3
+ A small TypeScript/Node.js library built on top of [extended-buffer](https://github.com/mvcbox/node-extended-buffer) that adds helpers commonly found in some Perfect World / PW-style binary protocols:
4
+
5
+ - **CUInt** (compressed unsigned integer) encoding/decoding
6
+ - **PW strings**: `CUInt` byte-length prefix + `utf16le` payload
7
+ - **PW octets**: `CUInt` byte-length prefix + raw bytes
8
+ - **MPPC** compressor/decompressor (stream-friendly) plus optional Node.js `Transform` wrappers
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ npm i pw-buffer
14
+ ```
15
+
16
+ ## Quick start
17
+
18
+ ```ts
19
+ import { PwBuffer } from 'pw-buffer';
20
+
21
+ const b = new PwBuffer();
22
+
23
+ b.writeCUInt(123);
24
+ b.writePwString('Hello');
25
+ b.writePwOctets(Buffer.from([1, 2, 3]));
26
+
27
+ // Read back
28
+ console.log(b.readCUInt()); // 123
29
+ console.log(b.readPwString()); // "Hello"
30
+ console.log(b.readPwOctets().nativeBufferView); // <Buffer 01 02 03>
31
+ ```
32
+
33
+ ## What is `PwBuffer`?
34
+
35
+ `PwBuffer` extends `ExtendedBuffer` from the `extended-buffer` package. That means you get all of `ExtendedBuffer` features (growable buffer, internal read pointer, append/prepend writes, etc.), plus PW-specific helpers.
36
+
37
+ ### Append vs prepend (`unshift`)
38
+
39
+ Most write methods support an optional `unshift?: boolean` flag:
40
+
41
+ - `unshift = false` (default): append to the end
42
+ - `unshift = true`: prepend to the start
43
+
44
+ This is useful when you want to write a payload first and then prepend headers (length, opcode, etc.).
45
+
46
+ Example: build a length-prefixed packet
47
+
48
+ ```ts
49
+ import { PwBuffer } from 'pw-buffer';
50
+
51
+ const pkt = new PwBuffer();
52
+
53
+ pkt.writePwString('Alice'); // body
54
+ pkt.writePwString('Bob'); //
55
+
56
+ // Prepend total byte length (example protocol convention)
57
+ pkt.writeCUInt(pkt.length, true); // length
58
+ pkt.writeCUInt(0x1234, true); // opcode
59
+
60
+ console.log(pkt.nativeBufferView);
61
+ ```
62
+
63
+ ## API
64
+
65
+ The package exports:
66
+
67
+ ```ts
68
+ export * from './utils';
69
+ export { PwBuffer } from './PwBuffer';
70
+ export type { PwBufferOptions } from './PwBufferOptions';
71
+ export { MppcCompressor, MppcDecompressor } from './mppc';
72
+ export { MppcCompressorTransform } from './MppcCompressorTransform';
73
+ export { MppcDecompressorTransform } from './MppcDecompressorTransform';
74
+ ```
75
+
76
+ ### `PwBuffer(options?: PwBufferOptions)`
77
+
78
+ `PwBufferOptions` is currently the same as `ExtendedBufferOptions`:
79
+
80
+ - `capacity?: number` – initial capacity (bytes)
81
+ - `capacityStep?: number` – resize step (bytes)
82
+
83
+ Example:
84
+
85
+ ```ts
86
+ import { PwBuffer } from 'pw-buffer';
87
+
88
+ const b = new PwBuffer({ capacity: 64 * 1024, capacityStep: 16 * 1024 });
89
+ ```
90
+
91
+ ### `isReadableCUInt(): boolean`
92
+
93
+ Checks whether a full `CUInt` value can be read at the current read pointer without running out of data.
94
+
95
+ This is handy when you parse a stream and may receive partial frames.
96
+
97
+ ```ts
98
+ if (b.isReadableCUInt()) {
99
+ const len = b.readCUInt();
100
+ // ...
17
101
  }
18
102
  ```
103
+
104
+ ### `readCUInt(): number`
105
+
106
+ Reads a **compressed unsigned integer** at the current read pointer.
107
+
108
+ - Returns a JavaScript `number` in the range **0…0xFFFFFFFF**.
109
+ - Advances the read pointer.
110
+
111
+ If there are not enough readable bytes, underlying `extended-buffer` reads may throw.
112
+
113
+ ### `writeCUInt(value: number, unshift?: boolean): this`
114
+
115
+ Writes a **compressed unsigned integer**.
116
+
117
+ - `value` must be an **integer** in **0…0xFFFFFFFF**.
118
+ - If the value is outside the allowed range, it throws an `ExtendedBufferRangeError('CUINT_VALUE_OUT_OF_RANGE')`.
119
+
120
+ ```ts
121
+ b.writeCUInt(1);
122
+ b.writeCUInt(127);
123
+ b.writeCUInt(128);
124
+ b.writeCUInt(0xFFFFFFFF);
125
+ ```
126
+
127
+ ### CUInt encoding format
128
+
129
+ CUInt uses a leading-bit pattern to choose the byte width:
130
+
131
+ | Range | Encoded bytes | Notes |
132
+ |------:|:-------------:|---------------------------------|
133
+ | `0x00000000 … 0x0000007F` | 1 | `0xxxxxxx` |
134
+ | `0x00000080 … 0x00003FFF` | 2 | stored as `value \| 0x8000` |
135
+ | `0x00004000 … 0x1FFFFFFF` | 4 | stored as `value \| 0xC0000000` |
136
+ | `0x20000000 … 0xFFFFFFFF` | 5 | marker `0xE0` + 4-byte BE value |
137
+
138
+ > Implementation note: in the 5-byte case, the first byte is the marker `0xE0`, followed by the 32-bit big-endian value.
139
+
140
+ ### `readPwString(): string`
141
+
142
+ Reads a PW string:
143
+
144
+ 1. Reads a `CUInt` **byte length** `N`
145
+ 2. Reads `N` bytes and decodes them as `utf16le`
146
+
147
+ ```ts
148
+ const s = b.readPwString();
149
+ ```
150
+
151
+ ### `writePwString(value: string, unshift?: boolean): this`
152
+
153
+ Writes a PW string:
154
+
155
+ 1. Encodes the string to bytes using `utf16le`
156
+ 2. Writes `CUInt(byteLength)`
157
+ 3. Writes the bytes
158
+
159
+ ```ts
160
+ b.writePwString('Hello');
161
+ ```
162
+
163
+ ### `readPwOctets(): PwBuffer`
164
+
165
+ Reads PW octets:
166
+
167
+ 1. Reads a `CUInt` **byte length** `N`
168
+ 2. Reads `N` raw bytes
169
+ 3. Returns a **new** `PwBuffer` containing only those bytes
170
+
171
+ ```ts
172
+ const oct = b.readPwOctets();
173
+ console.log(oct.length);
174
+ console.log(oct.nativeBufferView);
175
+ ```
176
+
177
+ ### `writePwOctets(octets: ExtendedBuffer | Buffer, unshift?: boolean): this`
178
+
179
+ Writes PW octets:
180
+
181
+ 1. Writes `CUInt(octets.length)`
182
+ 2. Writes raw `octets` bytes
183
+
184
+ ```ts
185
+ b.writePwOctets(Buffer.from([0xde, 0xad, 0xbe, 0xef]));
186
+ ```
187
+
188
+ ## MPPC compression
189
+
190
+ The library includes a streaming MPPC compressor and decompressor:
191
+
192
+ - `MppcCompressor.update(chunk: Buffer): Buffer`
193
+ - `MppcDecompressor.update(chunk: Buffer): Buffer`
194
+
195
+ Both are **stateful**. Create a new instance per independent stream/connection.
196
+
197
+ ### Basic usage
198
+
199
+ ```ts
200
+ import { MppcCompressor, MppcDecompressor } from 'pw-buffer';
201
+
202
+ const input = Buffer.from('hello hello hello', 'utf8');
203
+
204
+ const c = new MppcCompressor();
205
+ const compressed = c.update(input);
206
+
207
+ const d = new MppcDecompressor();
208
+ const decompressed = d.update(compressed);
209
+
210
+ console.log(decompressed.toString('utf8')); // "hello hello hello"
211
+ ```
212
+
213
+ ### Streaming usage (chunked)
214
+
215
+ ```ts
216
+ import { MppcCompressor, MppcDecompressor } from 'pw-buffer';
217
+
218
+ const c = new MppcCompressor();
219
+ const d = new MppcDecompressor();
220
+
221
+ const part1 = Buffer.from('hello ', 'utf8');
222
+ const part2 = Buffer.from('world', 'utf8');
223
+
224
+ const c1 = c.update(part1);
225
+ const c2 = c.update(part2);
226
+
227
+ const out1 = d.update(c1);
228
+ const out2 = d.update(c2);
229
+
230
+ console.log(Buffer.concat([out1, out2]).toString('utf8')); // "hello world"
231
+ ```
232
+
233
+ ## MPPC `Transform` streams
234
+
235
+ If you prefer Node.js stream piping, you can use the provided transforms:
236
+
237
+ - `MppcCompressorTransform`
238
+ - `MppcDecompressorTransform`
239
+
240
+ ```ts
241
+ import fs from 'node:fs';
242
+ import { MppcCompressorTransform, MppcDecompressorTransform } from 'pw-buffer';
243
+
244
+ // Compress a file
245
+ fs.createReadStream('input.bin')
246
+ .pipe(new MppcCompressorTransform())
247
+ .pipe(fs.createWriteStream('input.bin.mppc'));
248
+
249
+ // Decompress a file
250
+ fs.createReadStream('input.bin.mppc')
251
+ .pipe(new MppcDecompressorTransform())
252
+ .pipe(fs.createWriteStream('input.bin'));
253
+ ```
254
+
255
+ ## Error handling tips
256
+
257
+ - Before reading from a buffer fed by a socket/stream, prefer guard checks such as `isReadable(size)` (from `ExtendedBuffer`) and `isReadableCUInt()`.
258
+ - `writeCUInt()` validates the value is an integer and within `0…0xFFFFFFFF`.
259
+ - If you need the full set of buffer operations (pointer control, reading/writing primitives, etc.), refer to the upstream [extended-buffer](https://github.com/mvcbox/node-extended-buffer) documentation.
260
+
261
+ ## License
262
+
263
+ MIT (see the package metadata).
@@ -0,0 +1,8 @@
1
+ /// <reference types="node" />
2
+ import { MppcCompressor } from './mppc';
3
+ import { Transform, TransformOptions } from 'stream';
4
+ export declare class MppcCompressorTransform extends Transform {
5
+ protected readonly _mppcCompressor: MppcCompressor;
6
+ constructor(transformOptions?: TransformOptions);
7
+ _transform(chunk: Buffer | null, encoding: string, callback: Function): void;
8
+ }
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MppcCompressorTransform = void 0;
4
+ const mppc_1 = require("./mppc");
5
+ const stream_1 = require("stream");
6
+ class MppcCompressorTransform extends stream_1.Transform {
7
+ constructor(transformOptions) {
8
+ super(transformOptions);
9
+ this._mppcCompressor = new mppc_1.MppcCompressor();
10
+ }
11
+ _transform(chunk, encoding, callback) {
12
+ callback(null, chunk ? this._mppcCompressor.update(chunk) : null);
13
+ }
14
+ }
15
+ exports.MppcCompressorTransform = MppcCompressorTransform;
@@ -0,0 +1,8 @@
1
+ /// <reference types="node" />
2
+ import { MppcDecompressor } from './mppc';
3
+ import { Transform, TransformOptions } from 'stream';
4
+ export declare class MppcDecompressorTransform extends Transform {
5
+ protected readonly _mppcDecompressor: MppcDecompressor;
6
+ constructor(transformOptions?: TransformOptions);
7
+ _transform(chunk: Buffer | null, encoding: string, callback: Function): void;
8
+ }
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MppcDecompressorTransform = void 0;
4
+ const mppc_1 = require("./mppc");
5
+ const stream_1 = require("stream");
6
+ class MppcDecompressorTransform extends stream_1.Transform {
7
+ constructor(transformOptions) {
8
+ super(transformOptions);
9
+ this._mppcDecompressor = new mppc_1.MppcDecompressor();
10
+ }
11
+ _transform(chunk, encoding, callback) {
12
+ callback(null, chunk ? this._mppcDecompressor.update(chunk) : null);
13
+ }
14
+ }
15
+ exports.MppcDecompressorTransform = MppcDecompressorTransform;
@@ -1,14 +1,13 @@
1
1
  /// <reference types="node" />
2
- import { ExtendedBuffer, ExtendedBufferOptions } from 'extended-buffer';
3
- export interface PwBufferOptions extends ExtendedBufferOptions {
4
- }
2
+ import { ExtendedBuffer } from 'extended-buffer';
3
+ import type { PwBufferOptions } from './PwBufferOptions';
5
4
  export declare class PwBuffer extends ExtendedBuffer {
5
+ protected createInstance(options?: PwBufferOptions): this;
6
6
  isReadableCUInt(): boolean;
7
- readCUInt(noAssert?: boolean): number;
8
- _writeCUIntToBuffer(buffer: ExtendedBuffer, value: number, noAssert?: boolean): this;
9
- writeCUInt(value: number, unshift?: boolean, noAssert?: boolean): this;
10
- readPwString(noAssert?: boolean): string;
11
- writePwString(string: string, unshift?: boolean, noAssert?: boolean): this;
12
- readPwOctets(noAssert?: boolean): this;
13
- writePwOctets(octets: ExtendedBuffer | Buffer, unshift?: boolean, noAssert?: boolean): this;
7
+ readCUInt(): number;
8
+ writeCUInt(value: number, unshift?: boolean): this;
9
+ readPwString(): string;
10
+ writePwString(string: string, unshift?: boolean): this;
11
+ readPwOctets(): this;
12
+ writePwOctets(octets: ExtendedBuffer | Buffer, unshift?: boolean): this;
14
13
  }
package/dist/PwBuffer.js CHANGED
@@ -1,14 +1,20 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const extended_buffer_1 = require("extended-buffer");
3
+ exports.PwBuffer = void 0;
4
4
  const buffer_1 = require("buffer");
5
+ const utils = require("./utils");
6
+ const extended_buffer_1 = require("extended-buffer");
5
7
  class PwBuffer extends extended_buffer_1.ExtendedBuffer {
8
+ createInstance(options) {
9
+ const ThisClass = this.constructor;
10
+ return new ThisClass(options);
11
+ }
6
12
  isReadableCUInt() {
7
13
  if (!this.isReadable(1)) {
8
14
  return false;
9
15
  }
10
16
  let value = this.readUIntBE(1);
11
- --this._pointer;
17
+ this._pointer--;
12
18
  switch (value & 0xE0) {
13
19
  case 0xE0:
14
20
  return this.isReadable(5);
@@ -20,78 +26,55 @@ class PwBuffer extends extended_buffer_1.ExtendedBuffer {
20
26
  }
21
27
  return true;
22
28
  }
23
- readCUInt(noAssert) {
24
- let value = this.readUIntBE(1, noAssert);
29
+ readCUInt() {
30
+ let value = this.readUIntBE(1);
25
31
  switch (value & 0xE0) {
26
- case 0xE0:
27
- return this.readUIntBE(4, noAssert);
28
- case 0xC0:
29
- --this._pointer;
30
- return this.readUIntBE(4, noAssert) & 0x1FFFFFFF;
31
- case 0x80:
32
- case 0xA0:
33
- --this._pointer;
34
- return this.readUIntBE(2, noAssert) & 0x3FFF;
35
- }
36
- return value;
37
- }
38
- _writeCUIntToBuffer(buffer, value, noAssert) {
39
- let tmp;
40
- if (value < 0x80) {
41
- buffer.writeUIntBE(value, 1, false, noAssert);
42
- }
43
- else if (value < 0x4000) {
44
- if ((tmp = value | 0x8000) < 0) {
45
- buffer.writeIntBE(tmp, 2, false, noAssert);
46
- }
47
- else {
48
- buffer.writeUIntBE(tmp, 2, false, noAssert);
32
+ case 0xE0: {
33
+ try {
34
+ return this.readUIntBE(4);
35
+ }
36
+ catch (e) {
37
+ this._pointer--;
38
+ }
49
39
  }
50
- }
51
- else if (value < 0x20000000) {
52
- if ((tmp = value | 0xC0000000) < 0) {
53
- buffer.writeIntBE(tmp, 4, false, noAssert);
40
+ case 0xC0: {
41
+ this._pointer--;
42
+ return this.readUIntBE(4) & 0x1FFFFFFF;
54
43
  }
55
- else {
56
- buffer.writeUIntBE(tmp, 4, false, noAssert);
44
+ case 0x80:
45
+ case 0xA0: {
46
+ this._pointer--;
47
+ return this.readUIntBE(2) & 0x3FFF;
57
48
  }
58
49
  }
59
- else {
60
- buffer.writeUIntBE(0xE0, 1, false, noAssert).writeUIntBE(value, 4, false, noAssert);
61
- }
62
- return this;
50
+ return value;
63
51
  }
64
- writeCUInt(value, unshift, noAssert) {
65
- if (unshift) {
66
- const ThisClass = this.constructor;
67
- let buffer = new ThisClass({
68
- maxBufferLength: 5
69
- });
70
- buffer.allocEnd(buffer.getFreeSpace());
71
- return this._writeCUIntToBuffer(buffer, value, noAssert)._writeNativeBuffer(buffer.buffer, true);
72
- }
73
- return this._writeCUIntToBuffer(this, value, noAssert);
52
+ writeCUInt(value, unshift) {
53
+ utils.writeCUIntToPwBuffer(this, value, unshift);
54
+ return this;
74
55
  }
75
- readPwString(noAssert) {
76
- return this.readString(this.readCUInt(noAssert), 'utf16le');
56
+ readPwString() {
57
+ return this.readString(this.readCUInt(), 'utf16le');
77
58
  }
78
- writePwString(string, unshift, noAssert) {
59
+ writePwString(string, unshift) {
60
+ const bytes = buffer_1.Buffer.from(string, 'utf16le');
79
61
  if (unshift) {
80
- return this.writeString(string, 'utf16le', true).writeCUInt(buffer_1.Buffer.byteLength(string, 'utf16le'), true, noAssert);
62
+ return this.writeNativeBuffer(bytes, true).writeCUInt(bytes.length, true);
81
63
  }
82
- return this.writeCUInt(buffer_1.Buffer.byteLength(string, 'utf16le'), false, noAssert).writeString(string, 'utf16le', false);
64
+ return this.writeCUInt(bytes.length).writeNativeBuffer(bytes);
83
65
  }
84
- readPwOctets(noAssert) {
85
- let byteLength = this.readCUInt(noAssert);
66
+ readPwOctets() {
67
+ let byteLength = this.readCUInt();
86
68
  return this.readBuffer(byteLength, false, {
87
- maxBufferLength: byteLength
69
+ capacity: byteLength,
70
+ capacityStep: 0
88
71
  });
89
72
  }
90
- writePwOctets(octets, unshift, noAssert) {
73
+ writePwOctets(octets, unshift) {
91
74
  if (unshift) {
92
- return this.writeBuffer(octets, true).writeCUInt(octets.length, true, noAssert);
75
+ return this.writeBuffer(octets, true).writeCUInt(octets.length, true);
93
76
  }
94
- return this.writeCUInt(octets.length, false, noAssert).writeBuffer(octets, false);
77
+ return this.writeCUInt(octets.length).writeBuffer(octets);
95
78
  }
96
79
  }
97
80
  exports.PwBuffer = PwBuffer;
@@ -0,0 +1,2 @@
1
+ import { ExtendedBufferOptions } from 'extended-buffer';
2
+ export type PwBufferOptions = ExtendedBufferOptions;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/dist/index.d.ts CHANGED
@@ -1 +1,6 @@
1
- export * from './PwBuffer';
1
+ export * from './utils';
2
+ export { PwBuffer } from './PwBuffer';
3
+ export type { PwBufferOptions } from './PwBufferOptions';
4
+ export { MppcCompressor, MppcDecompressor } from './mppc';
5
+ export { MppcCompressorTransform } from './MppcCompressorTransform';
6
+ export { MppcDecompressorTransform } from './MppcDecompressorTransform';
package/dist/index.js CHANGED
@@ -1,6 +1,27 @@
1
1
  "use strict";
2
- function __export(m) {
3
- for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
4
- }
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
5
16
  Object.defineProperty(exports, "__esModule", { value: true });
6
- __export(require("./PwBuffer"));
17
+ exports.MppcDecompressorTransform = exports.MppcCompressorTransform = exports.MppcDecompressor = exports.MppcCompressor = exports.PwBuffer = void 0;
18
+ __exportStar(require("./utils"), exports);
19
+ var PwBuffer_1 = require("./PwBuffer");
20
+ Object.defineProperty(exports, "PwBuffer", { enumerable: true, get: function () { return PwBuffer_1.PwBuffer; } });
21
+ var mppc_1 = require("./mppc");
22
+ Object.defineProperty(exports, "MppcCompressor", { enumerable: true, get: function () { return mppc_1.MppcCompressor; } });
23
+ Object.defineProperty(exports, "MppcDecompressor", { enumerable: true, get: function () { return mppc_1.MppcDecompressor; } });
24
+ var MppcCompressorTransform_1 = require("./MppcCompressorTransform");
25
+ Object.defineProperty(exports, "MppcCompressorTransform", { enumerable: true, get: function () { return MppcCompressorTransform_1.MppcCompressorTransform; } });
26
+ var MppcDecompressorTransform_1 = require("./MppcDecompressorTransform");
27
+ Object.defineProperty(exports, "MppcDecompressorTransform", { enumerable: true, get: function () { return MppcDecompressorTransform_1.MppcDecompressorTransform; } });
package/dist/mppc.d.ts ADDED
@@ -0,0 +1,23 @@
1
+ /// <reference types="node" />
2
+ export declare class MppcDecompressor {
3
+ private history;
4
+ private histPtr;
5
+ private bitOffset;
6
+ private legacy;
7
+ update(chunk: Buffer): Buffer;
8
+ }
9
+ export declare class MppcCompressor {
10
+ private history;
11
+ private histPtr;
12
+ private readonly bw;
13
+ private readonly dict;
14
+ private key3;
15
+ private addPosToDict;
16
+ private pushHistoryByte;
17
+ private writeLiteral;
18
+ private writeOffset;
19
+ private writeLength;
20
+ private writeFlushMarker;
21
+ private resetSegment;
22
+ update(chunk: Buffer): Buffer;
23
+ }
package/dist/mppc.js ADDED
@@ -0,0 +1,445 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MppcCompressor = exports.MppcDecompressor = void 0;
4
+ const buffer_1 = require("buffer");
5
+ const extended_buffer_1 = require("extended-buffer");
6
+ const MPPC_HIST_LEN = 8192;
7
+ function readU32BEWithPadding(buffer, offset) {
8
+ const b0 = offset < buffer.length ? buffer[offset] : 0;
9
+ const b1 = offset + 1 < buffer.length ? buffer[offset + 1] : 0;
10
+ const b2 = offset + 2 < buffer.length ? buffer[offset + 2] : 0;
11
+ const b3 = offset + 3 < buffer.length ? buffer[offset + 3] : 0;
12
+ return (((b0 << 24) | (b1 << 16) | (b2 << 8) | b3) >>> 0);
13
+ }
14
+ class BitWriter {
15
+ constructor() {
16
+ this.out = [];
17
+ this.bitBuf = 0;
18
+ this.bitCount = 0;
19
+ }
20
+ writeBits(value, bits) {
21
+ if (bits <= 0) {
22
+ return;
23
+ }
24
+ if (bits > 31) {
25
+ throw new Error(`BitWriter.writeBits: bits=${bits} too large`);
26
+ }
27
+ const mask = bits === 31 ? 0x7FFFFFFF : (1 << bits) - 1;
28
+ const v = (value & mask) >>> 0;
29
+ this.bitBuf = (this.bitBuf * (Math.pow(2, bits))) + v;
30
+ this.bitCount += bits;
31
+ while (this.bitCount >= 8) {
32
+ const shift = this.bitCount - 8;
33
+ const byte = Math.floor(this.bitBuf / (Math.pow(2, shift))) & 0xFF;
34
+ this.out.push(byte);
35
+ this.bitCount -= 8;
36
+ if (this.bitCount === 0) {
37
+ this.bitBuf = 0;
38
+ }
39
+ else {
40
+ this.bitBuf = this.bitBuf % (Math.pow(2, this.bitCount));
41
+ }
42
+ }
43
+ }
44
+ alignToByteWithZeroPadding() {
45
+ const mod = this.bitCount & 7;
46
+ if (mod) {
47
+ this.writeBits(0, 8 - mod);
48
+ }
49
+ }
50
+ flushBytes() {
51
+ if (!this.out.length) {
52
+ return buffer_1.Buffer.alloc(0);
53
+ }
54
+ const buffer = buffer_1.Buffer.from(this.out);
55
+ this.out = [];
56
+ return buffer;
57
+ }
58
+ getPendingBitCount() {
59
+ return this.bitCount;
60
+ }
61
+ }
62
+ class MppcDecompressor {
63
+ constructor() {
64
+ this.history = new Uint8Array(MPPC_HIST_LEN);
65
+ this.histPtr = 0;
66
+ this.bitOffset = 0;
67
+ this.legacy = buffer_1.Buffer.alloc(0);
68
+ }
69
+ update(chunk) {
70
+ if (chunk.length) {
71
+ this.legacy = buffer_1.Buffer.concat([this.legacy, chunk]);
72
+ }
73
+ let rptr = 0;
74
+ let bitShift = this.bitOffset;
75
+ let consumedBits = 7;
76
+ const totalBits = this.legacy.length * 8 - bitShift;
77
+ let savedBitShift = 0;
78
+ let savedRptr = 0;
79
+ const outParts = [];
80
+ let histHead = this.histPtr;
81
+ const passBits = (n) => {
82
+ bitShift += n;
83
+ consumedBits += n;
84
+ if (consumedBits < totalBits) {
85
+ return true;
86
+ }
87
+ bitShift = savedBitShift;
88
+ rptr = savedRptr;
89
+ return false;
90
+ };
91
+ const fetch = () => {
92
+ rptr += bitShift >>> 3;
93
+ bitShift &= 7;
94
+ return ((readU32BEWithPadding(this.legacy, rptr) << bitShift) >>> 0);
95
+ };
96
+ while (totalBits > consumedBits) {
97
+ savedBitShift = bitShift;
98
+ savedRptr = rptr;
99
+ let val = fetch();
100
+ if (val < 0x80000000) {
101
+ if (!passBits(8)) {
102
+ break;
103
+ }
104
+ this.history[this.histPtr++] = (val >>> 24) & 0xFF;
105
+ continue;
106
+ }
107
+ if (val < 0xC0000000) {
108
+ if (!passBits(9)) {
109
+ break;
110
+ }
111
+ this.history[this.histPtr++] = (((val >>> 23) | 0x80) & 0xFF) >>> 0;
112
+ continue;
113
+ }
114
+ let off = 0;
115
+ if (val >= 0xF0000000) {
116
+ if (!passBits(10)) {
117
+ break;
118
+ }
119
+ off = (val >>> 22) & 0x3F;
120
+ if (off === 0) {
121
+ const advance = 8 - (bitShift & 7);
122
+ if (advance < 8) {
123
+ if (!passBits(advance)) {
124
+ break;
125
+ }
126
+ }
127
+ if (this.histPtr > histHead) {
128
+ outParts.push(buffer_1.Buffer.from(this.history.subarray(histHead, this.histPtr)));
129
+ }
130
+ if (this.histPtr === MPPC_HIST_LEN) {
131
+ this.histPtr = 0;
132
+ }
133
+ histHead = this.histPtr;
134
+ continue;
135
+ }
136
+ }
137
+ else if (val >= 0xE0000000) {
138
+ if (!passBits(12)) {
139
+ break;
140
+ }
141
+ off = ((val >>> 20) & 0xFF) + 64;
142
+ }
143
+ else if (val >= 0xC0000000) {
144
+ if (!passBits(16)) {
145
+ break;
146
+ }
147
+ off = ((val >>> 16) & 0x1FFF) + 320;
148
+ }
149
+ val = fetch();
150
+ let len = 0;
151
+ if (val < 0x80000000) {
152
+ if (!passBits(1)) {
153
+ break;
154
+ }
155
+ len = 3;
156
+ }
157
+ else if (val < 0xC0000000) {
158
+ if (!passBits(4)) {
159
+ break;
160
+ }
161
+ len = 4 | ((val >>> 28) & 3);
162
+ }
163
+ else if (val < 0xE0000000) {
164
+ if (!passBits(6)) {
165
+ break;
166
+ }
167
+ len = 8 | ((val >>> 26) & 7);
168
+ }
169
+ else if (val < 0xF0000000) {
170
+ if (!passBits(8)) {
171
+ break;
172
+ }
173
+ len = 16 | ((val >>> 24) & 15);
174
+ }
175
+ else if (val < 0xF8000000) {
176
+ if (!passBits(10)) {
177
+ break;
178
+ }
179
+ len = 32 | ((val >>> 22) & 0x1F);
180
+ }
181
+ else if (val < 0xFC000000) {
182
+ if (!passBits(12)) {
183
+ break;
184
+ }
185
+ len = 64 | ((val >>> 20) & 0x3F);
186
+ }
187
+ else if (val < 0xFE000000) {
188
+ if (!passBits(14)) {
189
+ break;
190
+ }
191
+ len = 128 | ((val >>> 18) & 0x7F);
192
+ }
193
+ else if (val < 0xFF000000) {
194
+ if (!passBits(16)) {
195
+ break;
196
+ }
197
+ len = 256 | ((val >>> 16) & 0xFF);
198
+ }
199
+ else if (val < 0xFF800000) {
200
+ if (!passBits(18)) {
201
+ break;
202
+ }
203
+ len = 0x200 | ((val >>> 14) & 0x1FF);
204
+ }
205
+ else if (val < 0xFFC00000) {
206
+ if (!passBits(20)) {
207
+ break;
208
+ }
209
+ len = 0x400 | ((val >>> 12) & 0x3FF);
210
+ }
211
+ else if (val < 0xFFE00000) {
212
+ if (!passBits(22)) {
213
+ break;
214
+ }
215
+ len = 0x800 | ((val >>> 10) & 0x7FF);
216
+ }
217
+ else if (val < 0xFFF00000) {
218
+ if (!passBits(24)) {
219
+ break;
220
+ }
221
+ len = 0x1000 | ((val >>> 8) & 0xFFF);
222
+ }
223
+ else {
224
+ bitShift = savedBitShift;
225
+ rptr = savedRptr;
226
+ break;
227
+ }
228
+ const src = this.histPtr - off;
229
+ const dstEnd = this.histPtr + len;
230
+ if (src < 0 || dstEnd > MPPC_HIST_LEN) {
231
+ break;
232
+ }
233
+ for (let i = 0; i < len; i++) {
234
+ this.history[this.histPtr + i] = this.history[src + i];
235
+ }
236
+ this.histPtr = dstEnd;
237
+ }
238
+ if (this.histPtr > histHead) {
239
+ outParts.push(buffer_1.Buffer.from(this.history.subarray(histHead, this.histPtr)));
240
+ }
241
+ this.legacy = (0, extended_buffer_1.nativeBufferSubarray)(this.legacy, rptr);
242
+ this.bitOffset = bitShift;
243
+ return outParts.length ? buffer_1.Buffer.concat(outParts) : buffer_1.Buffer.alloc(0);
244
+ }
245
+ }
246
+ exports.MppcDecompressor = MppcDecompressor;
247
+ class MppcCompressor {
248
+ constructor() {
249
+ this.history = new Uint8Array(MPPC_HIST_LEN);
250
+ this.histPtr = 0;
251
+ this.bw = new BitWriter();
252
+ this.dict = new Map();
253
+ }
254
+ key3(b0, b1, b2) {
255
+ return ((b0 & 0xFF) << 16) | ((b1 & 0xFF) << 8) | (b2 & 0xFF);
256
+ }
257
+ addPosToDict(pos) {
258
+ if (pos < 0 || pos + 2 >= this.histPtr) {
259
+ return;
260
+ }
261
+ const key = this.key3(this.history[pos], this.history[pos + 1], this.history[pos + 2]);
262
+ let arr = this.dict.get(key);
263
+ if (!arr) {
264
+ arr = [];
265
+ this.dict.set(key, arr);
266
+ }
267
+ arr.push(pos);
268
+ if (arr.length > 64) {
269
+ arr.splice(0, arr.length - 64);
270
+ }
271
+ }
272
+ pushHistoryByte(b) {
273
+ this.history[this.histPtr] = b & 0xFF;
274
+ this.histPtr++;
275
+ this.addPosToDict(this.histPtr - 3);
276
+ }
277
+ writeLiteral(b) {
278
+ if ((b & 0x80) === 0) {
279
+ this.bw.writeBits(b & 0xFF, 8);
280
+ return;
281
+ }
282
+ this.bw.writeBits(0x100 | (b & 0x7F), 9);
283
+ }
284
+ writeOffset(off) {
285
+ if (off < 0) {
286
+ throw new Error(`MPPC offset underflow: ${off}`);
287
+ }
288
+ if (off < 64) {
289
+ this.bw.writeBits((0b1111 << 6) | (off & 0x3F), 10);
290
+ return;
291
+ }
292
+ if (off < 320) {
293
+ this.bw.writeBits((0b1110 << 8) | ((off - 64) & 0xFF), 12);
294
+ return;
295
+ }
296
+ this.bw.writeBits((0b110 << 13) | ((off - 320) & 0x1FFF), 16);
297
+ }
298
+ writeLength(len) {
299
+ if (len < 3) {
300
+ throw new Error(`MPPC length too small: ${len}`);
301
+ }
302
+ if (len === 3) {
303
+ this.bw.writeBits(0, 1);
304
+ return;
305
+ }
306
+ if (len <= 7) {
307
+ this.bw.writeBits((0b10 << 2) | (len - 4), 4);
308
+ return;
309
+ }
310
+ if (len <= 15) {
311
+ this.bw.writeBits((0b110 << 3) | (len - 8), 6);
312
+ return;
313
+ }
314
+ if (len <= 31) {
315
+ this.bw.writeBits((0b1110 << 4) | (len - 16), 8);
316
+ return;
317
+ }
318
+ if (len <= 63) {
319
+ this.bw.writeBits((0b11110 << 5) | (len - 32), 10);
320
+ return;
321
+ }
322
+ if (len <= 127) {
323
+ this.bw.writeBits((0b111110 << 6) | (len - 64), 12);
324
+ return;
325
+ }
326
+ if (len <= 255) {
327
+ this.bw.writeBits((0b1111110 << 7) | (len - 128), 14);
328
+ return;
329
+ }
330
+ if (len <= 511) {
331
+ this.bw.writeBits((0b11111110 << 8) | (len - 256), 16);
332
+ return;
333
+ }
334
+ if (len <= 1023) {
335
+ this.bw.writeBits((0b111111110 << 9) | (len - 512), 18);
336
+ return;
337
+ }
338
+ if (len <= 2047) {
339
+ this.bw.writeBits((0b1111111110 << 10) | (len - 1024), 20);
340
+ return;
341
+ }
342
+ if (len <= 4095) {
343
+ this.bw.writeBits((0b11111111110 << 11) | (len - 2048), 22);
344
+ return;
345
+ }
346
+ if (len <= 8191) {
347
+ this.bw.writeBits((0b111111111110 << 12) | (len - 4096), 24);
348
+ return;
349
+ }
350
+ throw new Error(`MPPC length too large: ${len}`);
351
+ }
352
+ writeFlushMarker() {
353
+ this.writeOffset(0);
354
+ this.bw.alignToByteWithZeroPadding();
355
+ }
356
+ resetSegment() {
357
+ this.histPtr = 0;
358
+ this.dict.clear();
359
+ }
360
+ update(chunk) {
361
+ const flushBoundary = true;
362
+ const parts = [];
363
+ let i = 0;
364
+ while (i < chunk.length) {
365
+ if (this.histPtr === MPPC_HIST_LEN) {
366
+ this.writeFlushMarker();
367
+ parts.push(this.bw.flushBytes());
368
+ this.resetSegment();
369
+ }
370
+ const remainingHist = MPPC_HIST_LEN - this.histPtr;
371
+ const remainingIn = chunk.length - i;
372
+ const limit = remainingIn < remainingHist ? remainingIn : remainingHist;
373
+ if (limit <= 0) {
374
+ break;
375
+ }
376
+ let bestLen = 0;
377
+ let bestOff = 0;
378
+ if (limit >= 3 && this.histPtr >= 3) {
379
+ const key = this.key3(chunk[i], chunk[i + 1], chunk[i + 2]);
380
+ const candidates = this.dict.get(key);
381
+ if (candidates === null || candidates === void 0 ? void 0 : candidates.length) {
382
+ let checked = 0;
383
+ for (let c = candidates.length - 1; c >= 0; c--) {
384
+ const pos = candidates[c];
385
+ const off = this.histPtr - pos;
386
+ if (off <= 0 || off > this.histPtr) {
387
+ continue;
388
+ }
389
+ if (off > 0x1FFF + 320) {
390
+ continue;
391
+ }
392
+ const maxLen = Math.min(limit, this.histPtr - pos, 8191);
393
+ let l = 3;
394
+ while (l < maxLen && this.history[pos + l] === chunk[i + l]) {
395
+ l++;
396
+ }
397
+ if (l > bestLen) {
398
+ bestLen = l;
399
+ bestOff = off;
400
+ if (bestLen === maxLen) {
401
+ break;
402
+ }
403
+ }
404
+ if (++checked >= 32) {
405
+ break;
406
+ }
407
+ }
408
+ }
409
+ }
410
+ if (bestLen >= 3 && bestOff > 0) {
411
+ this.writeOffset(bestOff);
412
+ this.writeLength(bestLen);
413
+ for (let k = 0; k < bestLen; k++) {
414
+ this.pushHistoryByte(chunk[i + k]);
415
+ }
416
+ i += bestLen;
417
+ }
418
+ else {
419
+ const b = chunk[i];
420
+ this.writeLiteral(b);
421
+ this.pushHistoryByte(b);
422
+ i++;
423
+ }
424
+ const flushed = this.bw.flushBytes();
425
+ if (flushed.length) {
426
+ parts.push(flushed);
427
+ }
428
+ }
429
+ if (flushBoundary) {
430
+ this.writeFlushMarker();
431
+ const flushed = this.bw.flushBytes();
432
+ if (flushed.length) {
433
+ parts.push(flushed);
434
+ }
435
+ if (this.histPtr === MPPC_HIST_LEN) {
436
+ this.resetSegment();
437
+ }
438
+ }
439
+ if (!parts.length) {
440
+ return buffer_1.Buffer.alloc(0);
441
+ }
442
+ return parts.length === 1 ? parts[0] : buffer_1.Buffer.concat(parts);
443
+ }
444
+ }
445
+ exports.MppcCompressor = MppcCompressor;
@@ -0,0 +1 @@
1
+ export { writeCUIntToPwBuffer } from './write-cuint-to-pw-buffer';
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.writeCUIntToPwBuffer = void 0;
4
+ var write_cuint_to_pw_buffer_1 = require("./write-cuint-to-pw-buffer");
5
+ Object.defineProperty(exports, "writeCUIntToPwBuffer", { enumerable: true, get: function () { return write_cuint_to_pw_buffer_1.writeCUIntToPwBuffer; } });
@@ -0,0 +1,2 @@
1
+ import { ExtendedBuffer } from 'extended-buffer';
2
+ export declare function writeCUIntToPwBuffer(buffer: ExtendedBuffer, value: number, unshift?: boolean): void;
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.writeCUIntToPwBuffer = void 0;
4
+ const extended_buffer_1 = require("extended-buffer");
5
+ function writeCUIntToPwBuffer(buffer, value, unshift) {
6
+ (0, extended_buffer_1.assertInteger)(value);
7
+ if (value < 0 || value > 0xFFFFFFFF) {
8
+ throw new extended_buffer_1.ExtendedBufferRangeError('CUINT_VALUE_OUT_OF_RANGE');
9
+ }
10
+ if (value < 0x80) {
11
+ buffer.writeUIntBE(value & 0xFF, 1, unshift);
12
+ }
13
+ else if (value < 0x4000) {
14
+ buffer.writeUIntBE((value | 0x8000) & 0xFFFF, 2, unshift);
15
+ }
16
+ else if (value < 0x20000000) {
17
+ buffer.writeUIntBE((value | 0xC0000000) >>> 0, 4, unshift);
18
+ }
19
+ else {
20
+ const savedPointer = buffer.getPointer();
21
+ try {
22
+ if (unshift) {
23
+ buffer.allocStart(5).writeUIntBE(value >>> 0, 4, true).writeUIntBE(0xE0, 1, true);
24
+ }
25
+ else {
26
+ buffer.allocEnd(5).writeUIntBE(0xE0, 1).writeUIntBE(value >>> 0, 4);
27
+ }
28
+ }
29
+ catch (e) {
30
+ buffer.setPointer(savedPointer);
31
+ throw e;
32
+ }
33
+ }
34
+ }
35
+ exports.writeCUIntToPwBuffer = writeCUIntToPwBuffer;
package/package.json CHANGED
@@ -1,12 +1,35 @@
1
1
  {
2
2
  "name": "pw-buffer",
3
- "version": "2.1.0",
4
- "description": "PW Buffer, Perfect World",
5
- "main": "dist/index.js",
6
- "types": "dist/index.d.ts",
3
+ "version": "3.0.0",
4
+ "description": "PW Buffer, Perfect World, Beijing Perfect World",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "require": "./dist/index.js",
11
+ "default": "./dist/index.js"
12
+ }
13
+ },
7
14
  "scripts": {
8
- "test": "echo \"Error: no test specified\" && exit 1",
9
- "build": "node ./node_modules/.bin/tsc"
15
+ "build": "npm run clean && node ./node_modules/.bin/tsc",
16
+ "clean": "node -e \"['./dist'].forEach(item => require('fs').rmSync(item, {recursive:true,force:true}));\"",
17
+ "prepack": "npm run build"
18
+ },
19
+ "engines": {
20
+ "node": ">=6.0.0"
21
+ },
22
+ "devEngines": {
23
+ "runtime": {
24
+ "name": "node",
25
+ "version": ">=18.0.0",
26
+ "onFail": "error"
27
+ },
28
+ "packageManager": {
29
+ "name": "npm",
30
+ "version": ">=8.6.0",
31
+ "onFail": "error"
32
+ }
10
33
  },
11
34
  "repository": {
12
35
  "type": "git",
@@ -18,11 +41,16 @@
18
41
  "url": "https://github.com/mvcbox/node-pw-buffer/issues"
19
42
  },
20
43
  "homepage": "https://github.com/mvcbox/node-pw-buffer#readme",
44
+ "files": [
45
+ "dist",
46
+ "LICENSE",
47
+ "README.md"
48
+ ],
21
49
  "dependencies": {
22
- "extended-buffer": "^6.0.4"
50
+ "extended-buffer": "^7.0.1"
23
51
  },
24
52
  "devDependencies": {
25
- "@types/node": "^4.9.3",
26
- "typescript": "^3.6.2"
53
+ "@types/node": "^6.14.13",
54
+ "typescript": "^4.7.2"
27
55
  }
28
56
  }