protobufjs 8.6.5 → 8.7.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/src/writer.js CHANGED
@@ -9,118 +9,52 @@ var LongBits = util.LongBits,
9
9
  base64 = util.base64,
10
10
  utf8 = util.utf8;
11
11
 
12
- /**
13
- * Constructs a new writer operation instance.
14
- * @classdesc Scheduled writer operation.
15
- * @constructor
16
- * @param {function(*, Uint8Array, number)} fn Function to call
17
- * @param {number} len Value byte length
18
- * @param {*} val Value to write
19
- * @ignore
20
- */
21
- function Op(fn, len, val) {
22
-
23
- /**
24
- * Function to call.
25
- * @type {function(Uint8Array, number, *)}
26
- */
27
- this.fn = fn;
28
-
29
- /**
30
- * Value byte length.
31
- * @type {number}
32
- */
33
- this.len = len;
34
-
35
- /**
36
- * Next operation.
37
- * @type {Writer.Op|undefined}
38
- */
39
- this.next = undefined;
40
-
41
- /**
42
- * Value to write.
43
- * @type {*}
44
- */
45
- this.val = val; // type varies
46
- }
47
-
48
- /* istanbul ignore next */
49
- function noop() {} // eslint-disable-line no-empty-function
50
-
51
- /**
52
- * Constructs a new writer state instance.
53
- * @classdesc Copied writer state.
54
- * @memberof Writer
55
- * @constructor
56
- * @param {Writer} writer Writer to copy state from
57
- * @ignore
58
- */
59
- function State(writer) {
60
-
61
- /**
62
- * Current head.
63
- * @type {Writer.Op}
64
- */
65
- this.head = writer.head;
66
-
67
- /**
68
- * Current tail.
69
- * @type {Writer.Op}
70
- */
71
- this.tail = writer.tail;
72
-
73
- /**
74
- * Current buffer length.
75
- * @type {number}
76
- */
77
- this.len = writer.len;
78
-
79
- /**
80
- * Next state.
81
- * @type {State|null}
82
- */
83
- this.next = writer.states;
84
- }
85
-
86
12
  /**
87
13
  * Constructs a new writer instance.
88
- * @classdesc Wire format writer using `Uint8Array` if available, otherwise `Array`.
14
+ * @classdesc Wire format writer using `Uint8Array`.
89
15
  * @constructor
90
16
  */
91
17
  function Writer() {
92
18
 
93
19
  /**
94
- * Current length.
20
+ * Write cursor into {@link Writer#buf}.
95
21
  * @type {number}
96
22
  */
97
- this.len = 0;
23
+ this.pos = 0;
98
24
 
99
25
  /**
100
- * Operations head.
101
- * @type {Object}
26
+ * Backing buffer.
27
+ * @type {Uint8Array}
102
28
  */
103
- this.head = new Op(noop, 0, 0);
29
+ this.buf = this.constructor.alloc(64);
104
30
 
105
31
  /**
106
- * Operations tail
107
- * @type {Object}
32
+ * Cached DataView over {@link Writer#buf}.
33
+ * @type {DataView|null}
108
34
  */
109
- this.tail = this.head;
35
+ this.view = null;
110
36
 
111
37
  /**
112
- * Linked forked states.
113
- * @type {Object|null}
38
+ * Stack of forked length-prefix positions.
39
+ * @type {Array<number>|null}
114
40
  */
115
41
  this.states = null;
116
-
117
- // When a value is written, the writer calculates its byte length and puts it into a linked
118
- // list of operations to perform when finish() is called. This both allows us to allocate
119
- // buffers of the exact required size and reduces the amount of work we have to do compared
120
- // to first calculating over objects and then encoding over objects. In our case, the encoding
121
- // part is just a linked list walk calling operations with already prepared values.
122
42
  }
123
43
 
