protobufjs 8.1.6-experimental → 8.2.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.
Files changed (76) hide show
  1. package/README.md +219 -565
  2. package/dist/light/protobuf.js +1986 -1483
  3. package/dist/light/protobuf.js.map +1 -1
  4. package/dist/light/protobuf.min.js +3 -3
  5. package/dist/light/protobuf.min.js.map +1 -1
  6. package/dist/minimal/protobuf.js +1122 -861
  7. package/dist/minimal/protobuf.js.map +1 -1
  8. package/dist/minimal/protobuf.min.js +3 -3
  9. package/dist/minimal/protobuf.min.js.map +1 -1
  10. package/dist/protobuf.js +2089 -1513
  11. package/dist/protobuf.js.map +1 -1
  12. package/dist/protobuf.min.js +3 -3
  13. package/dist/protobuf.min.js.map +1 -1
  14. package/ext/README.md +81 -0
  15. package/ext/descriptor/README.md +3 -70
  16. package/ext/descriptor/index.d.ts +1 -191
  17. package/ext/descriptor/index.js +1 -1161
  18. package/ext/descriptor.d.ts +309 -0
  19. package/ext/descriptor.js +1236 -0
  20. package/ext/textformat.d.ts +30 -0
  21. package/ext/textformat.js +1249 -0
  22. package/google/protobuf/compiler/plugin.json +126 -0
  23. package/google/protobuf/compiler/plugin.proto +47 -0
  24. package/google/protobuf/descriptor.json +2 -2
  25. package/google/protobuf/descriptor.proto +2 -1
  26. package/index.d.ts +590 -476
  27. package/package.json +23 -38
  28. package/src/converter.js +60 -24
  29. package/src/decoder.js +122 -49
  30. package/src/encoder.js +10 -2
  31. package/src/enum.js +4 -1
  32. package/src/field.js +10 -7
  33. package/src/mapfield.js +1 -0
  34. package/src/message.js +7 -6
  35. package/src/method.js +4 -3
  36. package/src/namespace.js +23 -12
  37. package/src/object.js +24 -19
  38. package/src/oneof.js +2 -0
  39. package/src/parse.js +114 -46
  40. package/src/reader.js +145 -30
  41. package/src/reader_buffer.js +24 -3
  42. package/src/root.js +7 -4
  43. package/src/service.js +12 -6
  44. package/src/tokenize.js +6 -1
  45. package/src/type.js +48 -25
  46. package/src/types.js +1 -1
  47. package/src/util/aspromise.d.ts +13 -0
  48. package/src/util/aspromise.js +52 -0
  49. package/src/util/base64.d.ts +32 -0
  50. package/src/util/base64.js +146 -0
  51. package/src/util/codegen.d.ts +31 -0
  52. package/src/util/codegen.js +113 -0
  53. package/src/util/eventemitter.d.ts +45 -0
  54. package/src/util/eventemitter.js +84 -0
  55. package/src/util/fetch.d.ts +56 -0
  56. package/src/util/fetch.js +112 -0
  57. package/src/util/float.d.ts +83 -0
  58. package/src/util/float.js +335 -0
  59. package/src/util/fs.js +11 -0
  60. package/src/util/inquire.d.ts +10 -0
  61. package/src/util/inquire.js +38 -0
  62. package/src/util/minimal.js +67 -12
  63. package/src/util/path.d.ts +22 -0
  64. package/src/util/path.js +72 -0
  65. package/src/util/patterns.js +8 -0
  66. package/src/util/pool.d.ts +32 -0
  67. package/src/util/pool.js +48 -0
  68. package/src/util/utf8.d.ts +24 -0
  69. package/src/util/utf8.js +104 -0
  70. package/src/util.js +30 -13
  71. package/src/verifier.js +7 -4
  72. package/src/wrappers.js +4 -3
  73. package/src/writer.js +27 -4
  74. package/src/writer_buffer.js +12 -0
  75. package/tsconfig.json +2 -2
  76. package/ext/descriptor/test.js +0 -54
package/src/parse.js CHANGED
@@ -23,9 +23,9 @@ var base10Re = /^[1-9][0-9]*$/,
23
23
  base16NegRe = /^-?0[x][0-9a-fA-F]+$/,
24
24
  base8Re = /^0[0-7]+$/,
25
25
  base8NegRe = /^-?0[0-7]+$/,
