@zero-server/grpc 0.9.1 → 0.9.3
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 +21 -21
- package/index.d.ts +1 -1
- package/index.js +27 -27
- package/lib/debug.js +372 -0
- package/lib/grpc/balancer.js +378 -0
- package/lib/grpc/call.js +708 -0
- package/lib/grpc/client.js +764 -0
- package/lib/grpc/codec.js +1221 -0
- package/lib/grpc/credentials.js +398 -0
- package/lib/grpc/frame.js +262 -0
- package/lib/grpc/health.js +287 -0
- package/lib/grpc/index.js +121 -0
- package/lib/grpc/metadata.js +461 -0
- package/lib/grpc/proto.js +821 -0
- package/lib/grpc/reflection.js +590 -0
- package/lib/grpc/server.js +445 -0
- package/lib/grpc/status.js +118 -0
- package/lib/grpc/watch.js +173 -0
- package/package.json +10 -3
- package/types/app.d.ts +223 -0
- package/types/auth.d.ts +520 -0
- package/types/body.d.ts +14 -0
- package/types/cli.d.ts +2 -0
- package/types/cluster.d.ts +75 -0
- package/types/env.d.ts +80 -0
- package/types/errors.d.ts +316 -0
- package/types/fetch.d.ts +43 -0
- package/types/grpc.d.ts +432 -0
- package/types/index.d.ts +384 -0
- package/types/lifecycle.d.ts +60 -0
- package/types/middleware.d.ts +320 -0
- package/types/observe.d.ts +304 -0
- package/types/orm.d.ts +1887 -0
- package/types/request.d.ts +109 -0
- package/types/response.d.ts +157 -0
- package/types/router.d.ts +78 -0
- package/types/sse.d.ts +78 -0
- package/types/websocket.d.ts +126 -0
|
@@ -0,0 +1,1221 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module grpc/codec
|
|
3
|
+
* @description Zero-dependency Protocol Buffers wire-format encoder/decoder.
|
|
4
|
+
* Implements the proto3 binary encoding for all scalar types,
|
|
5
|
+
* nested messages, repeated fields, maps, oneofs, and enums.
|
|
6
|
+
*
|
|
7
|
+
* Wire types:
|
|
8
|
+
* - 0: Varint (int32, int64, uint32, uint64, sint32, sint64, bool, enum)
|
|
9
|
+
* - 1: 64-bit fixed (fixed64, sfixed64, double)
|
|
10
|
+
* - 2: Length-delimited (string, bytes, nested message, packed repeated)
|
|
11
|
+
* - 5: 32-bit fixed (fixed32, sfixed32, float)
|
|
12
|
+
*
|
|
13
|
+
* @see https://protobuf.dev/programming-guides/encoding/
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const log = require('../debug')('zero:grpc');
|
|
17
|
+
|
|
18
|
+
// -- Constants ---------------------------------------------
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Protobuf wire types.
|
|
22
|
+
* @enum {number}
|
|
23
|
+
*/
|
|
24
|
+
const WIRE_TYPE = {
|
|
25
|
+
VARINT: 0,
|
|
26
|
+
FIXED64: 1,
|
|
27
|
+
LENGTH_DELIMITED: 2,
|
|
28
|
+
/** @deprecated Not used in proto3. */
|
|
29
|
+
START_GROUP: 3,
|
|
30
|
+
/** @deprecated Not used in proto3. */
|
|
31
|
+
END_GROUP: 4,
|
|
32
|
+
FIXED32: 5,
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Maximum varint size in bytes (10 bytes for 64-bit values).
|
|
37
|
+
* @type {number}
|
|
38
|
+
*/
|
|
39
|
+
const MAX_VARINT_SIZE = 10;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Maximum message size (4 MB default, configurable per-call).
|
|
43
|
+
* @type {number}
|
|
44
|
+
*/
|
|
45
|
+
const DEFAULT_MAX_MESSAGE_SIZE = 4 * 1024 * 1024;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Maximum recursion depth for nested messages (prevents stack overflow from malicious payloads).
|
|
49
|
+
* @type {number}
|
|
50
|
+
*/
|
|
51
|
+
const MAX_RECURSION_DEPTH = 64;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Maps proto3 type names to wire types and read/write helpers.
|
|
55
|
+
* @private
|
|
56
|
+
*/
|
|
57
|
+
const TYPE_INFO = {
|
|
58
|
+
double: { wire: WIRE_TYPE.FIXED64, size: 8 },
|
|
59
|
+
float: { wire: WIRE_TYPE.FIXED32, size: 4 },
|
|
60
|
+
int32: { wire: WIRE_TYPE.VARINT },
|
|
61
|
+
int64: { wire: WIRE_TYPE.VARINT },
|
|
62
|
+
uint32: { wire: WIRE_TYPE.VARINT },
|
|
63
|
+
uint64: { wire: WIRE_TYPE.VARINT },
|
|
64
|
+
sint32: { wire: WIRE_TYPE.VARINT },
|
|
65
|
+
sint64: { wire: WIRE_TYPE.VARINT },
|
|
66
|
+
fixed32: { wire: WIRE_TYPE.FIXED32, size: 4 },
|
|
67
|
+
fixed64: { wire: WIRE_TYPE.FIXED64, size: 8 },
|
|
68
|
+
sfixed32: { wire: WIRE_TYPE.FIXED32, size: 4 },
|
|
69
|
+
sfixed64: { wire: WIRE_TYPE.FIXED64, size: 8 },
|
|
70
|
+
bool: { wire: WIRE_TYPE.VARINT },
|
|
71
|
+
string: { wire: WIRE_TYPE.LENGTH_DELIMITED },
|
|
72
|
+
bytes: { wire: WIRE_TYPE.LENGTH_DELIMITED },
|
|
73
|
+
// enum and message are handled dynamically
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// -- Writer ------------------------------------------------
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Protobuf binary writer — encodes JavaScript objects into wire-format bytes.
|
|
80
|
+
*
|
|
81
|
+
* @class
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* const writer = new Writer();
|
|
85
|
+
* writer.writeVarint(1 << 3 | 0, 150); // field 1, varint = 150
|
|
86
|
+
* const bytes = writer.finish();
|
|
87
|
+
*/
|
|
88
|
+
class Writer
|
|
89
|
+
{
|
|
90
|
+
constructor()
|
|
91
|
+
{
|
|
92
|
+
/** @private */
|
|
93
|
+
this._chunks = [];
|
|
94
|
+
/** @private */
|
|
95
|
+
this._size = 0;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// -- Low-Level Primitives ------------------------------
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Write raw bytes.
|
|
102
|
+
*
|
|
103
|
+
* @param {Buffer} buf
|
|
104
|
+
* @returns {Writer} `this` for chaining.
|
|
105
|
+
*/
|
|
106
|
+
writeRaw(buf)
|
|
107
|
+
{
|
|
108
|
+
this._chunks.push(buf);
|
|
109
|
+
this._size += buf.length;
|
|
110
|
+
return this;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Write a varint (variable-length integer) using LEB128 encoding.
|
|
115
|
+
* Handles values up to 2^53 safely (JavaScript number precision limit).
|
|
116
|
+
*
|
|
117
|
+
* @param {number} value - Non-negative integer.
|
|
118
|
+
* @returns {Writer} `this` for chaining.
|
|
119
|
+
*/
|
|
120
|
+
writeVarint(value)
|
|
121
|
+
{
|
|
122
|
+
const buf = Buffer.alloc(MAX_VARINT_SIZE);
|
|
123
|
+
let offset = 0;
|
|
124
|
+
|
|
125
|
+
// Handle negative numbers as unsigned 64-bit
|
|
126
|
+
if (value < 0)
|
|
127
|
+
{
|
|
128
|
+
// Two's complement for 64-bit
|
|
129
|
+
const lo = (value & 0xFFFFFFFF) >>> 0;
|
|
130
|
+
const hi = (Math.floor(value / 0x100000000) & 0xFFFFFFFF) >>> 0;
|
|
131
|
+
return this._writeVarint64(lo, hi);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
while (value > 0x7F)
|
|
135
|
+
{
|
|
136
|
+
buf[offset++] = (value & 0x7F) | 0x80;
|
|
137
|
+
value >>>= 7;
|
|
138
|
+
}
|
|
139
|
+
buf[offset++] = value & 0x7F;
|
|
140
|
+
this._chunks.push(buf.subarray(0, offset));
|
|
141
|
+
this._size += offset;
|
|
142
|
+
return this;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Write a 64-bit varint as two 32-bit halves.
|
|
147
|
+
* @private
|
|
148
|
+
* @param {number} lo - Low 32 bits.
|
|
149
|
+
* @param {number} hi - High 32 bits.
|
|
150
|
+
* @returns {Writer}
|
|
151
|
+
*/
|
|
152
|
+
_writeVarint64(lo, hi)
|
|
153
|
+
{
|
|
154
|
+
const buf = Buffer.alloc(MAX_VARINT_SIZE);
|
|
155
|
+
let offset = 0;
|
|
156
|
+
|
|
157
|
+
while (hi > 0 || lo > 0x7F)
|
|
158
|
+
{
|
|
159
|
+
buf[offset++] = (lo & 0x7F) | 0x80;
|
|
160
|
+
lo = ((lo >>> 7) | (hi << 25)) >>> 0;
|
|
161
|
+
hi >>>= 7;
|
|
162
|
+
}
|
|
163
|
+
buf[offset++] = lo & 0x7F;
|
|
164
|
+
this._chunks.push(buf.subarray(0, offset));
|
|
165
|
+
this._size += offset;
|
|
166
|
+
return this;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Write a signed varint using ZigZag encoding (sint32/sint64).
|
|
171
|
+
*
|
|
172
|
+
* @param {number} value - Signed integer.
|
|
173
|
+
* @returns {Writer}
|
|
174
|
+
*/
|
|
175
|
+
writeSVarint(value)
|
|
176
|
+
{
|
|
177
|
+
// ZigZag: (n << 1) ^ (n >> 31) for 32-bit
|
|
178
|
+
return this.writeVarint(((value << 1) ^ (value >> 31)) >>> 0);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Write a 32-bit fixed integer (little-endian).
|
|
183
|
+
*
|
|
184
|
+
* @param {number} value
|
|
185
|
+
* @returns {Writer}
|
|
186
|
+
*/
|
|
187
|
+
writeFixed32(value)
|
|
188
|
+
{
|
|
189
|
+
const buf = Buffer.alloc(4);
|
|
190
|
+
buf.writeUInt32LE(value >>> 0, 0);
|
|
191
|
+
return this.writeRaw(buf);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Write a 32-bit signed fixed integer (little-endian).
|
|
196
|
+
*
|
|
197
|
+
* @param {number} value
|
|
198
|
+
* @returns {Writer}
|
|
199
|
+
*/
|
|
200
|
+
writeSFixed32(value)
|
|
201
|
+
{
|
|
202
|
+
const buf = Buffer.alloc(4);
|
|
203
|
+
buf.writeInt32LE(value, 0);
|
|
204
|
+
return this.writeRaw(buf);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Write a 64-bit fixed integer (little-endian) from a BigInt or number.
|
|
209
|
+
*
|
|
210
|
+
* @param {number|BigInt} value
|
|
211
|
+
* @returns {Writer}
|
|
212
|
+
*/
|
|
213
|
+
writeFixed64(value)
|
|
214
|
+
{
|
|
215
|
+
const buf = Buffer.alloc(8);
|
|
216
|
+
if (typeof value === 'bigint')
|
|
217
|
+
{
|
|
218
|
+
buf.writeBigUInt64LE(value, 0);
|
|
219
|
+
}
|
|
220
|
+
else
|
|
221
|
+
{
|
|
222
|
+
buf.writeUInt32LE(value >>> 0, 0);
|
|
223
|
+
buf.writeUInt32LE((value / 0x100000000) >>> 0, 4);
|
|
224
|
+
}
|
|
225
|
+
return this.writeRaw(buf);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Write a 64-bit signed fixed integer (little-endian).
|
|
230
|
+
*
|
|
231
|
+
* @param {number|BigInt} value
|
|
232
|
+
* @returns {Writer}
|
|
233
|
+
*/
|
|
234
|
+
writeSFixed64(value)
|
|
235
|
+
{
|
|
236
|
+
const buf = Buffer.alloc(8);
|
|
237
|
+
if (typeof value === 'bigint')
|
|
238
|
+
{
|
|
239
|
+
buf.writeBigInt64LE(value, 0);
|
|
240
|
+
}
|
|
241
|
+
else
|
|
242
|
+
{
|
|
243
|
+
buf.writeInt32LE(value & 0xFFFFFFFF, 0);
|
|
244
|
+
buf.writeInt32LE(Math.floor(value / 0x100000000), 4);
|
|
245
|
+
}
|
|
246
|
+
return this.writeRaw(buf);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Write a 32-bit IEEE 754 float.
|
|
251
|
+
*
|
|
252
|
+
* @param {number} value
|
|
253
|
+
* @returns {Writer}
|
|
254
|
+
*/
|
|
255
|
+
writeFloat(value)
|
|
256
|
+
{
|
|
257
|
+
const buf = Buffer.alloc(4);
|
|
258
|
+
buf.writeFloatLE(value, 0);
|
|
259
|
+
return this.writeRaw(buf);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Write a 64-bit IEEE 754 double.
|
|
264
|
+
*
|
|
265
|
+
* @param {number} value
|
|
266
|
+
* @returns {Writer}
|
|
267
|
+
*/
|
|
268
|
+
writeDouble(value)
|
|
269
|
+
{
|
|
270
|
+
const buf = Buffer.alloc(8);
|
|
271
|
+
buf.writeDoubleLE(value, 0);
|
|
272
|
+
return this.writeRaw(buf);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Write a boolean as a single-byte varint.
|
|
277
|
+
*
|
|
278
|
+
* @param {boolean} value
|
|
279
|
+
* @returns {Writer}
|
|
280
|
+
*/
|
|
281
|
+
writeBool(value)
|
|
282
|
+
{
|
|
283
|
+
return this.writeVarint(value ? 1 : 0);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Write a UTF-8 string (length-prefixed).
|
|
288
|
+
*
|
|
289
|
+
* @param {string} value
|
|
290
|
+
* @returns {Writer}
|
|
291
|
+
*/
|
|
292
|
+
writeString(value)
|
|
293
|
+
{
|
|
294
|
+
const strBuf = Buffer.from(value, 'utf8');
|
|
295
|
+
this.writeVarint(strBuf.length);
|
|
296
|
+
return this.writeRaw(strBuf);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Write raw bytes (length-prefixed).
|
|
301
|
+
*
|
|
302
|
+
* @param {Buffer} value
|
|
303
|
+
* @returns {Writer}
|
|
304
|
+
*/
|
|
305
|
+
writeBytes(value)
|
|
306
|
+
{
|
|
307
|
+
if (!Buffer.isBuffer(value)) value = Buffer.from(value);
|
|
308
|
+
this.writeVarint(value.length);
|
|
309
|
+
return this.writeRaw(value);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Write a field tag (field number + wire type).
|
|
314
|
+
*
|
|
315
|
+
* @param {number} fieldNumber - Protobuf field number.
|
|
316
|
+
* @param {number} wireType - Wire type (0-5).
|
|
317
|
+
* @returns {Writer}
|
|
318
|
+
*/
|
|
319
|
+
writeTag(fieldNumber, wireType)
|
|
320
|
+
{
|
|
321
|
+
return this.writeVarint((fieldNumber << 3) | wireType);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Finalize and return the complete encoded buffer.
|
|
326
|
+
*
|
|
327
|
+
* @returns {Buffer} Concatenated protobuf binary.
|
|
328
|
+
*/
|
|
329
|
+
finish()
|
|
330
|
+
{
|
|
331
|
+
if (this._chunks.length === 0) return Buffer.alloc(0);
|
|
332
|
+
if (this._chunks.length === 1) return this._chunks[0];
|
|
333
|
+
return Buffer.concat(this._chunks, this._size);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Get the total byte size of all written data.
|
|
338
|
+
*
|
|
339
|
+
* @returns {number}
|
|
340
|
+
*/
|
|
341
|
+
get length()
|
|
342
|
+
{
|
|
343
|
+
return this._size;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// -- Field-level convenience methods (tag + value) ------
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Write a string field (tag + length-delimited string).
|
|
350
|
+
* @param {number} fieldNumber
|
|
351
|
+
* @param {string} value
|
|
352
|
+
* @returns {Writer}
|
|
353
|
+
*/
|
|
354
|
+
string(fieldNumber, value)
|
|
355
|
+
{
|
|
356
|
+
this.writeTag(fieldNumber, 2);
|
|
357
|
+
return this.writeString(value);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Write a bytes/embedded-message field (tag + length-delimited bytes).
|
|
362
|
+
* @param {number} fieldNumber
|
|
363
|
+
* @param {Buffer} value
|
|
364
|
+
* @returns {Writer}
|
|
365
|
+
*/
|
|
366
|
+
bytes(fieldNumber, value)
|
|
367
|
+
{
|
|
368
|
+
this.writeTag(fieldNumber, 2);
|
|
369
|
+
return this.writeBytes(value);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Write an int32/enum field (tag + varint).
|
|
374
|
+
* @param {number} fieldNumber
|
|
375
|
+
* @param {number} value
|
|
376
|
+
* @returns {Writer}
|
|
377
|
+
*/
|
|
378
|
+
int32(fieldNumber, value)
|
|
379
|
+
{
|
|
380
|
+
this.writeTag(fieldNumber, 0);
|
|
381
|
+
return this.writeVarint(value);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Write a bool field (tag + varint 0/1).
|
|
386
|
+
* @param {number} fieldNumber
|
|
387
|
+
* @param {boolean} value
|
|
388
|
+
* @returns {Writer}
|
|
389
|
+
*/
|
|
390
|
+
bool(fieldNumber, value)
|
|
391
|
+
{
|
|
392
|
+
this.writeTag(fieldNumber, 0);
|
|
393
|
+
return this.writeBool(value);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// -- Reader ------------------------------------------------
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Protobuf binary reader — decodes wire-format bytes into JavaScript values.
|
|
401
|
+
*
|
|
402
|
+
* @class
|
|
403
|
+
*
|
|
404
|
+
* @param {Buffer} buffer - Protobuf binary data to read.
|
|
405
|
+
*
|
|
406
|
+
* @example
|
|
407
|
+
* const reader = new Reader(buffer);
|
|
408
|
+
* while (reader.remaining > 0) {
|
|
409
|
+
* const { fieldNumber, wireType } = reader.readTag();
|
|
410
|
+
* // read value based on wireType...
|
|
411
|
+
* }
|
|
412
|
+
*/
|
|
413
|
+
class Reader
|
|
414
|
+
{
|
|
415
|
+
/**
|
|
416
|
+
* @constructor
|
|
417
|
+
* @param {Buffer} buffer - Protobuf wire-format data.
|
|
418
|
+
*/
|
|
419
|
+
constructor(buffer)
|
|
420
|
+
{
|
|
421
|
+
if (!Buffer.isBuffer(buffer))
|
|
422
|
+
throw new TypeError('Reader requires a Buffer');
|
|
423
|
+
|
|
424
|
+
/** @private */
|
|
425
|
+
this._buf = buffer;
|
|
426
|
+
/** @private */
|
|
427
|
+
this._pos = 0;
|
|
428
|
+
/** @private */
|
|
429
|
+
this._end = buffer.length;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Number of bytes remaining to be read.
|
|
434
|
+
*
|
|
435
|
+
* @returns {number}
|
|
436
|
+
*/
|
|
437
|
+
get remaining()
|
|
438
|
+
{
|
|
439
|
+
return this._end - this._pos;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Whether all bytes have been consumed.
|
|
444
|
+
*
|
|
445
|
+
* @returns {boolean}
|
|
446
|
+
*/
|
|
447
|
+
get done()
|
|
448
|
+
{
|
|
449
|
+
return this._pos >= this._end;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* Current read position (byte offset).
|
|
454
|
+
*
|
|
455
|
+
* @returns {number}
|
|
456
|
+
*/
|
|
457
|
+
get position()
|
|
458
|
+
{
|
|
459
|
+
return this._pos;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// -- Low-Level Primitives ------------------------------
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* Read a varint (LEB128-encoded variable-length integer).
|
|
466
|
+
* Returns a JavaScript number (safe for values up to 2^53).
|
|
467
|
+
*
|
|
468
|
+
* @returns {number}
|
|
469
|
+
*/
|
|
470
|
+
readVarint()
|
|
471
|
+
{
|
|
472
|
+
let result = 0;
|
|
473
|
+
let shift = 0;
|
|
474
|
+
|
|
475
|
+
for (let i = 0; i < MAX_VARINT_SIZE; i++)
|
|
476
|
+
{
|
|
477
|
+
if (this._pos >= this._end)
|
|
478
|
+
throw new RangeError('Varint extends past end of buffer');
|
|
479
|
+
|
|
480
|
+
const byte = this._buf[this._pos++];
|
|
481
|
+
result |= (byte & 0x7F) << shift;
|
|
482
|
+
|
|
483
|
+
if ((byte & 0x80) === 0)
|
|
484
|
+
{
|
|
485
|
+
// For values that might overflow 32-bit, reconstruct using multiplication
|
|
486
|
+
if (shift >= 28)
|
|
487
|
+
{
|
|
488
|
+
return this._readVarintSlow(result, shift, byte);
|
|
489
|
+
}
|
|
490
|
+
return result >>> 0;
|
|
491
|
+
}
|
|
492
|
+
shift += 7;
|
|
493
|
+
if (shift >= 28)
|
|
494
|
+
{
|
|
495
|
+
return this._readVarintHigh(result, shift);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
throw new RangeError('Varint too long (> 10 bytes)');
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* Handle high-bit varint continuation.
|
|
504
|
+
* @private
|
|
505
|
+
*/
|
|
506
|
+
_readVarintHigh(lo, shift)
|
|
507
|
+
{
|
|
508
|
+
let hi = 0;
|
|
509
|
+
let hiShift = 0;
|
|
510
|
+
|
|
511
|
+
if (shift === 28)
|
|
512
|
+
{
|
|
513
|
+
// We've read 4 bytes (28 bits). The 5th byte contributes to both lo and hi.
|
|
514
|
+
if (this._pos >= this._end)
|
|
515
|
+
throw new RangeError('Varint extends past end of buffer');
|
|
516
|
+
const byte = this._buf[this._pos++];
|
|
517
|
+
lo |= (byte & 0x0F) << 28;
|
|
518
|
+
hi = (byte & 0x7F) >> 4;
|
|
519
|
+
if ((byte & 0x80) === 0)
|
|
520
|
+
return (hi * 0x100000000 + (lo >>> 0));
|
|
521
|
+
hiShift = 3;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
for (let i = 0; i < 5; i++)
|
|
525
|
+
{
|
|
526
|
+
if (this._pos >= this._end)
|
|
527
|
+
throw new RangeError('Varint extends past end of buffer');
|
|
528
|
+
const byte = this._buf[this._pos++];
|
|
529
|
+
hi |= (byte & 0x7F) << hiShift;
|
|
530
|
+
hiShift += 7;
|
|
531
|
+
if ((byte & 0x80) === 0)
|
|
532
|
+
return (hi * 0x100000000 + (lo >>> 0));
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
throw new RangeError('Varint too long');
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/**
|
|
539
|
+
* Reconstruct large varint value.
|
|
540
|
+
* @private
|
|
541
|
+
*/
|
|
542
|
+
_readVarintSlow(lo, shift, lastByte)
|
|
543
|
+
{
|
|
544
|
+
return lo >>> 0;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* Read a signed varint using ZigZag decoding (sint32/sint64).
|
|
549
|
+
*
|
|
550
|
+
* @returns {number}
|
|
551
|
+
*/
|
|
552
|
+
readSVarint()
|
|
553
|
+
{
|
|
554
|
+
const n = this.readVarint();
|
|
555
|
+
return ((n >>> 1) ^ -(n & 1)) | 0;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
/**
|
|
559
|
+
* Read a 32-bit fixed unsigned integer (little-endian).
|
|
560
|
+
*
|
|
561
|
+
* @returns {number}
|
|
562
|
+
*/
|
|
563
|
+
readFixed32()
|
|
564
|
+
{
|
|
565
|
+
this._checkBounds(4);
|
|
566
|
+
const val = this._buf.readUInt32LE(this._pos);
|
|
567
|
+
this._pos += 4;
|
|
568
|
+
return val;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
/**
|
|
572
|
+
* Read a 32-bit fixed signed integer (little-endian).
|
|
573
|
+
*
|
|
574
|
+
* @returns {number}
|
|
575
|
+
*/
|
|
576
|
+
readSFixed32()
|
|
577
|
+
{
|
|
578
|
+
this._checkBounds(4);
|
|
579
|
+
const val = this._buf.readInt32LE(this._pos);
|
|
580
|
+
this._pos += 4;
|
|
581
|
+
return val;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
/**
|
|
585
|
+
* Read a 64-bit fixed unsigned integer (little-endian).
|
|
586
|
+
* Returns a number (precision loss above 2^53).
|
|
587
|
+
*
|
|
588
|
+
* @returns {number}
|
|
589
|
+
*/
|
|
590
|
+
readFixed64()
|
|
591
|
+
{
|
|
592
|
+
this._checkBounds(8);
|
|
593
|
+
const lo = this._buf.readUInt32LE(this._pos);
|
|
594
|
+
const hi = this._buf.readUInt32LE(this._pos + 4);
|
|
595
|
+
this._pos += 8;
|
|
596
|
+
return hi * 0x100000000 + lo;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
/**
|
|
600
|
+
* Read a 64-bit fixed signed integer (little-endian).
|
|
601
|
+
*
|
|
602
|
+
* @returns {number}
|
|
603
|
+
*/
|
|
604
|
+
readSFixed64()
|
|
605
|
+
{
|
|
606
|
+
this._checkBounds(8);
|
|
607
|
+
const lo = this._buf.readUInt32LE(this._pos);
|
|
608
|
+
const hi = this._buf.readInt32LE(this._pos + 4);
|
|
609
|
+
this._pos += 8;
|
|
610
|
+
return hi * 0x100000000 + lo;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
/**
|
|
614
|
+
* Read a 32-bit IEEE 754 float.
|
|
615
|
+
*
|
|
616
|
+
* @returns {number}
|
|
617
|
+
*/
|
|
618
|
+
readFloat()
|
|
619
|
+
{
|
|
620
|
+
this._checkBounds(4);
|
|
621
|
+
const val = this._buf.readFloatLE(this._pos);
|
|
622
|
+
this._pos += 4;
|
|
623
|
+
return val;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
/**
|
|
627
|
+
* Read a 64-bit IEEE 754 double.
|
|
628
|
+
*
|
|
629
|
+
* @returns {number}
|
|
630
|
+
*/
|
|
631
|
+
readDouble()
|
|
632
|
+
{
|
|
633
|
+
this._checkBounds(8);
|
|
634
|
+
const val = this._buf.readDoubleLE(this._pos);
|
|
635
|
+
this._pos += 8;
|
|
636
|
+
return val;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
/**
|
|
640
|
+
* Read a boolean varint.
|
|
641
|
+
*
|
|
642
|
+
* @returns {boolean}
|
|
643
|
+
*/
|
|
644
|
+
readBool()
|
|
645
|
+
{
|
|
646
|
+
return this.readVarint() !== 0;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
/**
|
|
650
|
+
* Read a length-prefixed UTF-8 string.
|
|
651
|
+
*
|
|
652
|
+
* @returns {string}
|
|
653
|
+
*/
|
|
654
|
+
readString()
|
|
655
|
+
{
|
|
656
|
+
const len = this.readVarint();
|
|
657
|
+
this._checkBounds(len);
|
|
658
|
+
const str = this._buf.toString('utf8', this._pos, this._pos + len);
|
|
659
|
+
this._pos += len;
|
|
660
|
+
return str;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
/**
|
|
664
|
+
* Read length-prefixed raw bytes.
|
|
665
|
+
*
|
|
666
|
+
* @returns {Buffer}
|
|
667
|
+
*/
|
|
668
|
+
readBytes()
|
|
669
|
+
{
|
|
670
|
+
const len = this.readVarint();
|
|
671
|
+
this._checkBounds(len);
|
|
672
|
+
const buf = this._buf.subarray(this._pos, this._pos + len);
|
|
673
|
+
this._pos += len;
|
|
674
|
+
return Buffer.from(buf); // copy to detach from source
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
/**
|
|
678
|
+
* Read a field tag and decode field number + wire type.
|
|
679
|
+
*
|
|
680
|
+
* @returns {{ fieldNumber: number, wireType: number }}
|
|
681
|
+
*/
|
|
682
|
+
readTag()
|
|
683
|
+
{
|
|
684
|
+
const tag = this.readVarint();
|
|
685
|
+
return {
|
|
686
|
+
fieldNumber: tag >>> 3,
|
|
687
|
+
wireType: tag & 0x07,
|
|
688
|
+
};
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
/**
|
|
692
|
+
* Skip a field value based on its wire type (for unknown fields).
|
|
693
|
+
*
|
|
694
|
+
* @param {number} wireType - Wire type to skip.
|
|
695
|
+
*/
|
|
696
|
+
skipField(wireType)
|
|
697
|
+
{
|
|
698
|
+
switch (wireType)
|
|
699
|
+
{
|
|
700
|
+
case WIRE_TYPE.VARINT:
|
|
701
|
+
this.readVarint();
|
|
702
|
+
break;
|
|
703
|
+
case WIRE_TYPE.FIXED64:
|
|
704
|
+
this._checkBounds(8);
|
|
705
|
+
this._pos += 8;
|
|
706
|
+
break;
|
|
707
|
+
case WIRE_TYPE.LENGTH_DELIMITED:
|
|
708
|
+
{
|
|
709
|
+
const len = this.readVarint();
|
|
710
|
+
this._checkBounds(len);
|
|
711
|
+
this._pos += len;
|
|
712
|
+
break;
|
|
713
|
+
}
|
|
714
|
+
case WIRE_TYPE.FIXED32:
|
|
715
|
+
this._checkBounds(4);
|
|
716
|
+
this._pos += 4;
|
|
717
|
+
break;
|
|
718
|
+
default:
|
|
719
|
+
throw new Error(`Unknown wire type: ${wireType}`);
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
/**
|
|
724
|
+
* Create a sub-reader for a length-delimited embedded message.
|
|
725
|
+
*
|
|
726
|
+
* @returns {Reader} A new Reader limited to the embedded message bytes.
|
|
727
|
+
*/
|
|
728
|
+
readSubReader()
|
|
729
|
+
{
|
|
730
|
+
const len = this.readVarint();
|
|
731
|
+
this._checkBounds(len);
|
|
732
|
+
const sub = new Reader(this._buf.subarray(this._pos, this._pos + len));
|
|
733
|
+
this._pos += len;
|
|
734
|
+
return sub;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
/**
|
|
738
|
+
* Bounds check helper.
|
|
739
|
+
* @private
|
|
740
|
+
*/
|
|
741
|
+
_checkBounds(needed)
|
|
742
|
+
{
|
|
743
|
+
if (this._pos + needed > this._end)
|
|
744
|
+
throw new RangeError(`Not enough data: need ${needed} bytes at offset ${this._pos}, have ${this._end - this._pos}`);
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
// -- Message Codec -----------------------------------------
|
|
749
|
+
|
|
750
|
+
/**
|
|
751
|
+
* Encode a JavaScript object to protobuf binary using a message descriptor.
|
|
752
|
+
*
|
|
753
|
+
* @param {object} obj - The JavaScript object to encode.
|
|
754
|
+
* @param {object} messageDesc - Message descriptor from the proto parser.
|
|
755
|
+
* @param {Object<string, object>} allMessages - Map of all message descriptors (for nested types).
|
|
756
|
+
* @param {number} [depth=0] - Current recursion depth (stack overflow protection).
|
|
757
|
+
* @returns {Buffer} Encoded protobuf binary.
|
|
758
|
+
*
|
|
759
|
+
* @example
|
|
760
|
+
* const buf = encode({ name: 'Alice', age: 30 }, personDesc, allMessages);
|
|
761
|
+
*/
|
|
762
|
+
function encode(obj, messageDesc, allMessages, depth = 0)
|
|
763
|
+
{
|
|
764
|
+
if (depth > MAX_RECURSION_DEPTH)
|
|
765
|
+
throw new Error(`Maximum encoding depth (${MAX_RECURSION_DEPTH}) exceeded — possible circular reference`);
|
|
766
|
+
|
|
767
|
+
if (!obj || typeof obj !== 'object')
|
|
768
|
+
return Buffer.alloc(0);
|
|
769
|
+
|
|
770
|
+
const writer = new Writer();
|
|
771
|
+
const fields = messageDesc.fields;
|
|
772
|
+
|
|
773
|
+
for (const field of fields)
|
|
774
|
+
{
|
|
775
|
+
const value = obj[field.name];
|
|
776
|
+
|
|
777
|
+
// Proto3: skip fields with default/zero values
|
|
778
|
+
if (value === undefined || value === null) continue;
|
|
779
|
+
|
|
780
|
+
if (field.map)
|
|
781
|
+
{
|
|
782
|
+
_encodeMap(writer, field, value, allMessages, depth);
|
|
783
|
+
}
|
|
784
|
+
else if (field.repeated)
|
|
785
|
+
{
|
|
786
|
+
_encodeRepeated(writer, field, value, allMessages, depth);
|
|
787
|
+
}
|
|
788
|
+
else
|
|
789
|
+
{
|
|
790
|
+
_encodeField(writer, field, value, allMessages, depth);
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
return writer.finish();
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
/**
|
|
798
|
+
* Decode protobuf binary into a JavaScript object using a message descriptor.
|
|
799
|
+
*
|
|
800
|
+
* @param {Buffer} buffer - Protobuf wire-format data.
|
|
801
|
+
* @param {object} messageDesc - Message descriptor from the proto parser.
|
|
802
|
+
* @param {Object<string, object>} allMessages - Map of all message descriptors.
|
|
803
|
+
* @param {number} [depth=0] - Current recursion depth.
|
|
804
|
+
* @returns {object} Decoded JavaScript object.
|
|
805
|
+
*
|
|
806
|
+
* @example
|
|
807
|
+
* const person = decode(buffer, personDesc, allMessages);
|
|
808
|
+
*/
|
|
809
|
+
function decode(buffer, messageDesc, allMessages, depth = 0)
|
|
810
|
+
{
|
|
811
|
+
if (depth > MAX_RECURSION_DEPTH)
|
|
812
|
+
throw new Error(`Maximum decoding depth (${MAX_RECURSION_DEPTH}) exceeded — possible circular reference`);
|
|
813
|
+
|
|
814
|
+
if (!Buffer.isBuffer(buffer) || buffer.length === 0)
|
|
815
|
+
return _defaultObject(messageDesc);
|
|
816
|
+
|
|
817
|
+
const reader = new Reader(buffer);
|
|
818
|
+
const result = _defaultObject(messageDesc);
|
|
819
|
+
const fieldMap = {};
|
|
820
|
+
|
|
821
|
+
for (const f of messageDesc.fields)
|
|
822
|
+
{
|
|
823
|
+
fieldMap[f.number] = f;
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
while (reader.remaining > 0)
|
|
827
|
+
{
|
|
828
|
+
const { fieldNumber, wireType } = reader.readTag();
|
|
829
|
+
const field = fieldMap[fieldNumber];
|
|
830
|
+
|
|
831
|
+
if (!field)
|
|
832
|
+
{
|
|
833
|
+
// Unknown field — skip it (forward compatibility)
|
|
834
|
+
reader.skipField(wireType);
|
|
835
|
+
continue;
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
if (field.map)
|
|
839
|
+
{
|
|
840
|
+
_decodeMapEntry(reader, field, result, allMessages, depth);
|
|
841
|
+
}
|
|
842
|
+
else if (field.repeated && wireType === WIRE_TYPE.LENGTH_DELIMITED && isPackable(field.type))
|
|
843
|
+
{
|
|
844
|
+
// Packed repeated field
|
|
845
|
+
_decodePackedRepeated(reader, field, result);
|
|
846
|
+
}
|
|
847
|
+
else if (field.repeated)
|
|
848
|
+
{
|
|
849
|
+
// Non-packed repeated (one element per tag)
|
|
850
|
+
const val = _readFieldValue(reader, field, wireType, allMessages, depth);
|
|
851
|
+
result[field.name].push(val);
|
|
852
|
+
}
|
|
853
|
+
else
|
|
854
|
+
{
|
|
855
|
+
result[field.name] = _readFieldValue(reader, field, wireType, allMessages, depth);
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
return result;
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
// -- Private Encoding Helpers ------------------------------
|
|
863
|
+
|
|
864
|
+
/** @private */
|
|
865
|
+
function _encodeField(writer, field, value, allMessages, depth)
|
|
866
|
+
{
|
|
867
|
+
const typeInfo = TYPE_INFO[field.type];
|
|
868
|
+
|
|
869
|
+
if (typeInfo)
|
|
870
|
+
{
|
|
871
|
+
// Scalar type — skip default values in proto3
|
|
872
|
+
if (_isDefaultValue(field.type, value)) return;
|
|
873
|
+
|
|
874
|
+
writer.writeTag(field.number, typeInfo.wire);
|
|
875
|
+
_writeScalar(writer, field.type, value);
|
|
876
|
+
}
|
|
877
|
+
else if (field.enumDef)
|
|
878
|
+
{
|
|
879
|
+
// Enum field
|
|
880
|
+
const numVal = typeof value === 'string' ? (field.enumDef.values[value] || 0) : Number(value);
|
|
881
|
+
if (numVal === 0) return; // default enum value
|
|
882
|
+
writer.writeTag(field.number, WIRE_TYPE.VARINT);
|
|
883
|
+
writer.writeVarint(numVal);
|
|
884
|
+
}
|
|
885
|
+
else
|
|
886
|
+
{
|
|
887
|
+
// Nested message
|
|
888
|
+
const msgDesc = allMessages[field.type];
|
|
889
|
+
if (!msgDesc) throw new Error(`Unknown message type: ${field.type}`);
|
|
890
|
+
|
|
891
|
+
const nested = encode(value, msgDesc, allMessages, depth + 1);
|
|
892
|
+
writer.writeTag(field.number, WIRE_TYPE.LENGTH_DELIMITED);
|
|
893
|
+
writer.writeBytes(nested);
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
/** @private */
|
|
898
|
+
function _encodeRepeated(writer, field, values, allMessages, depth)
|
|
899
|
+
{
|
|
900
|
+
if (!Array.isArray(values) || values.length === 0) return;
|
|
901
|
+
|
|
902
|
+
const typeInfo = TYPE_INFO[field.type];
|
|
903
|
+
|
|
904
|
+
// Pack scalars (proto3 default)
|
|
905
|
+
if (typeInfo && isPackable(field.type))
|
|
906
|
+
{
|
|
907
|
+
const inner = new Writer();
|
|
908
|
+
for (const v of values) _writeScalar(inner, field.type, v);
|
|
909
|
+
writer.writeTag(field.number, WIRE_TYPE.LENGTH_DELIMITED);
|
|
910
|
+
const packed = inner.finish();
|
|
911
|
+
writer.writeVarint(packed.length);
|
|
912
|
+
writer.writeRaw(packed);
|
|
913
|
+
}
|
|
914
|
+
else
|
|
915
|
+
{
|
|
916
|
+
// Non-packable (strings, bytes, messages) — one tag per element
|
|
917
|
+
for (const v of values) _encodeField(writer, field, v, allMessages, depth);
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
/** @private */
|
|
922
|
+
function _encodeMap(writer, field, mapObj, allMessages, depth)
|
|
923
|
+
{
|
|
924
|
+
if (!mapObj || typeof mapObj !== 'object') return;
|
|
925
|
+
|
|
926
|
+
// Maps are encoded as repeated message { key = 1; value = 2; }
|
|
927
|
+
const entries = mapObj instanceof Map ? mapObj.entries() : Object.entries(mapObj);
|
|
928
|
+
|
|
929
|
+
for (const [k, v] of entries)
|
|
930
|
+
{
|
|
931
|
+
const entryWriter = new Writer();
|
|
932
|
+
|
|
933
|
+
// Encode key (field 1)
|
|
934
|
+
const keyField = { number: 1, type: field.keyType, name: 'key' };
|
|
935
|
+
entryWriter.writeTag(1, TYPE_INFO[field.keyType].wire);
|
|
936
|
+
_writeScalar(entryWriter, field.keyType, k);
|
|
937
|
+
|
|
938
|
+
// Encode value (field 2)
|
|
939
|
+
const valField = { number: 2, type: field.valueType, name: 'value', enumDef: field.enumDef };
|
|
940
|
+
_encodeField(entryWriter, valField, v, allMessages, depth);
|
|
941
|
+
|
|
942
|
+
const entryBuf = entryWriter.finish();
|
|
943
|
+
writer.writeTag(field.number, WIRE_TYPE.LENGTH_DELIMITED);
|
|
944
|
+
writer.writeVarint(entryBuf.length);
|
|
945
|
+
writer.writeRaw(entryBuf);
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
// -- Private Decoding Helpers ------------------------------
|
|
950
|
+
|
|
951
|
+
/** @private */
|
|
952
|
+
function _readFieldValue(reader, field, wireType, allMessages, depth)
|
|
953
|
+
{
|
|
954
|
+
const typeInfo = TYPE_INFO[field.type];
|
|
955
|
+
|
|
956
|
+
if (typeInfo)
|
|
957
|
+
{
|
|
958
|
+
return _readScalar(reader, field.type, wireType);
|
|
959
|
+
}
|
|
960
|
+
else if (field.enumDef)
|
|
961
|
+
{
|
|
962
|
+
const val = reader.readVarint();
|
|
963
|
+
// Reverse lookup: number → name
|
|
964
|
+
const reverseEnum = field.enumDef._reverse || _buildReverseEnum(field.enumDef);
|
|
965
|
+
return reverseEnum[val] || val;
|
|
966
|
+
}
|
|
967
|
+
else
|
|
968
|
+
{
|
|
969
|
+
// Nested message
|
|
970
|
+
const msgDesc = allMessages[field.type];
|
|
971
|
+
if (!msgDesc) throw new Error(`Unknown message type: ${field.type}`);
|
|
972
|
+
const sub = reader.readSubReader();
|
|
973
|
+
return decode(sub._buf, msgDesc, allMessages, depth + 1);
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
/** @private */
|
|
978
|
+
function _decodeMapEntry(reader, field, result, allMessages, depth)
|
|
979
|
+
{
|
|
980
|
+
const sub = reader.readSubReader();
|
|
981
|
+
let key, value;
|
|
982
|
+
|
|
983
|
+
while (sub.remaining > 0)
|
|
984
|
+
{
|
|
985
|
+
const { fieldNumber, wireType } = sub.readTag();
|
|
986
|
+
if (fieldNumber === 1)
|
|
987
|
+
{
|
|
988
|
+
key = _readScalar(sub, field.keyType, wireType);
|
|
989
|
+
}
|
|
990
|
+
else if (fieldNumber === 2)
|
|
991
|
+
{
|
|
992
|
+
const valField = { type: field.valueType, enumDef: field.enumDef };
|
|
993
|
+
value = _readFieldValue(sub, valField, wireType, allMessages, depth);
|
|
994
|
+
}
|
|
995
|
+
else
|
|
996
|
+
{
|
|
997
|
+
sub.skipField(wireType);
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
if (key !== undefined)
|
|
1002
|
+
{
|
|
1003
|
+
result[field.name][key] = value;
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
/** @private */
|
|
1008
|
+
function _decodePackedRepeated(reader, field, result)
|
|
1009
|
+
{
|
|
1010
|
+
const sub = reader.readSubReader();
|
|
1011
|
+
|
|
1012
|
+
while (sub.remaining > 0)
|
|
1013
|
+
{
|
|
1014
|
+
const val = _readScalar(sub, field.type);
|
|
1015
|
+
result[field.name].push(val);
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
// -- Scalar Helpers ----------------------------------------
|
|
1020
|
+
|
|
1021
|
+
/** @private */
|
|
1022
|
+
function _writeScalar(writer, type, value)
|
|
1023
|
+
{
|
|
1024
|
+
switch (type)
|
|
1025
|
+
{
|
|
1026
|
+
case 'int32':
|
|
1027
|
+
case 'int64':
|
|
1028
|
+
case 'uint32':
|
|
1029
|
+
case 'uint64':
|
|
1030
|
+
case 'enum':
|
|
1031
|
+
writer.writeVarint(Number(value));
|
|
1032
|
+
break;
|
|
1033
|
+
case 'sint32':
|
|
1034
|
+
case 'sint64':
|
|
1035
|
+
writer.writeSVarint(Number(value));
|
|
1036
|
+
break;
|
|
1037
|
+
case 'bool':
|
|
1038
|
+
writer.writeBool(!!value);
|
|
1039
|
+
break;
|
|
1040
|
+
case 'fixed32':
|
|
1041
|
+
writer.writeFixed32(Number(value));
|
|
1042
|
+
break;
|
|
1043
|
+
case 'sfixed32':
|
|
1044
|
+
writer.writeSFixed32(Number(value));
|
|
1045
|
+
break;
|
|
1046
|
+
case 'fixed64':
|
|
1047
|
+
writer.writeFixed64(value);
|
|
1048
|
+
break;
|
|
1049
|
+
case 'sfixed64':
|
|
1050
|
+
writer.writeSFixed64(value);
|
|
1051
|
+
break;
|
|
1052
|
+
case 'float':
|
|
1053
|
+
writer.writeFloat(Number(value));
|
|
1054
|
+
break;
|
|
1055
|
+
case 'double':
|
|
1056
|
+
writer.writeDouble(Number(value));
|
|
1057
|
+
break;
|
|
1058
|
+
case 'string':
|
|
1059
|
+
writer.writeString(String(value));
|
|
1060
|
+
break;
|
|
1061
|
+
case 'bytes':
|
|
1062
|
+
writer.writeBytes(Buffer.isBuffer(value) ? value : Buffer.from(value));
|
|
1063
|
+
break;
|
|
1064
|
+
default:
|
|
1065
|
+
throw new Error(`Unknown scalar type: ${type}`);
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
/** @private */
|
|
1070
|
+
function _readScalar(reader, type, wireType)
|
|
1071
|
+
{
|
|
1072
|
+
switch (type)
|
|
1073
|
+
{
|
|
1074
|
+
case 'int32':
|
|
1075
|
+
{
|
|
1076
|
+
const v = reader.readVarint();
|
|
1077
|
+
return v > 0x7FFFFFFF ? v - 0x100000000 : v;
|
|
1078
|
+
}
|
|
1079
|
+
case 'int64':
|
|
1080
|
+
return reader.readVarint();
|
|
1081
|
+
case 'uint32':
|
|
1082
|
+
case 'uint64':
|
|
1083
|
+
return reader.readVarint();
|
|
1084
|
+
case 'sint32':
|
|
1085
|
+
case 'sint64':
|
|
1086
|
+
return reader.readSVarint();
|
|
1087
|
+
case 'bool':
|
|
1088
|
+
return reader.readBool();
|
|
1089
|
+
case 'fixed32':
|
|
1090
|
+
return reader.readFixed32();
|
|
1091
|
+
case 'sfixed32':
|
|
1092
|
+
return reader.readSFixed32();
|
|
1093
|
+
case 'fixed64':
|
|
1094
|
+
return reader.readFixed64();
|
|
1095
|
+
case 'sfixed64':
|
|
1096
|
+
return reader.readSFixed64();
|
|
1097
|
+
case 'float':
|
|
1098
|
+
return reader.readFloat();
|
|
1099
|
+
case 'double':
|
|
1100
|
+
return reader.readDouble();
|
|
1101
|
+
case 'string':
|
|
1102
|
+
return reader.readString();
|
|
1103
|
+
case 'bytes':
|
|
1104
|
+
return reader.readBytes();
|
|
1105
|
+
case 'enum':
|
|
1106
|
+
return reader.readVarint();
|
|
1107
|
+
default:
|
|
1108
|
+
throw new Error(`Unknown scalar type: ${type}`);
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
/** @private */
|
|
1113
|
+
function _isDefaultValue(type, value)
|
|
1114
|
+
{
|
|
1115
|
+
switch (type)
|
|
1116
|
+
{
|
|
1117
|
+
case 'string': return value === '';
|
|
1118
|
+
case 'bytes': return Buffer.isBuffer(value) && value.length === 0;
|
|
1119
|
+
case 'bool': return value === false;
|
|
1120
|
+
case 'float':
|
|
1121
|
+
case 'double':
|
|
1122
|
+
case 'int32':
|
|
1123
|
+
case 'int64':
|
|
1124
|
+
case 'uint32':
|
|
1125
|
+
case 'uint64':
|
|
1126
|
+
case 'sint32':
|
|
1127
|
+
case 'sint64':
|
|
1128
|
+
case 'fixed32':
|
|
1129
|
+
case 'fixed64':
|
|
1130
|
+
case 'sfixed32':
|
|
1131
|
+
case 'sfixed64':
|
|
1132
|
+
return value === 0;
|
|
1133
|
+
default:
|
|
1134
|
+
return false;
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
/** @private */
|
|
1139
|
+
function _defaultObject(messageDesc)
|
|
1140
|
+
{
|
|
1141
|
+
const obj = {};
|
|
1142
|
+
for (const field of messageDesc.fields)
|
|
1143
|
+
{
|
|
1144
|
+
if (field.map)
|
|
1145
|
+
{
|
|
1146
|
+
obj[field.name] = {};
|
|
1147
|
+
}
|
|
1148
|
+
else if (field.repeated)
|
|
1149
|
+
{
|
|
1150
|
+
obj[field.name] = [];
|
|
1151
|
+
}
|
|
1152
|
+
else
|
|
1153
|
+
{
|
|
1154
|
+
obj[field.name] = _defaultScalar(field);
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
return obj;
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
/** @private */
|
|
1161
|
+
function _defaultScalar(field)
|
|
1162
|
+
{
|
|
1163
|
+
if (field.enumDef) return 0;
|
|
1164
|
+
switch (field.type)
|
|
1165
|
+
{
|
|
1166
|
+
case 'string': return '';
|
|
1167
|
+
case 'bytes': return Buffer.alloc(0);
|
|
1168
|
+
case 'bool': return false;
|
|
1169
|
+
case 'float':
|
|
1170
|
+
case 'double':
|
|
1171
|
+
case 'int32':
|
|
1172
|
+
case 'int64':
|
|
1173
|
+
case 'uint32':
|
|
1174
|
+
case 'uint64':
|
|
1175
|
+
case 'sint32':
|
|
1176
|
+
case 'sint64':
|
|
1177
|
+
case 'fixed32':
|
|
1178
|
+
case 'fixed64':
|
|
1179
|
+
case 'sfixed32':
|
|
1180
|
+
case 'sfixed64':
|
|
1181
|
+
return 0;
|
|
1182
|
+
default:
|
|
1183
|
+
return null; // nested message default
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
/**
|
|
1188
|
+
* Check if a protobuf type can be packed (numeric/bool scalars only).
|
|
1189
|
+
*
|
|
1190
|
+
* @param {string} type - Protobuf type name.
|
|
1191
|
+
* @returns {boolean}
|
|
1192
|
+
*/
|
|
1193
|
+
function isPackable(type)
|
|
1194
|
+
{
|
|
1195
|
+
return type !== 'string' && type !== 'bytes' && TYPE_INFO[type] !== undefined;
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
/** @private */
|
|
1199
|
+
function _buildReverseEnum(enumDef)
|
|
1200
|
+
{
|
|
1201
|
+
const rev = {};
|
|
1202
|
+
for (const [name, val] of Object.entries(enumDef.values))
|
|
1203
|
+
{
|
|
1204
|
+
rev[val] = name;
|
|
1205
|
+
}
|
|
1206
|
+
enumDef._reverse = rev;
|
|
1207
|
+
return rev;
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
module.exports = {
|
|
1211
|
+
Writer,
|
|
1212
|
+
Reader,
|
|
1213
|
+
WIRE_TYPE,
|
|
1214
|
+
TYPE_INFO,
|
|
1215
|
+
MAX_VARINT_SIZE,
|
|
1216
|
+
MAX_RECURSION_DEPTH,
|
|
1217
|
+
DEFAULT_MAX_MESSAGE_SIZE,
|
|
1218
|
+
encode,
|
|
1219
|
+
decode,
|
|
1220
|
+
isPackable,
|
|
1221
|
+
};
|