44
+ /**
45
+ * Current write position.
46
+ * @name Writer#len
47
+ * @type {number}
48
+ * @deprecated Use {@link Writer#pos} instead.
49
+ */
50
+ Object.defineProperty(Writer.prototype, "len", {
51
+ configurable: true,
52
+ enumerable: true,
53
+ get: function get_len() {
54
+ return this.pos;
55
+ }
56
+ });
57
+
124
58
  var create = function create() {
125
59
  return util.Buffer
126
60
  ? function create_buffer_setup() {
@@ -147,32 +81,45 @@ Writer.create = create();
147
81
  * @returns {Uint8Array} Buffer
148
82
  */
149
83
  Writer.alloc = function alloc(size) {
150
- return new util.Array(size);
84
+ return new Uint8Array(size);
151
85
  };
152
86
 
153
87
  // Use Uint8Array buffer pool in the browser, just like node does with buffers
154
- /* istanbul ignore else */
155
- if (util.Array !== Array)
156
- Writer.alloc = util.pool(Writer.alloc, util.Array.prototype.subarray);
88
+ Writer.alloc = util.pool(Writer.alloc, Uint8Array.prototype.subarray);
157
89
 
158
90
  /**
159
- * Pushes a new operation to the queue.
160
- * @param {function(Uint8Array, number, *)} fn Function to call
161
- * @param {number} len Value byte length
162
- * @param {number} val Value to write
163
- * @returns {Writer} `this`
91
+ * Calculates the number of bytes a value occupies as a varint.
92
+ * @param {number} value Value to size (unsigned)
93
+ * @returns {number} Byte length (1..5)
94
+ * @ignore
95
+ */
96
+ function sizeVarint32(value) {
97
+ return value < 128 ? 1
98
+ : value < 16384 ? 2
99
+ : value < 2097152 ? 3
100
+ : value < 268435456 ? 4
101
+ : 5;
102
+ }
103
+
104
+ /**
105
+ * Ensures that at least `n` more bytes fit into the backing buffer, doubling it if not.
106
+ * @param {number} n Number of additional bytes required
107
+ * @returns {undefined}
164
108
  * @private
165
109
  */
166
- Writer.prototype._push = function push(fn, len, val) {
167
- this.tail = this.tail.next = new Op(fn, len, val);
168
- this.len += len;
169
- return this;
110
+ Writer.prototype._reserve = function _reserve(n) {
111
+ var need = this.pos + n;
112
+ if (need > this.buf.length) {
113
+ var size = this.buf.length << 1;
114
+ if (size < need)
115
+ size = need;
116
+ var buf = this.constructor.alloc(size);
117
+ buf.set(this.buf.subarray(0, this.pos), 0);
118
+ this.buf = buf;
119
+ this.view = null; // invalidate
120
+ }
170
121
  };
171
122
 
172
- function writeByte(val, buf, pos) {
173
- buf[pos] = val & 255;
174
- }
175
-
176
123
  function writeStringAscii(val, buf, pos) {
177
124
  for (var i = 0; i < val.length;)
178
125
  buf[pos++] = val.charCodeAt(i++);
@@ -184,42 +131,19 @@ function writeVarint32(val, buf, pos) {
184
131
  val >>>= 7;
185
132
  }
186
133
  buf[pos] = val;
134
+ return pos + 1;
187
135
  }
188
136
 
189
- /**
190
- * Constructs a new varint writer operation instance.
191
- * @classdesc Scheduled varint writer operation.
192
- * @extends Op
193
- * @constructor
194
- * @param {number} len Value byte length
195
- * @param {number} val Value to write
196
- * @ignore
197
- */
198
- function VarintOp(len, val) {
199
- this.len = len;
200
- this.next = undefined;
201
- this.val = val;
202
- }
203
-
204
- VarintOp.prototype = Object.create(Op.prototype);
205
- VarintOp.prototype.fn = writeVarint32;
206
-
207
137
  /**
208
138
  * Writes an unsigned 32 bit value as a varint.
209
139
  * @param {number} value Value to write
210
140
  * @returns {Writer} `this`
211
141
  */
212
142
  Writer.prototype.uint32 = function write_uint32(value) {
213
- // here, the call to this.push has been inlined and a varint specific Op subclass is used.
214
- // uint32 is by far the most frequently used operation and benefits significantly from this.
215
- this.len += (this.tail = this.tail.next = new VarintOp(
216
- (value = value >>> 0)
217
- < 128 ? 1
218
- : value < 16384 ? 2
219
- : value < 2097152 ? 3
220
- : value < 268435456 ? 4
221
- : 5,
222
- value)).len;
143
+ value = value >>> 0;
144
+ this._reserve(5);
145
+ var pos = this.pos;
146
+ this.pos = writeVarint32(value, this.buf, pos);
223
147
  return this;
224
148
  };
225
149
 
@@ -230,9 +154,13 @@ Writer.prototype.uint32 = function write_uint32(value) {
230
154
  * @returns {Writer} `this`
231
155
  */
232
156
  Writer.prototype.int32 = function write_int32(value) {
233
- return (value |= 0) < 0
234
- ? this._push(writeVarint64, 10, LongBits.fromNumber(value)) // 10 bytes per spec
235
- : this.uint32(value);
157
+ if ((value |= 0) < 0) { // 10 bytes per spec
158
+ this._reserve(10);
159
+ writeVarint64(LongBits.fromNumber(value), this.buf, this.pos);
160
+ this.pos += 10;
161
+ return this;
162
+ }
163
+ return this.uint32(value);
236
164
  };
237
165
 
238
166
  /**
@@ -256,7 +184,8 @@ function writeVarint64(val, buf, pos) {
256
184
  buf[pos++] = lo & 127 | 128;
257
185
  lo = lo >>> 7;
258
186
  }
259
- buf[pos++] = lo;
187
+ buf[pos] = lo;
188
+ return pos + 1;
260
189
  }
261
190
 
262
191
  /**
@@ -267,7 +196,10 @@ function writeVarint64(val, buf, pos) {
267
196
  */
268
197
  Writer.prototype.uint64 = function write_uint64(value) {
269
198
  var bits = LongBits.from(value);
270
- return this._push(writeVarint64, bits.length(), bits);
199
+ this._reserve(10);
200
+ var pos = this.pos;
201
+ this.pos = writeVarint64(bits, this.buf, pos);
202
+ return this;
271
203
  };
272
204
 
273
205
  /**
@@ -287,7 +219,10 @@ Writer.prototype.int64 = Writer.prototype.uint64;
287
219
  */
288
220
  Writer.prototype.sint64 = function write_sint64(value) {
289
221
  var bits = LongBits.from(value).zzEncode();
290
- return this._push(writeVarint64, bits.length(), bits);
222
+ this._reserve(10);
223
+ var pos = this.pos;
224
+ this.pos = writeVarint64(bits, this.buf, pos);
225
+ return this;
291
226
  };
292
227
 
293
228
  /**
@@ -296,7 +231,9 @@ Writer.prototype.sint64 = function write_sint64(value) {
296
231
  * @returns {Writer} `this`
297
232
  */
298
233
  Writer.prototype.bool = function write_bool(value) {
299
- return this._push(writeByte, 1, value ? 1 : 0);
234
+ this._reserve(1);
235
+ this.buf[this.pos++] = value ? 1 : 0;
236
+ return this;
300
237
  };
301
238
 
302
239
  function writeFixed32(val, buf, pos) {
@@ -312,7 +249,10 @@ function writeFixed32(val, buf, pos) {
312
249
  * @returns {Writer} `this`
313
250
  */
314
251
  Writer.prototype.fixed32 = function write_fixed32(value) {
315
- return this._push(writeFixed32, 4, value >>> 0);
252
+ this._reserve(4);
253
+ writeFixed32(value >>> 0, this.buf, this.pos);
254
+ this.pos += 4;
255
+ return this;
316
256
  };
317
257
 
318
258
  /**
@@ -331,7 +271,11 @@ Writer.prototype.sfixed32 = Writer.prototype.fixed32;
331
271
  */
332
272
  Writer.prototype.fixed64 = function write_fixed64(value) {
333
273
  var bits = LongBits.from(value);
334
- return this._push(writeFixed32, 4, bits.lo)._push(writeFixed32, 4, bits.hi);
274
+ this._reserve(8);
275
+ writeFixed32(bits.lo, this.buf, this.pos);
276
+ writeFixed32(bits.hi, this.buf, this.pos + 4);
277
+ this.pos += 8;
278
+ return this;
335
279
  };
336
280
 
337
281
  /**
@@ -350,7 +294,10 @@ Writer.prototype.sfixed64 = Writer.prototype.fixed64;
350
294
  * @returns {Writer} `this`
351
295
  */
352
296
  Writer.prototype.float = function write_float(value) {
353
- return this._push(util.float.writeFloatLE, 4, value);
297
+ this._reserve(4);
298
+ util.float.writeFloatLE(value, this.buf, this.pos);
299
+ this.pos += 4;
300
+ return this;
354
301
  };
355
302
 
356
303
  /**
@@ -360,19 +307,12 @@ Writer.prototype.float = function write_float(value) {
360
307
  * @returns {Writer} `this`
361
308
  */
362
309
  Writer.prototype.double = function write_double(value) {
363
- return this._push(util.float.writeDoubleLE, 8, value);
310
+ this._reserve(8);
311
+ util.float.writeDoubleLE(value, this.buf, this.pos);
312
+ this.pos += 8;
313
+ return this;
364
314
  };
365
315
 
366
- var writeBytes = util.Array.prototype.set
367
- ? function writeBytes_set(val, buf, pos) {
368
- buf.set(val, pos); // also works for plain array values
369
- }
370
- /* istanbul ignore next */
371
- : function writeBytes_for(val, buf, pos) {
372
- for (var i = 0; i < val.length; ++i)
373
- buf[pos + i] = val[i];
374
- };
375
-
376
316
  /**
377
317
  * Writes a sequence of bytes.
378
318
  * @param {Uint8Array|string} value Buffer or base64 encoded string to write
@@ -380,14 +320,21 @@ var writeBytes = util.Array.prototype.set
380
320
  */
381
321
  Writer.prototype.bytes = function write_bytes(value) {
382
322
  var len = value.length >>> 0;
383
- if (!len)
384
- return this._push(writeByte, 1, 0);
323
+ if (!len) {
324
+ this._reserve(1);
325
+ this.buf[this.pos++] = 0;
326
+ return this;
327
+ }
385
328
  if (util.isString(value)) {
386
329
  var buf = Writer.alloc(len = base64.length(value));
387
330
  base64.decode(value, buf, 0);
388
331
  value = buf;
389
332
  }
390
- return this.uint32(len)._push(writeBytes, len, value);
333
+ this.uint32(len);
334
+ this._reserve(len);
335
+ this.buf.set(value, this.pos);
336
+ this.pos += len;
337
+ return this;
391
338
  };
392
339
 
393
340
  /**
@@ -397,7 +344,28 @@ Writer.prototype.bytes = function write_bytes(value) {
397
344
  */
398
345
  Writer.prototype.raw = function write_raw(value) {
399
346
  var len = value.length >>> 0;
400
- return len ? this._push(writeBytes, len, value) : this;
347
+ if (!len)
348
+ return this;
349
+ this._reserve(len);
350
+ this.buf.set(value, this.pos);
351
+ this.pos += len;
352
+ return this;
353
+ };
354
+
355
+ /**
356
+ * Backfills the length varint.
357
+ * @param {number} pos Position of reserved length byte
358
+ * @param {number} len Length of content after length varint
359
+ * @returns {Writer} `this`
360
+ * @private
361
+ */
362
+ Writer.prototype._delim = function _delim(pos, len) {
363
+ var n = sizeVarint32(len);
364
+ if (n > 1)
365
+ this.buf.copyWithin(pos + n, pos + 1, pos + 1 + len);
366
+ writeVarint32(len, this.buf, pos);
367
+ this.pos = pos + n + len;
368
+ return this;
401
369
  };
402
370
 
403
371
  /**
@@ -406,21 +374,245 @@ Writer.prototype.raw = function write_raw(value) {
406
374
  * @returns {Writer} `this`
407
375
  */
408
376
  Writer.prototype.string = function write_string(value) {
377
+ var n = value.length;
378
+ if (!n) {
379
+ this._reserve(1);
380
+ this.buf[this.pos++] = 0;
381
+ return this;
382
+ }
383
+ if (n < 0x80) {
384
+ this._reserve(n * 3 + 5); // worst case
385
+ var lenPos = this.pos;
386
+ return this._delim(lenPos, utf8.write(value, this.buf, lenPos + 1));
387
+ }
409
388
  var len = utf8.length(value);
410
- return len
411
- ? this.uint32(len)._push(len === value.length ? writeStringAscii : utf8.write, len, value)
412
- : this._push(writeByte, 1, 0);
389
+ this.uint32(len);
390
+ this._reserve(len);
391
+ if (len === value.length)
392
+ writeStringAscii(value, this.buf, this.pos);
393
+ else
394
+ utf8.write(value, this.buf, this.pos);
395
+ this.pos += len;
396
+ return this;
397
+ };
398
+
399
+ /**
400
+ * Writes an array of unsigned 32 bit values as a packed repeated field.
401
+ * @param {number[]} value Values to write
402
+ * @returns {Writer} `this`
403
+ */
404
+ Writer.prototype.uint32s = function write_uint32s(value) {
405
+ var n = value.length;
406
+ this._reserve(n * 5 + 5); // worst case: 5 bytes per value + length varint
407
+ var buf = this.buf, lenPos = this.pos, p = lenPos + 1;
408
+ for (var i = 0; i < n; ++i)
409
+ p = writeVarint32(value[i] >>> 0, buf, p);
410
+ return this._delim(lenPos, p - lenPos - 1);
411
+ };
412
+
413
+ /**
414
+ * Writes an array of signed 32 bit values as a packed repeated field.
415
+ * @param {number[]} value Values to write
416
+ * @returns {Writer} `this`
417
+ */
418
+ Writer.prototype.int32s = function write_int32s(value) {
419
+ var n = value.length;
420
+ this._reserve(n * 10 + 5); // worst case: 10 bytes per negative value + length varint
421
+ var buf = this.buf, lenPos = this.pos, pos = lenPos + 1, val;
422
+ for (var i = 0; i < n; ++i) {
423
+ if ((val = value[i] | 0) < 0) { // negatives are 10 bytes per spec
424
+ pos = writeVarint64(LongBits.fromNumber(val), buf, pos);
425
+ } else {
426
+ pos = writeVarint32(val, buf, pos);
427
+ }
428
+ }
429
+ return this._delim(lenPos, pos - lenPos - 1);
430
+ };
431
+
432
+ /**
433
+ * Writes an array of 32 bit values as packed, zig-zag encoded varints.
434
+ * @param {number[]} value Values to write
435
+ * @returns {Writer} `this`
436
+ */
437
+ Writer.prototype.sint32s = function write_sint32s(value) {
438
+ var n = value.length;
439
+ this._reserve(n * 5 + 5);
440
+ var buf = this.buf, lenPos = this.pos, pos = lenPos + 1;
441
+ for (var i = 0; i < n; ++i)
442
+ pos = writeVarint32((value[i] << 1 ^ value[i] >> 31) >>> 0, buf, pos);
443
+ return this._delim(lenPos, pos - lenPos - 1);
444
+ };
445
+
446
+ /**
447
+ * Writes an array of unsigned 64 bit values as a packed repeated field.
448
+ * @param {Array.<Long|number|string>} value Values to write
449
+ * @returns {Writer} `this`
450
+ */
451
+ Writer.prototype.uint64s = function write_uint64s(value) {
452
+ var n = value.length;
453
+ this._reserve(n * 10 + 5);
454
+ var buf = this.buf, lenPos = this.pos, pos = lenPos + 1;
455
+ for (var i = 0; i < n; ++i) {
456
+ pos = writeVarint64(LongBits.from(value[i]), buf, pos);
457
+ }
458
+ return this._delim(lenPos, pos - lenPos - 1);
459
+ };
460
+
461
+ /**
462
+ * Writes an array of signed 64 bit values as a packed repeated field.
463
+ * @function
464
+ * @param {Array.<Long|number|string>} value Values to write
465
+ * @returns {Writer} `this`
466
+ */
467
+ Writer.prototype.int64s = Writer.prototype.uint64s;
468
+
469
+ /**
470
+ * Writes an array of 64 bit values as packed, zig-zag encoded varints.
471
+ * @param {Array.<Long|number|string>} value Values to write
472
+ * @returns {Writer} `this`
473
+ */
474
+ Writer.prototype.sint64s = function write_sint64s(value) {
475
+ var n = value.length;
476
+ this._reserve(n * 10 + 5);
477
+ var buf = this.buf, lenPos = this.pos, pos = lenPos + 1;
478
+ for (var i = 0; i < n; ++i) {
479
+ pos = writeVarint64(LongBits.from(value[i]).zzEncode(), buf, pos);
480
+ }
481
+ return this._delim(lenPos, pos - lenPos - 1);
482
+ };
483
+
484
+ /**
485
+ * Writes an array of boolish values as a packed repeated field.
486
+ * @param {boolean[]} value Values to write
487
+ * @returns {Writer} `this`
488
+ */
489
+ Writer.prototype.bools = function write_bools(value) {
490
+ var n = value.length;
491
+ this.uint32(n); // one byte per value
492
+ this._reserve(n);
493
+ var buf = this.buf, p = this.pos;
494
+ for (var i = 0; i < n; ++i)
495
+ buf[p++] = value[i] ? 1 : 0;
496
+ this.pos += n;
497
+ return this;
498
+ };
499
+
500
+ // The view allocation only pays off when amortized over enough writes
501
+ var VIEW_THRESHOLD_FLOAT = 16,
502
+ VIEW_THRESHOLD_INT = 128;
503
+
504
+ function getLazyView(writer, count, threshold) {
505
+ var view = writer.view;
506
+ if (view || count < threshold)
507
+ return view;
508
+ var buf = writer.buf;
509
+ return writer.view = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);
510
+ }
511
+
512
+ /**
513
+ * Writes an array of unsigned 32 bit values as packed, fixed 32 bits.
514
+ * @param {number[]} value Values to write
515
+ * @returns {Writer} `this`
516
+ */
517
+ Writer.prototype.fixed32s = function write_fixed32s(value) {
518
+ var n = value.length, bytes = n * 4;
519
+ this.uint32(bytes); // length is known exactly
520
+ this._reserve(bytes);
521
+ var p = this.pos, i, dv = getLazyView(this, n, VIEW_THRESHOLD_INT);
522
+ if (dv)
523
+ for (i = 0; i < n; ++i) { dv.setUint32(p, value[i] >>> 0, true); p += 4; }
524
+ else {
525
+ var buf = this.buf;
526
+ for (i = 0; i < n; ++i) { writeFixed32(value[i] >>> 0, buf, p); p += 4; }
527
+ }
528
+ this.pos += bytes;
529
+ return this;
530
+ };
531
+
532
+ /**
533
+ * Writes an array of signed 32 bit values as packed, fixed 32 bits.
534
+ * @function
535
+ * @param {number[]} value Values to write
536
+ * @returns {Writer} `this`
537
+ */
538
+ Writer.prototype.sfixed32s = Writer.prototype.fixed32s;
539
+
540
+ /**
541
+ * Writes an array of unsigned 64 bit values as packed, fixed 64 bits.
542
+ * @param {Array.<Long|number|string>} value Values to write
543
+ * @returns {Writer} `this`
544
+ */
545
+ Writer.prototype.fixed64s = function write_fixed64s(value) {
546
+ var n = value.length, bytes = n * 8;
547
+ this.uint32(bytes);
548
+ this._reserve(bytes);
549
+ var p = this.pos, i, bits, dv = getLazyView(this, n, VIEW_THRESHOLD_INT);
550
+ if (dv)
551
+ for (i = 0; i < n; ++i) { bits = LongBits.from(value[i]); dv.setUint32(p, bits.lo, true); dv.setUint32(p + 4, bits.hi, true); p += 8; }
552
+ else {
553
+ var buf = this.buf;
554
+ for (i = 0; i < n; ++i) { bits = LongBits.from(value[i]); writeFixed32(bits.lo, buf, p); writeFixed32(bits.hi, buf, p + 4); p += 8; }
555
+ }
556
+ this.pos += bytes;
557
+ return this;
558
+ };
559
+
560
+ /**
561
+ * Writes an array of signed 64 bit values as packed, fixed 64 bits.
562
+ * @function
563
+ * @param {Array.<Long|number|string>} value Values to write
564
+ * @returns {Writer} `this`
565
+ */
566
+ Writer.prototype.sfixed64s = Writer.prototype.fixed64s;
567
+
568
+ /**
569
+ * Writes an array of floats (32 bit) as a packed repeated field.
570
+ * @param {number[]} value Values to write
571
+ * @returns {Writer} `this`
572
+ */
573
+ Writer.prototype.floats = function write_floats(value) {
574
+ var n = value.length, bytes = n * 4;
575
+ this.uint32(bytes);
576
+ this._reserve(bytes);
577
+ var p = this.pos, i, dv = getLazyView(this, n, VIEW_THRESHOLD_FLOAT);
578
+ if (dv)
579
+ for (i = 0; i < n; ++i) { dv.setFloat32(p, value[i], true); p += 4; }
580
+ else {
581
+ var buf = this.buf;
582
+ for (i = 0; i < n; ++i) { util.float.writeFloatLE(value[i], buf, p); p += 4; }
583
+ }
584
+ this.pos += bytes;
585
+ return this;
586
+ };
587
+
588
+ /**
589
+ * Writes an array of doubles (64 bit float) as a packed repeated field.
590
+ * @param {number[]} value Values to write
591
+ * @returns {Writer} `this`
592
+ */
593
+ Writer.prototype.doubles = function write_doubles(value) {
594
+ var n = value.length, bytes = n * 8;
595
+ this.uint32(bytes);
596
+ this._reserve(bytes);
597
+ var p = this.pos, i, dv = getLazyView(this, n, VIEW_THRESHOLD_FLOAT);
598
+ if (dv)
599
+ for (i = 0; i < n; ++i) { dv.setFloat64(p, value[i], true); p += 8; }
600
+ else {
601
+ var buf = this.buf;
602
+ for (i = 0; i < n; ++i) { util.float.writeDoubleLE(value[i], buf, p); p += 8; }
603
+ }
604
+ this.pos += bytes;
605
+ return this;
413
606
  };
414
607
 
415
608
  /**
416
609
  * Forks this writer's state by pushing it to a stack.
417
- * Calling {@link Writer#reset|reset} or {@link Writer#ldelim|ldelim} resets the writer to the previous state.
418
610
  * @returns {Writer} `this`
419
611
  */
420
612
  Writer.prototype.fork = function fork() {
421
- this.states = new State(this);
422
- this.head = this.tail = new Op(noop, 0, 0);
423
- this.len = 0;
613
+ this._reserve(1);
614
+ (this.states || (this.states = [])).push(this.pos);
615
+ this.pos += 1;
424
616
  return this;
425
617
  };
426
618
 
@@ -429,47 +621,66 @@ Writer.prototype.fork = function fork() {
429
621
  * @returns {Writer} `this`
430
622
  */
431
623
  Writer.prototype.reset = function reset() {
432
- if (this.states) {
433
- this.head = this.states.head;
434
- this.tail = this.states.tail;
435
- this.len = this.states.len;
436
- this.states = this.states.next;
624
+ var states = this.states;
625
+ if (states && states.length) {
626
+ this.pos = states.pop();
437
627
  } else {
438
- this.head = this.tail = new Op(noop, 0, 0);
439
- this.len = 0;
628
+ this.pos = 0;
440
629
  }
441
630
  return this;
442
631
  };
443
632
 
444
633
  /**
445
- * Resets to the last state and appends the fork state's current write length as a varint followed by its operations.
634
+ * Resets to the last state and prepends the fork state's current write length as a varint.
446
635
  * @returns {Writer} `this`
447
636
  */
448
637
  Writer.prototype.ldelim = function ldelim() {
449
- var head = this.head,
450
- tail = this.tail,
451
- len = this.len;
452
- this.reset().uint32(len);
453
- if (len) {
454
- this.tail.next = head.next; // skip noop
455
- this.tail = tail;
456
- this.len += len;
638
+ var states = this.states,
639
+ len,
640
+ vlen;
641
+ if (states && states.length) {
642
+ var lenPos = states.pop();
643
+ len = this.pos - lenPos - 1;
644
+ vlen = sizeVarint32(len);
645
+ if (vlen > 1) { // grow the reserved single byte and shift content forward
646
+ this._reserve(vlen - 1);
647
+ this.buf.copyWithin(lenPos + vlen, lenPos + 1, lenPos + 1 + len);
648
+ this.pos += vlen - 1;
649
+ writeVarint32(len, this.buf, lenPos);
650
+ } else {
651
+ this.buf[lenPos] = len;
652
+ }
653
+ } else { // not forked: prefix the entire buffer with its length
654
+ // TODO: Compatibility with older generated code.
655
+ // Remove this branch in the next major release.
656
+ len = this.pos;
657
+ vlen = sizeVarint32(len);
658
+ this._reserve(vlen);
659
+ this.buf.copyWithin(vlen, 0, len);
660
+ writeVarint32(len, this.buf, 0);
661
+ this.pos += vlen;
457
662
  }
458
663
  return this;
459
664
  };
460
665
 
461
666
  /**
462
667
  * Finishes the write operation.
668
+ * Returns a buffer sized to the written data by default.
669
+ * @param {boolean} [shared=false] Whether to return a shared view instead of a unique copy
463
670
  * @returns {Uint8Array} Finished buffer
464
671
  */
465
- Writer.prototype.finish = function finish() {
466
- return this.finishInto(this.constructor.alloc(this.len), 0);
672
+ Writer.prototype.finish = function finish(shared) {
673
+ if (shared)
674
+ return this.buf.subarray(0, this.pos);
675
+ var buf = this.constructor.alloc(this.pos);
676
+ buf.set(this.buf.subarray(0, this.pos), 0);
677
+ return buf;
467
678
  };
468
679
 
469
680
  /**
470
681
  * Finishes the write operation, writing into the provided buffer.
471
682
  * The caller must ensure that `buf` has enough space starting at `offset`
472
- * to hold {@link Writer#len} bytes.
683
+ * to hold {@link Writer#pos} bytes.
473
684
  * @param {T} buf Target buffer
474
685
  * @param {number} [offset=0] Offset to start writing at
475
686
  * @returns {T} The provided buffer
@@ -478,13 +689,7 @@ Writer.prototype.finish = function finish() {
478
689
  Writer.prototype.finishInto = function finishInto(buf, offset) {
479
690
  if (offset === undefined)
480
691
  offset = 0;
481
- var head = this.head.next,
482
- pos = offset;
483
- while (head) {
484
- head.fn(head.val, buf, pos);
485
- pos += head.len;
486
- head = head.next;
487
- }
692
+ buf.set(this.buf.subarray(0, this.pos), offset);
488
693
  return buf;
489
694
  };
490
695