26
- numberRe = /^(?![eE])[0-9]*(?:\.[0-9]*)?(?:[eE][+-]?[0-9]+)?$/,
26
+ numberRe = util.patterns.numberRe,
27
27
  nameRe = /^[a-zA-Z_][a-zA-Z_0-9]*$/,
28
- typeRefRe = /^(?:\.?[a-zA-Z_][a-zA-Z_0-9]*)(?:\.[a-zA-Z_][a-zA-Z_0-9]*)*$/;
28
+ typeRefRe = util.patterns.typeRefRe;
29
29
 
30
30
  /**
31
31
  * Result object returned from {@link parse}.
@@ -261,6 +261,16 @@ function parse(source, root, options) {
261
261
  var token = peek();
262
262
  var whichImports;
263
263
  switch (token) {
264
+ case "option":
265
+ if (edition < "2024") {
266
+ throw illegal("option");
267
+ }
268
+ // Import options are only used for resolving options, which we don't
269
+ // do. We can just throw them out.
270
+ next();
271
+ readString();
272
+ skip(";");
273
+ return;
264
274
  case "weak":
265
275
  whichImports = weakImports || (weakImports = []);
266
276
  next();
@@ -291,7 +301,7 @@ function parse(source, root, options) {
291
301
  function parseEdition() {
292
302
  skip("=");
293
303
  edition = readString();
294
- const supportedEditions = ["2023"];
304
+ const supportedEditions = ["2023", "2024"];
295
305
 
296
306
  /* istanbul ignore if */
297
307
  if (!supportedEditions.includes(edition))
@@ -301,7 +311,8 @@ function parse(source, root, options) {
301
311
  }
302
312
 
303
313
 
304
- function parseCommon(parent, token) {
314
+ function parseCommon(parent, token, depth) {
315
+ depth = util.checkDepth(depth);
305
316
  switch (token) {
306
317
 
307
318
  case "option":
@@ -310,19 +321,35 @@ function parse(source, root, options) {
310
321
  return true;
311
322
 
312
323
  case "message":
313
- parseType(parent, token);
324
+ parseType(parent, token, depth + 1);
314
325
  return true;
315
326
 
316
327
  case "enum":
317
328
  parseEnum(parent, token);
318
329
  return true;
319
330
 
331
+ case "export":
332
+ case "local":
333
+ if (edition < "2024") {
334
+ return false;
335
+ }
336
+ token = next();
337
+ if (token === "export" || token === "local") {
338
+ return false;
339
+ }
340
+ if (token !== "message" && token !== "enum") {
341
+ return false;
342
+ }
343
+ /* eslint-disable no-warning-comments */
344
+ // TODO: actually enforce visiblity modifiers like protoc does.
345
+ return parseCommon(parent, token, depth);
346
+
320
347
  case "service":
321
- parseService(parent, token);
348
+ parseService(parent, token, depth + 1);
322
349
  return true;
323
350
 
324
351
  case "extend":
325
- parseExtension(parent, token);
352
+ parseExtension(parent, token, depth);
326
353
  return true;
327
354
  }
328
355
  return false;
@@ -350,7 +377,8 @@ function parse(source, root, options) {
350
377
  }
351
378
  }
352
379
 
353
- function parseType(parent, token) {
380
+ function parseType(parent, token, depth) {
381
+ depth = util.checkDepth(depth);
354
382
 
355
383
  /* istanbul ignore if */
356
384
  if (!nameRe.test(token = next()))
@@ -358,11 +386,14 @@ function parse(source, root, options) {
358
386
 
359
387
  var type = new Type(token);
360
388
  ifBlock(type, function parseType_block(token) {
361
- if (parseCommon(type, token))
389
+ if (parseCommon(type, token, depth))
362
390
  return;
363
391
 
364
392
  switch (token) {
365
393
 
394
+ case ";":
395
+ break;
396
+
366
397
  case "map":
367
398
  parseMapField(type, token);
368
399
  break;
@@ -372,22 +403,22 @@ function parse(source, root, options) {
372
403
  throw illegal(token);
373
404
  /* eslint-disable no-fallthrough */
374
405
  case "repeated":
375
- parseField(type, token);
406
+ parseField(type, token, undefined, depth + 1);
376
407
  break;
377
408
 
378
409
  case "optional":
379
410
  /* istanbul ignore if */
380
411
  if (edition === "proto3") {
381
- parseField(type, "proto3_optional");
412
+ parseField(type, "proto3_optional", undefined, depth + 1);
382
413
  } else if (edition !== "proto2") {
383
414
  throw illegal(token);
384
415
  } else {
385
- parseField(type, "optional");
416
+ parseField(type, "optional", undefined, depth + 1);
386
417
  }
387
418
  break;
388
419
 
389
420
  case "oneof":
390
- parseOneOf(type, token);
421
+ parseOneOf(type, token, depth + 1);
391
422
  break;
392
423
 
393
424
  case "extensions":
@@ -405,7 +436,7 @@ function parse(source, root, options) {
405
436
  }
406
437
 
407
438
  push(token);
408
- parseField(type, "optional");
439
+ parseField(type, "optional", undefined, depth + 1);
409
440
  break;
410
441
  }
411
442
  });
@@ -415,10 +446,10 @@ function parse(source, root, options) {
415
446
  }
416
447
  }
417
448
 
418
- function parseField(parent, rule, extend) {
449
+ function parseField(parent, rule, extend, depth) {
419
450
  var type = next();
420
451
  if (type === "group") {
421
- parseGroup(parent, rule);
452
+ parseGroup(parent, rule, extend, depth);
422
453
  return;
423
454
  }
424
455
  // Type names can consume multiple tokens, in multiple variants:
@@ -475,7 +506,8 @@ function parse(source, root, options) {
475
506
  }
476
507
  }
477
508
 
478
- function parseGroup(parent, rule) {
509
+ function parseGroup(parent, rule, extend, depth) {
510
+ depth = util.checkDepth(depth);
479
511
  if (edition >= 2023) {
480
512
  throw illegal("group");
481
513
  }
@@ -492,31 +524,34 @@ function parse(source, root, options) {
492
524
  var id = parseId(next());
493
525
  var type = new Type(name);
494
526
  type.group = true;
495
- var field = new Field(fieldName, id, name, rule);
527
+ var field = new Field(fieldName, id, name, rule, extend);
496
528
  field.filename = parse.filename;
497
529
  ifBlock(type, function parseGroup_block(token) {
498
530
  switch (token) {
499
531
 
532
+ case ";":
533
+ break;
534
+
500
535
  case "option":
501
536
  parseOption(type, token);
502
537
  skip(";");
503
538
  break;
504
539
  case "required":
505
540
  case "repeated":
506
- parseField(type, token);
541
+ parseField(type, token, undefined, depth + 1);
507
542
  break;
508
543
 
509
544
  case "optional":
510
545
  /* istanbul ignore if */
511
546
  if (edition === "proto3") {
512
- parseField(type, "proto3_optional");
547
+ parseField(type, "proto3_optional", undefined, depth + 1);
513
548
  } else {
514
- parseField(type, "optional");
549
+ parseField(type, "optional", undefined, depth + 1);
515
550
  }
516
551
  break;
517
552
 
518
553
  case "message":
519
- parseType(type, token);
554
+ parseType(type, token, depth + 1);
520
555
  break;
521
556
 
522
557
  case "enum":
@@ -527,6 +562,24 @@ function parse(source, root, options) {
527
562
  readRanges(type.reserved || (type.reserved = []), true);
528
563
  break;
529
564
 
565
+ case "export":
566
+ case "local":
567
+ if (edition < "2024") {
568
+ throw illegal(token);
569
+ }
570
+ token = next();
571
+ switch (token) {
572
+ case "message":
573
+ parseType(type, token, depth + 1);
574
+ break;
575
+ case "enum":
576
+ parseType(type, token, depth + 1);
577
+ break;
578
+ default:
579
+ throw illegal(token);
580
+ }
581
+ break;
582
+
530
583
  /* istanbul ignore next */
531
584
  default:
532
585
  throw illegal(token); // there are no groups with proto3 semantics
@@ -534,6 +587,10 @@ function parse(source, root, options) {
534
587
  });
535
588
  parent.add(type)
536
589
  .add(field);
590
+ if (parent === ptr) {
591
+ topLevelObjects.push(type);
592
+ topLevelObjects.push(field);
593
+ }
537
594
  }
538
595
 
539
596
  function parseMapField(parent) {
@@ -575,7 +632,7 @@ function parse(source, root, options) {
575
632
  parent.add(field);
576
633
  }
577
634
 
578
- function parseOneOf(parent, token) {
635
+ function parseOneOf(parent, token, depth) {
579
636
 
580
637
  /* istanbul ignore if */
581
638
  if (!nameRe.test(token = next()))
@@ -588,7 +645,7 @@ function parse(source, root, options) {
588
645
  skip(";");
589
646
  } else {
590
647
  push(token);
591
- parseField(oneof, "optional");
648
+ parseField(oneof, "optional", undefined, depth);
592
649
  }
593
650
  });
594
651
  parent.add(oneof);
@@ -603,6 +660,9 @@ function parse(source, root, options) {
603
660
  var enm = new Enum(token);
604
661
  ifBlock(enm, function parseEnum_block(token) {
605
662
  switch(token) {
663
+ case ";":
664
+ break;
665
+
606
666
  case "option":
607
667
  parseOption(enm, token);
608
668
  skip(";");
@@ -693,7 +753,8 @@ function parse(source, root, options) {
693
753
  setParsedOption(parent, option, optionValue, propName);
694
754
  }
695
755
 
696
- function parseOptionValue(parent, name) {
756
+ function parseOptionValue(parent, name, depth) {
757
+ depth = util.checkDepth(depth);
697
758
  // { a: "foo" b { c: "bar" } }
698
759
  if (skip("{", true)) {
699
760
  var objectResult = {};
@@ -716,18 +777,20 @@ function parse(source, root, options) {
716
777
  // option (my_option) = {
717
778
  // repeated_value: [ "foo", "bar" ]
718
779
  // };
719
- value = parseOptionValue(parent, name + "." + token);
780
+ value = parseOptionValue(parent, name + "." + token, depth + 1);
720
781
  } else if (peek() === "[") {
721
782
  value = [];
722
783
  var lastValue;
723
784
  if (skip("[", true)) {
724
- do {
725
- lastValue = readValue(true);
726
- value.push(lastValue);
727
- } while (skip(",", true));
728
- skip("]");
729
- if (typeof lastValue !== "undefined") {
730
- setOption(parent, name + "." + token, lastValue);
785
+ if (!skip("]", true)) {
786
+ do {
787
+ lastValue = readValue(true);
788
+ value.push(lastValue);
789
+ } while (skip(",", true));
790
+ skip("]");
791
+ if (typeof lastValue !== "undefined") {
792
+ setOption(parent, name + "." + token, lastValue);
793
+ }
731
794
  }
732
795
  }
733
796
  } else {
@@ -740,7 +803,8 @@ function parse(source, root, options) {
740
803
  if (prevValue)
741
804
  value = [].concat(prevValue).concat(value);
742
805
 
743
- objectResult[propName] = value;
806
+ if (propName !== "__proto__")
807
+ objectResult[propName] = value;
744
808
 
745
809
  // Semicolons and commas can be optional
746
810
  skip(",", true);
@@ -780,7 +844,8 @@ function parse(source, root, options) {
780
844
  return parent;
781
845
  }
782
846
 
783
- function parseService(parent, token) {
847
+ function parseService(parent, token, depth) {
848
+ depth = util.checkDepth(depth);
784
849
 
785
850
  /* istanbul ignore if */
786
851
  if (!nameRe.test(token = next()))
@@ -788,11 +853,13 @@ function parse(source, root, options) {
788
853
 
789
854
  var service = new Service(token);
790
855
  ifBlock(service, function parseService_block(token) {
791
- if (parseCommon(service, token)) {
856
+ if (parseCommon(service, token, depth)) {
792
857
  return;
793
858
  }
794
859
 
795
860
  /* istanbul ignore else */
861
+ if (token === ";")
862
+ return;
796
863
  if (token === "rpc")
797
864
  parseMethod(service, token);
798
865
  else
@@ -844,6 +911,8 @@ function parse(source, root, options) {
844
911
  ifBlock(method, function parseMethod_block(token) {
845
912
 
846
913
  /* istanbul ignore else */
914
+ if (token === ";")
915
+ return;
847
916
  if (token === "option") {
848
917
  parseOption(method, token);
849
918
  skip(";");
@@ -854,7 +923,7 @@ function parse(source, root, options) {
854
923
  parent.add(method);
855
924
  }
856
925
 
857
- function parseExtension(parent, token) {
926
+ function parseExtension(parent, token, depth) {
858
927
 
859
928
  /* istanbul ignore if */
860
929
  if (!typeRefRe.test(token = next()))
@@ -866,15 +935,15 @@ function parse(source, root, options) {
866
935
 
867
936
  case "required":
868
937
  case "repeated":
869
- parseField(parent, token, reference);
938
+ parseField(parent, token, reference, depth + 1);
870
939
  break;
871
940
 
872
941
  case "optional":
873
942
  /* istanbul ignore if */
874
943
  if (edition === "proto3") {
875
- parseField(parent, "proto3_optional", reference);
944
+ parseField(parent, "proto3_optional", reference, depth + 1);
876
945
  } else {
877
- parseField(parent, "optional", reference);
946
+ parseField(parent, "optional", reference, depth + 1);
878
947
  }
879
948
  break;
880
949
 
@@ -883,7 +952,7 @@ function parse(source, root, options) {
883
952
  if (edition === "proto2" || !typeRefRe.test(token))
884
953
  throw illegal(token);
885
954
  push(token);
886
- parseField(parent, "optional", reference);
955
+ parseField(parent, "optional", reference, depth + 1);
887
956
  break;
888
957
  }
889
958
  });
@@ -893,6 +962,9 @@ function parse(source, root, options) {
893
962
  while ((token = next()) !== null) {
894
963
  switch (token) {
895
964
 
965
+ case ";":
966
+ break;
967
+
896
968
  case "package":
897
969
 
898
970
  /* istanbul ignore if */
@@ -904,10 +976,6 @@ function parse(source, root, options) {
904
976
 
905
977
  case "import":
906
978
 
907
- /* istanbul ignore if */
908
- if (!head)
909
- throw illegal(token);
910
-
911
979
  parseImport();
912
980
  break;
913
981
 
@@ -935,7 +1003,7 @@ function parse(source, root, options) {
935
1003
  default:
936
1004
 
937
1005
  /* istanbul ignore else */
938
- if (parseCommon(ptr, token)) {
1006
+ if (parseCommon(ptr, token, 0)) {
939
1007
  head = false;
940
1008
  continue;
941
1009
  }
package/src/reader.js CHANGED
@@ -78,28 +78,108 @@ Reader.create = create();
78
78
 
79
79
  Reader.prototype._slice = util.Array.prototype.subarray || /* istanbul ignore next */ util.Array.prototype.slice;
80
80
 
81
+ /**
82
+ * Returns raw bytes from the backing buffer without advancing the reader.
83
+ * @param {number} start Start offset
84
+ * @param {number} end End offset
85
+ * @returns {Uint8Array} Raw bytes
86
+ */
87
+ Reader.prototype.raw = function read_raw(start, end) {
88
+ if (Array.isArray(this.buf)) // plain array
89
+ return this.buf.slice(start, end);
90
+
91
+ if (start === end) // fix for IE 10/Win8 and others' subarray returning array of size 1
92
+ return new this.buf.constructor(0);
93
+ return this._slice.call(this.buf, start, end);
94
+ };
95
+
81
96
  /**
82
97
  * Reads a varint as an unsigned 32 bit value.
83
98
  * @function
84
99
  * @returns {number} Value read
85
100
  */
86
- Reader.prototype.uint32 = (function read_uint32_setup() {
87
- var value = 4294967295; // optimizer type-hint, tends to deopt otherwise (?!)
88
- return function read_uint32() {
89
- value = ( this.buf[this.pos] & 127 ) >>> 0; if (this.buf[this.pos++] < 128) return value;
90
- value = (value | (this.buf[this.pos] & 127) << 7) >>> 0; if (this.buf[this.pos++] < 128) return value;
91
- value = (value | (this.buf[this.pos] & 127) << 14) >>> 0; if (this.buf[this.pos++] < 128) return value;
92
- value = (value | (this.buf[this.pos] & 127) << 21) >>> 0; if (this.buf[this.pos++] < 128) return value;
93
- value = (value | (this.buf[this.pos] & 15) << 28) >>> 0; if (this.buf[this.pos++] < 128) return value;
101
+ Reader.prototype.uint32 = function read_uint32() {
102
+ var buf = this.buf,
103
+ pos = this.pos,
104
+ value = (buf[pos] & 127) >>> 0;
105
+ if (buf[pos++] < 128) {
106
+ this.pos = pos;
107
+ return value;
108
+ }
109
+ value = (value | (buf[pos] & 127) << 7) >>> 0;
110
+ if (buf[pos++] < 128) {
111
+ this.pos = pos;
112
+ return value;
113
+ }
114
+ value = (value | (buf[pos] & 127) << 14) >>> 0;
115
+ if (buf[pos++] < 128) {
116
+ this.pos = pos;
117
+ return value;
118
+ }
119
+ value = (value | (buf[pos] & 127) << 21) >>> 0;
120
+ if (buf[pos++] < 128) {
121
+ this.pos = pos;
122
+ return value;
123
+ }
124
+ value = (value | (buf[pos] & 15) << 28) >>> 0;
125
+ if (buf[pos++] < 128) {
126
+ this.pos = pos;
127
+ return value;
128
+ }
94
129
 
130
+ for (var i = 0; i < 5; ++i) {
95
131
  /* istanbul ignore if */
96
- if ((this.pos += 5) > this.len) {
97
- this.pos = this.len;
98
- throw indexOutOfRange(this, 10);
132
+ if (pos >= this.len) {
133
+ this.pos = pos;
134
+ throw indexOutOfRange(this);
135
+ }
136
+ if (buf[pos++] < 128) {
137
+ this.pos = pos;
138
+ return value;
99
139
  }
140
+ }
141
+ /* istanbul ignore next */
142
+ this.pos = pos;
143
+ throw Error("invalid varint encoding");
144
+ };
145
+
146
+ /**
147
+ * Reads a field tag.
148
+ * @function
149
+ * @returns {number} Tag read
150
+ */
151
+ Reader.prototype.tag = function read_tag() {
152
+ var buf = this.buf,
153
+ pos = this.pos,
154
+ value = (buf[pos] & 127) >>> 0;
155
+ if (buf[pos++] < 128) {
156
+ this.pos = pos;
100
157
  return value;
101
- };
102
- })();
158
+ }
159
+ value = (value | (buf[pos] & 127) << 7) >>> 0;
160
+ if (buf[pos++] < 128) {
161
+ this.pos = pos;
162
+ return value;
163
+ }
164
+ value = (value | (buf[pos] & 127) << 14) >>> 0;
165
+ if (buf[pos++] < 128) {
166
+ this.pos = pos;
167
+ return value;
168
+ }
169
+ value = (value | (buf[pos] & 127) << 21) >>> 0;
170
+ if (buf[pos++] < 128) {
171
+ this.pos = pos;
172
+ return value;
173
+ }
174
+ value = (value | (buf[pos] & 15) << 28) >>> 0;
175
+ if (buf[pos] < 128 && (buf[pos] & 112) === 0) {
176
+ this.pos = pos + 1;
177
+ return value;
178
+ }
179
+
180
+ this.pos = pos + 1;
181
+ throw Error("invalid tag encoding");
182
+ };
103
183
 
104
184
  /**
105
185
  * Reads a varint as a signed 32 bit value.
@@ -201,7 +281,20 @@ function readLongVarint() {
201
281
  * @returns {boolean} Value read
202
282
  */
203
283
  Reader.prototype.bool = function read_bool() {
204
- return this.uint32() !== 0;
284
+ var value = false,
285
+ b;
286
+ for (var i = 0; i < 10; ++i) {
287
+ /* istanbul ignore if */
288
+ if (this.pos >= this.len)
289
+ throw indexOutOfRange(this);
290
+ b = this.buf[this.pos++];
291
+ if (b & 127)
292
+ value = true;
293
+ if (b < 128)
294
+ return value;
295
+ }
296
+ /* istanbul ignore next */
297
+ throw Error("invalid varint encoding");
205
298
  };
206
299
 
207
300
  function readFixed32_end(buf, end) { // note that this uses `end`, not `pos`
@@ -309,17 +402,8 @@ Reader.prototype.bytes = function read_bytes() {
309
402
  if (end > this.len)
310
403
  throw indexOutOfRange(this, length);
311
404
 
312
- this.pos += length;
313
- if (Array.isArray(this.buf)) // plain array
314
- return this.buf.slice(start, end);
315
-
316
- if (start === end) { // fix for IE 10/Win8 and others' subarray returning array of size 1
317
- var nativeBuffer = util.Buffer;
318
- return nativeBuffer
319
- ? nativeBuffer.alloc(0)
320
- : new this.buf.constructor(0);
321
- }
322
- return this._slice.call(this.buf, start, end);
405
+ this.pos = end;
406
+ return this.raw(start, end);
323
407
  };
324
408
 
325
409
  /**
@@ -327,8 +411,16 @@ Reader.prototype.bytes = function read_bytes() {
327
411
  * @returns {string} Value read
328
412
  */
329
413
  Reader.prototype.string = function read_string() {
330
- var bytes = this.bytes();
331
- return utf8.read(bytes, 0, bytes.length);
414
+ var length = this.uint32(),
415
+ start = this.pos,
416
+ end = this.pos + length;
417
+
418
+ /* istanbul ignore if */
419
+ if (end > this.len)
420
+ throw indexOutOfRange(this, length);
421
+
422
+ this.pos = end;
423
+ return utf8.read(this.buf, start, end);
332
424
  };
333
425
 
334
426
  /**
@@ -352,12 +444,25 @@ Reader.prototype.skip = function skip(length) {
352
444
  return this;
353
445
  };
354
446
 
447
+ /**
448
+ * Recursion limit.
449
+ * @type {number}
450
+ */
451
+ Reader.recursionLimit = util.recursionLimit;
452
+
355
453
  /**
356
454
  * Skips the next element of the specified wire type.
357
455
  * @param {number} wireType Wire type received
456
+ * @param {number} [depth] Depth of recursion to control nested calls; 0 if omitted
457
+ * @param {number} [fieldNumber] Field number for validating group end tags
358
458
  * @returns {Reader} `this`
359
459
  */
360
- Reader.prototype.skipType = function(wireType) {
460
+ Reader.prototype.skipType = function(wireType, depth, fieldNumber) {
461
+ if (depth === undefined) depth = 0;
462
+ if (depth > Reader.recursionLimit)
463
+ throw Error("max depth exceeded");
464
+ if (fieldNumber === 0)
465
+ throw Error("illegal tag: field number 0");
361
466
  switch (wireType) {
362
467
  case 0:
363
468
  this.skip();
@@ -369,8 +474,18 @@ Reader.prototype.skipType = function(wireType) {
369
474
  this.skip(this.uint32());
370
475
  break;
371
476
  case 3:
372
- while ((wireType = this.uint32() & 7) !== 4) {
373
- this.skipType(wireType);
477
+ while (true) {
478
+ var tag = this.tag();
479
+ var nestedField = tag >>> 3;
480
+ wireType = tag & 7;
481
+ if (!nestedField)
482
+ throw Error("illegal tag: field number 0");
483
+ if (wireType === 4) {
484
+ if (fieldNumber !== undefined && nestedField !== fieldNumber)
485
+ throw Error("invalid end group tag");
486
+ break;
487
+ }
488
+ this.skipType(wireType, depth + 1, nestedField);
374
489
  }
375
490
  break;
376
491
  case 5:
@@ -30,15 +30,36 @@ BufferReader._configure = function () {
30
30
  BufferReader.prototype._slice = util.Buffer.prototype.slice;
31
31
  };
32
32
 
33
+ /**
34
+ * Returns raw bytes from the backing buffer without advancing the reader.
35
+ * @name BufferReader#raw
36
+ * @function
37
+ * @param {number} start Start offset
38
+ * @param {number} end End offset
39
+ * @returns {Buffer} Raw bytes
40
+ */
41
+ BufferReader.prototype.raw = function read_raw_buffer(start, end) {
42
+ if (start === end)
43
+ return util.Buffer.alloc(0);
44
+ return this._slice.call(this.buf, start, end);
45
+ };
33
46
 
34
47
  /**
35
48
  * @override
36
49
  */
37
50
  BufferReader.prototype.string = function read_string_buffer() {
38
- var len = this.uint32(); // modifies pos
51
+ var len = this.uint32(), // modifies pos
52
+ start = this.pos,
53
+ end = this.pos + len;
54
+
55
+ /* istanbul ignore if */
56
+ if (end > this.len)
57
+ throw RangeError("index out of range: " + this.pos + " + " + len + " > " + this.len);
58
+
59
+ this.pos = end;
39
60
  return this.buf.utf8Slice
40
- ? this.buf.utf8Slice(this.pos, this.pos = Math.min(this.pos + len, this.len))
41
- : this.buf.toString("utf-8", this.pos, this.pos = Math.min(this.pos + len, this.len));
61
+ ? this.buf.utf8Slice(start, end)
62
+ : this.buf.toString("utf-8", start, end);
42
63
  };
43
64
 
44
65
  /**