UNPKG

152 kBJavaScriptView Raw
1/*
2 Copyright 2013-2014 Daniel Wirtz <dcode@dcode.io>
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15 */
16
17/**
18 * @license ByteBuffer.js (c) 2013-2014 Daniel Wirtz <dcode@dcode.io>
19 * This version of ByteBuffer.js uses an ArrayBuffer (AB) as its backing buffer and is compatible with modern browsers.
20 * Released under the Apache License, Version 2.0
21 * see: https://github.com/dcodeIO/ByteBuffer.js for details
22 */ //
23(function(global) {
24 "use strict";
25
26 /**
27 * @param {function(new: Long, number, number, boolean=)=} Long
28 * @returns {function(new: ByteBuffer, number=, boolean=, boolean=)}}
29 * @inner
30 */
31 function loadByteBuffer(Long) {
32
33 /**
34 * Constructs a new ByteBuffer.
35 * @class The swiss army knife for binary data in JavaScript.
36 * @exports ByteBuffer
37 * @constructor
38 * @param {number=} capacity Initial capacity. Defaults to {@link ByteBuffer.DEFAULT_CAPACITY}.
39 * @param {boolean=} littleEndian Whether to use little or big endian byte order. Defaults to
40 * {@link ByteBuffer.DEFAULT_ENDIAN}.
41 * @param {boolean=} noAssert Whether to skip assertions of offsets and values. Defaults to
42 * {@link ByteBuffer.DEFAULT_NOASSERT}.
43 * @expose
44 */
45 var ByteBuffer = function(capacity, littleEndian, noAssert) {
46 if (typeof capacity === 'undefined') capacity = ByteBuffer.DEFAULT_CAPACITY;
47 if (typeof littleEndian === 'undefined') littleEndian = ByteBuffer.DEFAULT_ENDIAN;
48 if (typeof noAssert === 'undefined') noAssert = ByteBuffer.DEFAULT_NOASSERT;
49 if (!noAssert) {
50 capacity = capacity | 0;
51 if (capacity < 0)
52 throw RangeError("Illegal capacity");
53 littleEndian = !!littleEndian;
54 noAssert = !!noAssert;
55 }
56
57 /**
58 * Backing buffer.
59 * @type {!ArrayBuffer}
60 * @expose
61 */
62 this.buffer = capacity === 0 ? EMPTY_BUFFER : new ArrayBuffer(capacity);
63
64 /**
65 * Data view to manipulate the backing buffer. Becomes `null` if the backing buffer has a capacity of `0`.
66 * @type {?DataView}
67 * @expose
68 */
69 this.view = capacity === 0 ? null : new DataView(this.buffer);
70
71 /**
72 * Absolute read/write offset.
73 * @type {number}
74 * @expose
75 * @see ByteBuffer#flip
76 * @see ByteBuffer#clear
77 */
78 this.offset = 0;
79
80 /**
81 * Marked offset.
82 * @type {number}
83 * @expose
84 * @see ByteBuffer#mark
85 * @see ByteBuffer#reset
86 */
87 this.markedOffset = -1;
88
89 /**
90 * Absolute limit of the contained data. Set to the backing buffer's capacity upon allocation.
91 * @type {number}
92 * @expose
93 * @see ByteBuffer#flip
94 * @see ByteBuffer#clear
95 */
96 this.limit = capacity;
97
98 /**
99 * Whether to use little endian byte order, defaults to `false` for big endian.
100 * @type {boolean}
101 * @expose
102 */
103 this.littleEndian = typeof littleEndian !== 'undefined' ? !!littleEndian : false;
104
105 /**
106 * Whether to skip assertions of offsets and values, defaults to `false`.
107 * @type {boolean}
108 * @expose
109 */
110 this.noAssert = !!noAssert;
111 };
112
113 /**
114 * ByteBuffer version.
115 * @type {string}
116 * @const
117 * @expose
118 */
119 ByteBuffer.VERSION = "3.5.0";
120
121 /**
122 * Little endian constant that can be used instead of its boolean value. Evaluates to `true`.
123 * @type {boolean}
124 * @const
125 * @expose
126 */
127 ByteBuffer.LITTLE_ENDIAN = true;
128
129 /**
130 * Big endian constant that can be used instead of its boolean value. Evaluates to `false`.
131 * @type {boolean}
132 * @const
133 * @expose
134 */
135 ByteBuffer.BIG_ENDIAN = false;
136
137 /**
138 * Default initial capacity of `16`.
139 * @type {number}
140 * @expose
141 */
142 ByteBuffer.DEFAULT_CAPACITY = 16;
143
144 /**
145 * Default endianess of `false` for big endian.
146 * @type {boolean}
147 * @expose
148 */
149 ByteBuffer.DEFAULT_ENDIAN = ByteBuffer.BIG_ENDIAN;
150
151 /**
152 * Default no assertions flag of `false`.
153 * @type {boolean}
154 * @expose
155 */
156 ByteBuffer.DEFAULT_NOASSERT = false;
157
158 /**
159 * A `Long` class for representing a 64-bit two's-complement integer value. May be `null` if Long.js has not been loaded
160 * and int64 support is not available.
161 * @type {?Long}
162 * @const
163 * @see https://github.com/dcodeIO/Long.js
164 * @expose
165 */
166 ByteBuffer.Long = Long || null;
167
168 /**
169 * @alias ByteBuffer.prototype
170 * @inner
171 */
172 var ByteBufferPrototype = ByteBuffer.prototype;
173
174 // helpers
175
176 /**
177 * @type {!ArrayBuffer}
178 * @inner
179 */
180 var EMPTY_BUFFER = new ArrayBuffer(0);
181
182 /**
183 * String.fromCharCode reference for compile-time renaming.
184 * @type {function(...number):string}
185 * @inner
186 */
187 var stringFromCharCode = String.fromCharCode;
188
189 /**
190 * Creates a source function for a string.
191 * @param {string} s String to read from
192 * @returns {function():number|null} Source function returning the next char code respectively `null` if there are
193 * no more characters left.
194 * @throws {TypeError} If the argument is invalid
195 * @inner
196 */
197 function stringSource(s) {
198 var i=0; return function() {
199 return i < s.length ? s.charCodeAt(i++) : null;
200 };
201 }
202
203 /**
204 * Creates a destination function for a string.
205 * @returns {function(number=):undefined|string} Destination function successively called with the next char code.
206 * Returns the final string when called without arguments.
207 * @inner
208 */
209 function stringDestination() {
210 var cs = [], ps = []; return function() {
211 if (arguments.length === 0)
212 return ps.join('')+stringFromCharCode.apply(String, cs);
213 if (cs.length + arguments.length > 1024)
214 ps.push(stringFromCharCode.apply(String, cs)),
215 cs.length = 0;
216 Array.prototype.push.apply(cs, arguments);
217 };
218 }
219
220 /**
221 * Allocates a new ByteBuffer backed by a buffer of the specified capacity.
222 * @param {number=} capacity Initial capacity. Defaults to {@link ByteBuffer.DEFAULT_CAPACITY}.
223 * @param {boolean=} littleEndian Whether to use little or big endian byte order. Defaults to
224 * {@link ByteBuffer.DEFAULT_ENDIAN}.
225 * @param {boolean=} noAssert Whether to skip assertions of offsets and values. Defaults to
226 * {@link ByteBuffer.DEFAULT_NOASSERT}.
227 * @returns {!ByteBuffer}
228 * @expose
229 */
230 ByteBuffer.allocate = function(capacity, littleEndian, noAssert) {
231 return new ByteBuffer(capacity, littleEndian, noAssert);
232 };
233
234 /**
235 * Concatenates multiple ByteBuffers into one.
236 * @param {!Array.<!ByteBuffer|!ArrayBuffer|!Uint8Array|string>} buffers Buffers to concatenate
237 * @param {(string|boolean)=} encoding String encoding if `buffers` contains a string ("base64", "hex", "binary",
238 * defaults to "utf8")
239 * @param {boolean=} littleEndian Whether to use little or big endian byte order for the resulting ByteBuffer. Defaults
240 * to {@link ByteBuffer.DEFAULT_ENDIAN}.
241 * @param {boolean=} noAssert Whether to skip assertions of offsets and values for the resulting ByteBuffer. Defaults to
242 * {@link ByteBuffer.DEFAULT_NOASSERT}.
243 * @returns {!ByteBuffer} Concatenated ByteBuffer
244 * @expose
245 */
246 ByteBuffer.concat = function(buffers, encoding, littleEndian, noAssert) {
247 if (typeof encoding === 'boolean' || typeof encoding !== 'string') {
248 noAssert = littleEndian;
249 littleEndian = encoding;
250 encoding = undefined;
251 }
252 var capacity = 0;
253 for (var i=0, k=buffers.length, length; i<k; ++i) {
254 if (!ByteBuffer.isByteBuffer(buffers[i]))
255 buffers[i] = ByteBuffer.wrap(buffers[i], encoding);
256 length = buffers[i].limit - buffers[i].offset;
257 if (length > 0) capacity += length;
258 }
259 if (capacity === 0)
260 return new ByteBuffer(0, littleEndian, noAssert);
261 var bb = new ByteBuffer(capacity, littleEndian, noAssert),
262 bi;
263 var view = new Uint8Array(bb.buffer);
264 i=0; while (i<k) {
265 bi = buffers[i++];
266 length = bi.limit - bi.offset;
267 if (length <= 0) continue;
268 view.set(new Uint8Array(bi.buffer).subarray(bi.offset, bi.limit), bb.offset);
269 bb.offset += length;
270 }
271 bb.limit = bb.offset;
272 bb.offset = 0;
273 return bb;
274 };
275
276 /**
277 * Tests if the specified type is a ByteBuffer.
278 * @param {*} bb ByteBuffer to test
279 * @returns {boolean} `true` if it is a ByteBuffer, otherwise `false`
280 * @expose
281 */
282 ByteBuffer.isByteBuffer = function(bb) {
283 return (bb && bb instanceof ByteBuffer) === true;
284 };
285 /**
286 * Gets the backing buffer type.
287 * @returns {Function} `Buffer` for NB builds, `ArrayBuffer` for AB builds (classes)
288 * @expose
289 */
290 ByteBuffer.type = function() {
291 return ArrayBuffer;
292 };
293
294 /**
295 * Wraps a buffer or a string. Sets the allocated ByteBuffer's {@link ByteBuffer#offset} to `0` and its
296 * {@link ByteBuffer#limit} to the length of the wrapped data.
297 * @param {!ByteBuffer|!ArrayBuffer|!Uint8Array|string|!Array.<number>} buffer Anything that can be wrapped
298 * @param {(string|boolean)=} encoding String encoding if `buffer` is a string ("base64", "hex", "binary", defaults to
299 * "utf8")
300 * @param {boolean=} littleEndian Whether to use little or big endian byte order. Defaults to
301 * {@link ByteBuffer.DEFAULT_ENDIAN}.
302 * @param {boolean=} noAssert Whether to skip assertions of offsets and values. Defaults to
303 * {@link ByteBuffer.DEFAULT_NOASSERT}.
304 * @returns {!ByteBuffer} A ByteBuffer wrapping `buffer`
305 * @expose
306 */
307 ByteBuffer.wrap = function(buffer, encoding, littleEndian, noAssert) {
308 if (typeof encoding !== 'string') {
309 noAssert = littleEndian;
310 littleEndian = encoding;
311 encoding = undefined;
312 }
313 if (typeof buffer === 'string') {
314 if (typeof encoding === 'undefined')
315 encoding = "utf8";
316 switch (encoding) {
317 case "base64":
318 return ByteBuffer.fromBase64(buffer, littleEndian);
319 case "hex":
320 return ByteBuffer.fromHex(buffer, littleEndian);
321 case "binary":
322 return ByteBuffer.fromBinary(buffer, littleEndian);
323 case "utf8":
324 return ByteBuffer.fromUTF8(buffer, littleEndian);
325 case "debug":
326 return ByteBuffer.fromDebug(buffer, littleEndian);
327 default:
328 throw Error("Unsupported encoding: "+encoding);
329 }
330 }
331 if (buffer === null || typeof buffer !== 'object')
332 throw TypeError("Illegal buffer");
333 var bb;
334 if (ByteBuffer.isByteBuffer(buffer)) {
335 bb = ByteBufferPrototype.clone.call(buffer);
336 bb.markedOffset = -1;
337 return bb;
338 }
339 if (buffer instanceof Uint8Array) { // Extract ArrayBuffer from Uint8Array
340 bb = new ByteBuffer(0, littleEndian, noAssert);
341 if (buffer.length > 0) { // Avoid references to more than one EMPTY_BUFFER
342 bb.buffer = buffer.buffer;
343 bb.offset = buffer.byteOffset;
344 bb.limit = buffer.byteOffset + buffer.length;
345 bb.view = buffer.length > 0 ? new DataView(buffer.buffer) : null;
346 }
347 } else if (buffer instanceof ArrayBuffer) { // Reuse ArrayBuffer
348 bb = new ByteBuffer(0, littleEndian, noAssert);
349 if (buffer.byteLength > 0) {
350 bb.buffer = buffer;
351 bb.offset = 0;
352 bb.limit = buffer.byteLength;
353 bb.view = buffer.byteLength > 0 ? new DataView(buffer) : null;
354 }
355 } else if (Object.prototype.toString.call(buffer) === "[object Array]") { // Create from octets
356 bb = new ByteBuffer(buffer.length, littleEndian, noAssert);
357 bb.limit = buffer.length;
358 for (i=0; i<buffer.length; ++i)
359 bb.view.setUint8(i, buffer[i]);
360 } else
361 throw TypeError("Illegal buffer"); // Otherwise fail
362 return bb;
363 };
364
365 // types/ints/int8
366
367 /**
368 * Writes an 8bit signed integer.
369 * @param {number} value Value to write
370 * @param {number=} offset Offset to write to. Will use and advance {@link ByteBuffer#offset} by `1` if omitted.
371 * @returns {!ByteBuffer} this
372 * @expose
373 */
374 ByteBufferPrototype.writeInt8 = function(value, offset) {
375 var relative = typeof offset === 'undefined';
376 if (relative) offset = this.offset;
377 if (!this.noAssert) {
378 if (typeof value !== 'number' || value % 1 !== 0)
379 throw TypeError("Illegal value: "+value+" (not an integer)");
380 value |= 0;
381 if (typeof offset !== 'number' || offset % 1 !== 0)
382 throw TypeError("Illegal offset: "+offset+" (not an integer)");
383 offset >>>= 0;
384 if (offset < 0 || offset + 0 > this.buffer.byteLength)
385 throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength);
386 }
387 offset += 1;
388 var capacity0 = this.buffer.byteLength;
389 if (offset > capacity0)
390 this.resize((capacity0 *= 2) > offset ? capacity0 : offset);
391 offset -= 1;
392 this.view.setInt8(offset, value);
393 if (relative) this.offset += 1;
394 return this;
395 };
396
397 /**
398 * Writes an 8bit signed integer. This is an alias of {@link ByteBuffer#writeInt8}.
399 * @function
400 * @param {number} value Value to write
401 * @param {number=} offset Offset to write to. Will use and advance {@link ByteBuffer#offset} by `1` if omitted.
402 * @returns {!ByteBuffer} this
403 * @expose
404 */
405 ByteBufferPrototype.writeByte = ByteBufferPrototype.writeInt8;
406
407 /**
408 * Reads an 8bit signed integer.
409 * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} by `1` if omitted.
410 * @returns {number} Value read
411 * @expose
412 */
413 ByteBufferPrototype.readInt8 = function(offset) {
414 var relative = typeof offset === 'undefined';
415 if (relative) offset = this.offset;
416 if (!this.noAssert) {
417 if (typeof offset !== 'number' || offset % 1 !== 0)
418 throw TypeError("Illegal offset: "+offset+" (not an integer)");
419 offset >>>= 0;
420 if (offset < 0 || offset + 1 > this.buffer.byteLength)
421 throw RangeError("Illegal offset: 0 <= "+offset+" (+"+1+") <= "+this.buffer.byteLength);
422 }
423 var value = this.view.getInt8(offset);
424 if (relative) this.offset += 1;
425 return value;
426 };
427
428 /**
429 * Reads an 8bit signed integer. This is an alias of {@link ByteBuffer#readInt8}.
430 * @function
431 * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} by `1` if omitted.
432 * @returns {number} Value read
433 * @expose
434 */
435 ByteBufferPrototype.readByte = ByteBufferPrototype.readInt8;
436
437 /**
438 * Writes an 8bit unsigned integer.
439 * @param {number} value Value to write
440 * @param {number=} offset Offset to write to. Will use and advance {@link ByteBuffer#offset} by `1` if omitted.
441 * @returns {!ByteBuffer} this
442 * @expose
443 */
444 ByteBufferPrototype.writeUint8 = function(value, offset) {
445 var relative = typeof offset === 'undefined';
446 if (relative) offset = this.offset;
447 if (!this.noAssert) {
448 if (typeof value !== 'number' || value % 1 !== 0)
449 throw TypeError("Illegal value: "+value+" (not an integer)");
450 value >>>= 0;
451 if (typeof offset !== 'number' || offset % 1 !== 0)
452 throw TypeError("Illegal offset: "+offset+" (not an integer)");
453 offset >>>= 0;
454 if (offset < 0 || offset + 0 > this.buffer.byteLength)
455 throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength);
456 }
457 offset += 1;
458 var capacity1 = this.buffer.byteLength;
459 if (offset > capacity1)
460 this.resize((capacity1 *= 2) > offset ? capacity1 : offset);
461 offset -= 1;
462 this.view.setUint8(offset, value);
463 if (relative) this.offset += 1;
464 return this;
465 };
466
467 /**
468 * Reads an 8bit unsigned integer.
469 * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} by `1` if omitted.
470 * @returns {number} Value read
471 * @expose
472 */
473 ByteBufferPrototype.readUint8 = function(offset) {
474 var relative = typeof offset === 'undefined';
475 if (relative) offset = this.offset;
476 if (!this.noAssert) {
477 if (typeof offset !== 'number' || offset % 1 !== 0)
478 throw TypeError("Illegal offset: "+offset+" (not an integer)");
479 offset >>>= 0;
480 if (offset < 0 || offset + 1 > this.buffer.byteLength)
481 throw RangeError("Illegal offset: 0 <= "+offset+" (+"+1+") <= "+this.buffer.byteLength);
482 }
483 var value = this.view.getUint8(offset);
484 if (relative) this.offset += 1;
485 return value;
486 };
487
488 // types/ints/int16
489
490 /**
491 * Writes a 16bit signed integer.
492 * @param {number} value Value to write
493 * @param {number=} offset Offset to write to. Will use and advance {@link ByteBuffer#offset} by `2` if omitted.
494 * @throws {TypeError} If `offset` or `value` is not a valid number
495 * @throws {RangeError} If `offset` is out of bounds
496 * @expose
497 */
498 ByteBufferPrototype.writeInt16 = function(value, offset) {
499 var relative = typeof offset === 'undefined';
500 if (relative) offset = this.offset;
501 if (!this.noAssert) {
502 if (typeof value !== 'number' || value % 1 !== 0)
503 throw TypeError("Illegal value: "+value+" (not an integer)");
504 value |= 0;
505 if (typeof offset !== 'number' || offset % 1 !== 0)
506 throw TypeError("Illegal offset: "+offset+" (not an integer)");
507 offset >>>= 0;
508 if (offset < 0 || offset + 0 > this.buffer.byteLength)
509 throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength);
510 }
511 offset += 2;
512 var capacity2 = this.buffer.byteLength;
513 if (offset > capacity2)
514 this.resize((capacity2 *= 2) > offset ? capacity2 : offset);
515 offset -= 2;
516 this.view.setInt16(offset, value, this.littleEndian);
517 if (relative) this.offset += 2;
518 return this;
519 };
520
521 /**
522 * Writes a 16bit signed integer. This is an alias of {@link ByteBuffer#writeInt16}.
523 * @function
524 * @param {number} value Value to write
525 * @param {number=} offset Offset to write to. Will use and advance {@link ByteBuffer#offset} by `2` if omitted.
526 * @throws {TypeError} If `offset` or `value` is not a valid number
527 * @throws {RangeError} If `offset` is out of bounds
528 * @expose
529 */
530 ByteBufferPrototype.writeShort = ByteBufferPrototype.writeInt16;
531
532 /**
533 * Reads a 16bit signed integer.
534 * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} by `2` if omitted.
535 * @returns {number} Value read
536 * @throws {TypeError} If `offset` is not a valid number
537 * @throws {RangeError} If `offset` is out of bounds
538 * @expose
539 */
540 ByteBufferPrototype.readInt16 = function(offset) {
541 var relative = typeof offset === 'undefined';
542 if (relative) offset = this.offset;
543 if (!this.noAssert) {
544 if (typeof offset !== 'number' || offset % 1 !== 0)
545 throw TypeError("Illegal offset: "+offset+" (not an integer)");
546 offset >>>= 0;
547 if (offset < 0 || offset + 2 > this.buffer.byteLength)
548 throw RangeError("Illegal offset: 0 <= "+offset+" (+"+2+") <= "+this.buffer.byteLength);
549 }
550 var value = this.view.getInt16(offset, this.littleEndian);
551 if (relative) this.offset += 2;
552 return value;
553 };
554
555 /**
556 * Reads a 16bit signed integer. This is an alias of {@link ByteBuffer#readInt16}.
557 * @function
558 * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} by `2` if omitted.
559 * @returns {number} Value read
560 * @throws {TypeError} If `offset` is not a valid number
561 * @throws {RangeError} If `offset` is out of bounds
562 * @expose
563 */
564 ByteBufferPrototype.readShort = ByteBufferPrototype.readInt16;
565
566 /**
567 * Writes a 16bit unsigned integer.
568 * @param {number} value Value to write
569 * @param {number=} offset Offset to write to. Will use and advance {@link ByteBuffer#offset} by `2` if omitted.
570 * @throws {TypeError} If `offset` or `value` is not a valid number
571 * @throws {RangeError} If `offset` is out of bounds
572 * @expose
573 */
574 ByteBufferPrototype.writeUint16 = function(value, offset) {
575 var relative = typeof offset === 'undefined';
576 if (relative) offset = this.offset;
577 if (!this.noAssert) {
578 if (typeof value !== 'number' || value % 1 !== 0)
579 throw TypeError("Illegal value: "+value+" (not an integer)");
580 value >>>= 0;
581 if (typeof offset !== 'number' || offset % 1 !== 0)
582 throw TypeError("Illegal offset: "+offset+" (not an integer)");
583 offset >>>= 0;
584 if (offset < 0 || offset + 0 > this.buffer.byteLength)
585 throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength);
586 }
587 offset += 2;
588 var capacity3 = this.buffer.byteLength;
589 if (offset > capacity3)
590 this.resize((capacity3 *= 2) > offset ? capacity3 : offset);
591 offset -= 2;
592 this.view.setUint16(offset, value, this.littleEndian);
593 if (relative) this.offset += 2;
594 return this;
595 };
596
597 /**
598 * Reads a 16bit unsigned integer.
599 * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} by `2` if omitted.
600 * @returns {number} Value read
601 * @throws {TypeError} If `offset` is not a valid number
602 * @throws {RangeError} If `offset` is out of bounds
603 * @expose
604 */
605 ByteBufferPrototype.readUint16 = function(offset) {
606 var relative = typeof offset === 'undefined';
607 if (relative) offset = this.offset;
608 if (!this.noAssert) {
609 if (typeof offset !== 'number' || offset % 1 !== 0)
610 throw TypeError("Illegal offset: "+offset+" (not an integer)");
611 offset >>>= 0;
612 if (offset < 0 || offset + 2 > this.buffer.byteLength)
613 throw RangeError("Illegal offset: 0 <= "+offset+" (+"+2+") <= "+this.buffer.byteLength);
614 }
615 var value = this.view.getUint16(offset, this.littleEndian);
616 if (relative) this.offset += 2;
617 return value;
618 };
619
620 // types/ints/int32
621
622 /**
623 * Writes a 32bit signed integer.
624 * @param {number} value Value to write
625 * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by `4` if omitted.
626 * @expose
627 */
628 ByteBufferPrototype.writeInt32 = function(value, offset) {
629 var relative = typeof offset === 'undefined';
630 if (relative) offset = this.offset;
631 if (!this.noAssert) {
632 if (typeof value !== 'number' || value % 1 !== 0)
633 throw TypeError("Illegal value: "+value+" (not an integer)");
634 value |= 0;
635 if (typeof offset !== 'number' || offset % 1 !== 0)
636 throw TypeError("Illegal offset: "+offset+" (not an integer)");
637 offset >>>= 0;
638 if (offset < 0 || offset + 0 > this.buffer.byteLength)
639 throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength);
640 }
641 offset += 4;
642 var capacity4 = this.buffer.byteLength;
643 if (offset > capacity4)
644 this.resize((capacity4 *= 2) > offset ? capacity4 : offset);
645 offset -= 4;
646 this.view.setInt32(offset, value, this.littleEndian);
647 if (relative) this.offset += 4;
648 return this;
649 };
650
651 /**
652 * Writes a 32bit signed integer. This is an alias of {@link ByteBuffer#writeInt32}.
653 * @param {number} value Value to write
654 * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by `4` if omitted.
655 * @expose
656 */
657 ByteBufferPrototype.writeInt = ByteBufferPrototype.writeInt32;
658
659 /**
660 * Reads a 32bit signed integer.
661 * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by `4` if omitted.
662 * @returns {number} Value read
663 * @expose
664 */
665 ByteBufferPrototype.readInt32 = function(offset) {
666 var relative = typeof offset === 'undefined';
667 if (relative) offset = this.offset;
668 if (!this.noAssert) {
669 if (typeof offset !== 'number' || offset % 1 !== 0)
670 throw TypeError("Illegal offset: "+offset+" (not an integer)");
671 offset >>>= 0;
672 if (offset < 0 || offset + 4 > this.buffer.byteLength)
673 throw RangeError("Illegal offset: 0 <= "+offset+" (+"+4+") <= "+this.buffer.byteLength);
674 }
675 var value = this.view.getInt32(offset, this.littleEndian);
676 if (relative) this.offset += 4;
677 return value;
678 };
679
680 /**
681 * Reads a 32bit signed integer. This is an alias of {@link ByteBuffer#readInt32}.
682 * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} by `4` if omitted.
683 * @returns {number} Value read
684 * @expose
685 */
686 ByteBufferPrototype.readInt = ByteBufferPrototype.readInt32;
687
688 /**
689 * Writes a 32bit unsigned integer.
690 * @param {number} value Value to write
691 * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by `4` if omitted.
692 * @expose
693 */
694 ByteBufferPrototype.writeUint32 = function(value, offset) {
695 var relative = typeof offset === 'undefined';
696 if (relative) offset = this.offset;
697 if (!this.noAssert) {
698 if (typeof value !== 'number' || value % 1 !== 0)
699 throw TypeError("Illegal value: "+value+" (not an integer)");
700 value >>>= 0;
701 if (typeof offset !== 'number' || offset % 1 !== 0)
702 throw TypeError("Illegal offset: "+offset+" (not an integer)");
703 offset >>>= 0;
704 if (offset < 0 || offset + 0 > this.buffer.byteLength)
705 throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength);
706 }
707 offset += 4;
708 var capacity5 = this.buffer.byteLength;
709 if (offset > capacity5)
710 this.resize((capacity5 *= 2) > offset ? capacity5 : offset);
711 offset -= 4;
712 this.view.setUint32(offset, value, this.littleEndian);
713 if (relative) this.offset += 4;
714 return this;
715 };
716
717 /**
718 * Reads a 32bit unsigned integer.
719 * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by `4` if omitted.
720 * @returns {number} Value read
721 * @expose
722 */
723 ByteBufferPrototype.readUint32 = function(offset) {
724 var relative = typeof offset === 'undefined';
725 if (relative) offset = this.offset;
726 if (!this.noAssert) {
727 if (typeof offset !== 'number' || offset % 1 !== 0)
728 throw TypeError("Illegal offset: "+offset+" (not an integer)");
729 offset >>>= 0;
730 if (offset < 0 || offset + 4 > this.buffer.byteLength)
731 throw RangeError("Illegal offset: 0 <= "+offset+" (+"+4+") <= "+this.buffer.byteLength);
732 }
733 var value = this.view.getUint32(offset, this.littleEndian);
734 if (relative) this.offset += 4;
735 return value;
736 };
737
738 // types/ints/int64
739
740 if (Long) {
741
742 /**
743 * Writes a 64bit signed integer.
744 * @param {number|!Long} value Value to write
745 * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by `8` if omitted.
746 * @returns {!ByteBuffer} this
747 * @expose
748 */
749 ByteBufferPrototype.writeInt64 = function(value, offset) {
750 var relative = typeof offset === 'undefined';
751 if (relative) offset = this.offset;
752 if (!this.noAssert) {
753 if (typeof value === 'number')
754 value = Long.fromNumber(value);
755 else if (!(value && value instanceof Long))
756 throw TypeError("Illegal value: "+value+" (not an integer or Long)");
757 if (typeof offset !== 'number' || offset % 1 !== 0)
758 throw TypeError("Illegal offset: "+offset+" (not an integer)");
759 offset >>>= 0;
760 if (offset < 0 || offset + 0 > this.buffer.byteLength)
761 throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength);
762 }
763 if (typeof value === 'number')
764 value = Long.fromNumber(value);
765 offset += 8;
766 var capacity6 = this.buffer.byteLength;
767 if (offset > capacity6)
768 this.resize((capacity6 *= 2) > offset ? capacity6 : offset);
769 offset -= 8;
770 if (this.littleEndian) {
771 this.view.setInt32(offset , value.low , true);
772 this.view.setInt32(offset+4, value.high, true);
773 } else {
774 this.view.setInt32(offset , value.high, false);
775 this.view.setInt32(offset+4, value.low , false);
776 }
777 if (relative) this.offset += 8;
778 return this;
779 };
780
781 /**
782 * Writes a 64bit signed integer. This is an alias of {@link ByteBuffer#writeInt64}.
783 * @param {number|!Long} value Value to write
784 * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by `8` if omitted.
785 * @returns {!ByteBuffer} this
786 * @expose
787 */
788 ByteBufferPrototype.writeLong = ByteBufferPrototype.writeInt64;
789
790 /**
791 * Reads a 64bit signed integer.
792 * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by `8` if omitted.
793 * @returns {!Long}
794 * @expose
795 */
796 ByteBufferPrototype.readInt64 = function(offset) {
797 var relative = typeof offset === 'undefined';
798 if (relative) offset = this.offset;
799 if (!this.noAssert) {
800 if (typeof offset !== 'number' || offset % 1 !== 0)
801 throw TypeError("Illegal offset: "+offset+" (not an integer)");
802 offset >>>= 0;
803 if (offset < 0 || offset + 8 > this.buffer.byteLength)
804 throw RangeError("Illegal offset: 0 <= "+offset+" (+"+8+") <= "+this.buffer.byteLength);
805 }
806 var value = this.littleEndian
807 ? new Long(this.view.getInt32(offset , true ), this.view.getInt32(offset+4, true ), false)
808 : new Long(this.view.getInt32(offset+4, false), this.view.getInt32(offset , false), false);
809 if (relative) this.offset += 8;
810 return value;
811 };
812
813 /**
814 * Reads a 64bit signed integer. This is an alias of {@link ByteBuffer#readInt64}.
815 * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by `8` if omitted.
816 * @returns {!Long}
817 * @expose
818 */
819 ByteBufferPrototype.readLong = ByteBufferPrototype.readInt64;
820
821 /**
822 * Writes a 64bit unsigned integer.
823 * @param {number|!Long} value Value to write
824 * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by `8` if omitted.
825 * @returns {!ByteBuffer} this
826 * @expose
827 */
828 ByteBufferPrototype.writeUint64 = function(value, offset) {
829 var relative = typeof offset === 'undefined';
830 if (relative) offset = this.offset;
831 if (!this.noAssert) {
832 if (typeof value === 'number')
833 value = Long.fromNumber(value);
834 else if (!(value && value instanceof Long))
835 throw TypeError("Illegal value: "+value+" (not an integer or Long)");
836 if (typeof offset !== 'number' || offset % 1 !== 0)
837 throw TypeError("Illegal offset: "+offset+" (not an integer)");
838 offset >>>= 0;
839 if (offset < 0 || offset + 0 > this.buffer.byteLength)
840 throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength);
841 }
842 if (typeof value === 'number')
843 value = Long.fromNumber(value);
844 offset += 8;
845 var capacity7 = this.buffer.byteLength;
846 if (offset > capacity7)
847 this.resize((capacity7 *= 2) > offset ? capacity7 : offset);
848 offset -= 8;
849 if (this.littleEndian) {
850 this.view.setInt32(offset , value.low , true);
851 this.view.setInt32(offset+4, value.high, true);
852 } else {
853 this.view.setInt32(offset , value.high, false);
854 this.view.setInt32(offset+4, value.low , false);
855 }
856 if (relative) this.offset += 8;
857 return this;
858 };
859
860 /**
861 * Reads a 64bit unsigned integer.
862 * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by `8` if omitted.
863 * @returns {!Long}
864 * @expose
865 */
866 ByteBufferPrototype.readUint64 = function(offset) {
867 var relative = typeof offset === 'undefined';
868 if (relative) offset = this.offset;
869 if (!this.noAssert) {
870 if (typeof offset !== 'number' || offset % 1 !== 0)
871 throw TypeError("Illegal offset: "+offset+" (not an integer)");
872 offset >>>= 0;
873 if (offset < 0 || offset + 8 > this.buffer.byteLength)
874 throw RangeError("Illegal offset: 0 <= "+offset+" (+"+8+") <= "+this.buffer.byteLength);
875 }
876 var value = this.littleEndian
877 ? new Long(this.view.getInt32(offset , true ), this.view.getInt32(offset+4, true ), true)
878 : new Long(this.view.getInt32(offset+4, false), this.view.getInt32(offset , false), true);
879 if (relative) this.offset += 8;
880 return value;
881 };
882
883 } // Long
884
885
886 // types/floats/float32
887
888 /**
889 * Writes a 32bit float.
890 * @param {number} value Value to write
891 * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by `4` if omitted.
892 * @returns {!ByteBuffer} this
893 * @expose
894 */
895 ByteBufferPrototype.writeFloat32 = function(value, offset) {
896 var relative = typeof offset === 'undefined';
897 if (relative) offset = this.offset;
898 if (!this.noAssert) {
899 if (typeof value !== 'number')
900 throw TypeError("Illegal value: "+value+" (not a number)");
901 if (typeof offset !== 'number' || offset % 1 !== 0)
902 throw TypeError("Illegal offset: "+offset+" (not an integer)");
903 offset >>>= 0;
904 if (offset < 0 || offset + 0 > this.buffer.byteLength)
905 throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength);
906 }
907 offset += 4;
908 var capacity8 = this.buffer.byteLength;
909 if (offset > capacity8)
910 this.resize((capacity8 *= 2) > offset ? capacity8 : offset);
911 offset -= 4;
912 this.view.setFloat32(offset, value, this.littleEndian);
913 if (relative) this.offset += 4;
914 return this;
915 };
916
917 /**
918 * Writes a 32bit float. This is an alias of {@link ByteBuffer#writeFloat32}.
919 * @function
920 * @param {number} value Value to write
921 * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by `4` if omitted.
922 * @returns {!ByteBuffer} this
923 * @expose
924 */
925 ByteBufferPrototype.writeFloat = ByteBufferPrototype.writeFloat32;
926
927 /**
928 * Reads a 32bit float.
929 * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by `4` if omitted.
930 * @returns {number}
931 * @expose
932 */
933 ByteBufferPrototype.readFloat32 = function(offset) {
934 var relative = typeof offset === 'undefined';
935 if (relative) offset = this.offset;
936 if (!this.noAssert) {
937 if (typeof offset !== 'number' || offset % 1 !== 0)
938 throw TypeError("Illegal offset: "+offset+" (not an integer)");
939 offset >>>= 0;
940 if (offset < 0 || offset + 4 > this.buffer.byteLength)
941 throw RangeError("Illegal offset: 0 <= "+offset+" (+"+4+") <= "+this.buffer.byteLength);
942 }
943 var value = this.view.getFloat32(offset, this.littleEndian);
944 if (relative) this.offset += 4;
945 return value;
946 };
947
948 /**
949 * Reads a 32bit float. This is an alias of {@link ByteBuffer#readFloat32}.
950 * @function
951 * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by `4` if omitted.
952 * @returns {number}
953 * @expose
954 */
955 ByteBufferPrototype.readFloat = ByteBufferPrototype.readFloat32;
956
957 // types/floats/float64
958
959 /**
960 * Writes a 64bit float.
961 * @param {number} value Value to write
962 * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by `8` if omitted.
963 * @returns {!ByteBuffer} this
964 * @expose
965 */
966 ByteBufferPrototype.writeFloat64 = function(value, offset) {
967 var relative = typeof offset === 'undefined';
968 if (relative) offset = this.offset;
969 if (!this.noAssert) {
970 if (typeof value !== 'number')
971 throw TypeError("Illegal value: "+value+" (not a number)");
972 if (typeof offset !== 'number' || offset % 1 !== 0)
973 throw TypeError("Illegal offset: "+offset+" (not an integer)");
974 offset >>>= 0;
975 if (offset < 0 || offset + 0 > this.buffer.byteLength)
976 throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength);
977 }
978 offset += 8;
979 var capacity9 = this.buffer.byteLength;
980 if (offset > capacity9)
981 this.resize((capacity9 *= 2) > offset ? capacity9 : offset);
982 offset -= 8;
983 this.view.setFloat64(offset, value, this.littleEndian);
984 if (relative) this.offset += 8;
985 return this;
986 };
987
988 /**
989 * Writes a 64bit float. This is an alias of {@link ByteBuffer#writeFloat64}.
990 * @function
991 * @param {number} value Value to write
992 * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by `8` if omitted.
993 * @returns {!ByteBuffer} this
994 * @expose
995 */
996 ByteBufferPrototype.writeDouble = ByteBufferPrototype.writeFloat64;
997
998 /**
999 * Reads a 64bit float.
1000 * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by `8` if omitted.
1001 * @returns {number}
1002 * @expose
1003 */
1004 ByteBufferPrototype.readFloat64 = function(offset) {
1005 var relative = typeof offset === 'undefined';
1006 if (relative) offset = this.offset;
1007 if (!this.noAssert) {
1008 if (typeof offset !== 'number' || offset % 1 !== 0)
1009 throw TypeError("Illegal offset: "+offset+" (not an integer)");
1010 offset >>>= 0;
1011 if (offset < 0 || offset + 8 > this.buffer.byteLength)
1012 throw RangeError("Illegal offset: 0 <= "+offset+" (+"+8+") <= "+this.buffer.byteLength);
1013 }
1014 var value = this.view.getFloat64(offset, this.littleEndian);
1015 if (relative) this.offset += 8;
1016 return value;
1017 };
1018
1019 /**
1020 * Reads a 64bit float. This is an alias of {@link ByteBuffer#readFloat64}.
1021 * @function
1022 * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by `8` if omitted.
1023 * @returns {number}
1024 * @expose
1025 */
1026 ByteBufferPrototype.readDouble = ByteBufferPrototype.readFloat64;
1027
1028
1029 // types/varints/varint32
1030
1031 /**
1032 * Maximum number of bytes required to store a 32bit base 128 variable-length integer.
1033 * @type {number}
1034 * @const
1035 * @expose
1036 */
1037 ByteBuffer.MAX_VARINT32_BYTES = 5;
1038
1039 /**
1040 * Calculates the actual number of bytes required to store a 32bit base 128 variable-length integer.
1041 * @param {number} value Value to encode
1042 * @returns {number} Number of bytes required. Capped to {@link ByteBuffer.MAX_VARINT32_BYTES}
1043 * @expose
1044 */
1045 ByteBuffer.calculateVarint32 = function(value) {
1046 // ref: src/google/protobuf/io/coded_stream.cc
1047 value = value >>> 0;
1048 if (value < 1 << 7 ) return 1;
1049 else if (value < 1 << 14) return 2;
1050 else if (value < 1 << 21) return 3;
1051 else if (value < 1 << 28) return 4;
1052 else return 5;
1053 };
1054
1055 /**
1056 * Zigzag encodes a signed 32bit integer so that it can be effectively used with varint encoding.
1057 * @param {number} n Signed 32bit integer
1058 * @returns {number} Unsigned zigzag encoded 32bit integer
1059 * @expose
1060 */
1061 ByteBuffer.zigZagEncode32 = function(n) {
1062 return (((n |= 0) << 1) ^ (n >> 31)) >>> 0; // ref: src/google/protobuf/wire_format_lite.h
1063 };
1064
1065 /**
1066 * Decodes a zigzag encoded signed 32bit integer.
1067 * @param {number} n Unsigned zigzag encoded 32bit integer
1068 * @returns {number} Signed 32bit integer
1069 * @expose
1070 */
1071 ByteBuffer.zigZagDecode32 = function(n) {
1072 return ((n >>> 1) ^ -(n & 1)) | 0; // // ref: src/google/protobuf/wire_format_lite.h
1073 };
1074
1075 /**
1076 * Writes a 32bit base 128 variable-length integer.
1077 * @param {number} value Value to write
1078 * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by the number of bytes
1079 * written if omitted.
1080 * @returns {!ByteBuffer|number} this if `offset` is omitted, else the actual number of bytes written
1081 * @expose
1082 */
1083 ByteBufferPrototype.writeVarint32 = function(value, offset) {
1084 var relative = typeof offset === 'undefined';
1085 if (relative) offset = this.offset;
1086 if (!this.noAssert) {
1087 if (typeof value !== 'number' || value % 1 !== 0)
1088 throw TypeError("Illegal value: "+value+" (not an integer)");
1089 value |= 0;
1090 if (typeof offset !== 'number' || offset % 1 !== 0)
1091 throw TypeError("Illegal offset: "+offset+" (not an integer)");
1092 offset >>>= 0;
1093 if (offset < 0 || offset + 0 > this.buffer.byteLength)
1094 throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength);
1095 }
1096 var size = ByteBuffer.calculateVarint32(value),
1097 b;
1098 offset += size;
1099 var capacity10 = this.buffer.byteLength;
1100 if (offset > capacity10)
1101 this.resize((capacity10 *= 2) > offset ? capacity10 : offset);
1102 offset -= size;
1103 // ref: http://code.google.com/searchframe#WTeibokF6gE/trunk/src/google/protobuf/io/coded_stream.cc
1104 this.view.setUint8(offset, b = value | 0x80);
1105 value >>>= 0;
1106 if (value >= 1 << 7) {
1107 b = (value >> 7) | 0x80;
1108 this.view.setUint8(offset+1, b);
1109 if (value >= 1 << 14) {
1110 b = (value >> 14) | 0x80;
1111 this.view.setUint8(offset+2, b);
1112 if (value >= 1 << 21) {
1113 b = (value >> 21) | 0x80;
1114 this.view.setUint8(offset+3, b);
1115 if (value >= 1 << 28) {
1116 this.view.setUint8(offset+4, (value >> 28) & 0x0F);
1117 size = 5;
1118 } else {
1119 this.view.setUint8(offset+3, b & 0x7F);
1120 size = 4;
1121 }
1122 } else {
1123 this.view.setUint8(offset+2, b & 0x7F);
1124 size = 3;
1125 }
1126 } else {
1127 this.view.setUint8(offset+1, b & 0x7F);
1128 size = 2;
1129 }
1130 } else {
1131 this.view.setUint8(offset, b & 0x7F);
1132 size = 1;
1133 }
1134 if (relative) {
1135 this.offset += size;
1136 return this;
1137 }
1138 return size;
1139 };
1140
1141 /**
1142 * Writes a zig-zag encoded 32bit base 128 variable-length integer.
1143 * @param {number} value Value to write
1144 * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by the number of bytes
1145 * written if omitted.
1146 * @returns {!ByteBuffer|number} this if `offset` is omitted, else the actual number of bytes written
1147 * @expose
1148 */
1149 ByteBufferPrototype.writeVarint32ZigZag = function(value, offset) {
1150 return this.writeVarint32(ByteBuffer.zigZagEncode32(value), offset);
1151 };
1152
1153 /**
1154 * Reads a 32bit base 128 variable-length integer.
1155 * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by the number of bytes
1156 * written if omitted.
1157 * @returns {number|!{value: number, length: number}} The value read if offset is omitted, else the value read
1158 * and the actual number of bytes read.
1159 * @throws {Error} If it's not a valid varint. Has a property `truncated = true` if there is not enough data available
1160 * to fully decode the varint.
1161 * @expose
1162 */
1163 ByteBufferPrototype.readVarint32 = function(offset) {
1164 var relative = typeof offset === 'undefined';
1165 if (relative) offset = this.offset;
1166 if (!this.noAssert) {
1167 if (typeof offset !== 'number' || offset % 1 !== 0)
1168 throw TypeError("Illegal offset: "+offset+" (not an integer)");
1169 offset >>>= 0;
1170 if (offset < 0 || offset + 1 > this.buffer.byteLength)
1171 throw RangeError("Illegal offset: 0 <= "+offset+" (+"+1+") <= "+this.buffer.byteLength);
1172 }
1173 // ref: src/google/protobuf/io/coded_stream.cc
1174 var size = 0,
1175 value = 0 >>> 0,
1176 temp,
1177 ioffset;
1178 do {
1179 ioffset = offset+size;
1180 if (!this.noAssert && ioffset > this.limit) {
1181 var err = Error("Truncated");
1182 err['truncated'] = true;
1183 throw err;
1184 }
1185 temp = this.view.getUint8(ioffset);
1186 if (size < 5)
1187 value |= ((temp&0x7F)<<(7*size)) >>> 0;
1188 ++size;
1189 } while ((temp & 0x80) === 0x80);
1190 value = value | 0; // Make sure to discard the higher order bits
1191 if (relative) {
1192 this.offset += size;
1193 return value;
1194 }
1195 return {
1196 "value": value,
1197 "length": size
1198 };
1199 };
1200
1201 /**
1202 * Reads a zig-zag encoded 32bit base 128 variable-length integer.
1203 * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by the number of bytes
1204 * written if omitted.
1205 * @returns {number|!{value: number, length: number}} The value read if offset is omitted, else the value read
1206 * and the actual number of bytes read.
1207 * @throws {Error} If it's not a valid varint
1208 * @expose
1209 */
1210 ByteBufferPrototype.readVarint32ZigZag = function(offset) {
1211 var val = this.readVarint32(offset);
1212 if (typeof val === 'object')
1213 val["value"] = ByteBuffer.zigZagDecode32(val["value"]);
1214 else
1215 val = ByteBuffer.zigZagDecode32(val);
1216 return val;
1217 };
1218
1219 // types/varints/varint64
1220
1221 if (Long) {
1222
1223 /**
1224 * Maximum number of bytes required to store a 64bit base 128 variable-length integer.
1225 * @type {number}
1226 * @const
1227 * @expose
1228 */
1229 ByteBuffer.MAX_VARINT64_BYTES = 10;
1230
1231 /**
1232 * Calculates the actual number of bytes required to store a 64bit base 128 variable-length integer.
1233 * @param {number|!Long} value Value to encode
1234 * @returns {number} Number of bytes required. Capped to {@link ByteBuffer.MAX_VARINT64_BYTES}
1235 * @expose
1236 */
1237 ByteBuffer.calculateVarint64 = function(value) {
1238 if (typeof value === 'number')
1239 value = Long.fromNumber(value);
1240 // ref: src/google/protobuf/io/coded_stream.cc
1241 var part0 = value.toInt() >>> 0,
1242 part1 = value.shiftRightUnsigned(28).toInt() >>> 0,
1243 part2 = value.shiftRightUnsigned(56).toInt() >>> 0;
1244 if (part2 == 0) {
1245 if (part1 == 0) {
1246 if (part0 < 1 << 14)
1247 return part0 < 1 << 7 ? 1 : 2;
1248 else
1249 return part0 < 1 << 21 ? 3 : 4;
1250 } else {
1251 if (part1 < 1 << 14)
1252 return part1 < 1 << 7 ? 5 : 6;
1253 else
1254 return part1 < 1 << 21 ? 7 : 8;
1255 }
1256 } else
1257 return part2 < 1 << 7 ? 9 : 10;
1258 };
1259
1260 /**
1261 * Zigzag encodes a signed 64bit integer so that it can be effectively used with varint encoding.
1262 * @param {number|!Long} value Signed long
1263 * @returns {!Long} Unsigned zigzag encoded long
1264 * @expose
1265 */
1266 ByteBuffer.zigZagEncode64 = function(value) {
1267 if (typeof value === 'number')
1268 value = Long.fromNumber(value, false);
1269 else if (value.unsigned !== false) value = value.toSigned();
1270 // ref: src/google/protobuf/wire_format_lite.h
1271 return value.shiftLeft(1).xor(value.shiftRight(63)).toUnsigned();
1272 };
1273
1274 /**
1275 * Decodes a zigzag encoded signed 64bit integer.
1276 * @param {!Long|number} value Unsigned zigzag encoded long or JavaScript number
1277 * @returns {!Long} Signed long
1278 * @expose
1279 */
1280 ByteBuffer.zigZagDecode64 = function(value) {
1281 if (typeof value === 'number')
1282 value = Long.fromNumber(value, false);
1283 else if (value.unsigned !== false) value = value.toSigned();
1284 // ref: src/google/protobuf/wire_format_lite.h
1285 return value.shiftRightUnsigned(1).xor(value.and(Long.ONE).toSigned().negate()).toSigned();
1286 };
1287
1288 /**
1289 * Writes a 64bit base 128 variable-length integer.
1290 * @param {number|Long} value Value to write
1291 * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by the number of bytes
1292 * written if omitted.
1293 * @returns {!ByteBuffer|number} `this` if offset is omitted, else the actual number of bytes written.
1294 * @expose
1295 */
1296 ByteBufferPrototype.writeVarint64 = function(value, offset) {
1297 var relative = typeof offset === 'undefined';
1298 if (relative) offset = this.offset;
1299 if (!this.noAssert) {
1300 if (typeof value === 'number')
1301 value = Long.fromNumber(value);
1302 else if (!(value && value instanceof Long))
1303 throw TypeError("Illegal value: "+value+" (not an integer or Long)");
1304 if (typeof offset !== 'number' || offset % 1 !== 0)
1305 throw TypeError("Illegal offset: "+offset+" (not an integer)");
1306 offset >>>= 0;
1307 if (offset < 0 || offset + 0 > this.buffer.byteLength)
1308 throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength);
1309 }
1310 if (typeof value === 'number')
1311 value = Long.fromNumber(value, false);
1312 else if (value.unsigned !== false) value = value.toSigned();
1313 var size = ByteBuffer.calculateVarint64(value),
1314 part0 = value.toInt() >>> 0,
1315 part1 = value.shiftRightUnsigned(28).toInt() >>> 0,
1316 part2 = value.shiftRightUnsigned(56).toInt() >>> 0;
1317 offset += size;
1318 var capacity11 = this.buffer.byteLength;
1319 if (offset > capacity11)
1320 this.resize((capacity11 *= 2) > offset ? capacity11 : offset);
1321 offset -= size;
1322 switch (size) {
1323 case 10: this.view.setUint8(offset+9, (part2 >>> 7) & 0x01);
1324 case 9 : this.view.setUint8(offset+8, size !== 9 ? (part2 ) | 0x80 : (part2 ) & 0x7F);
1325 case 8 : this.view.setUint8(offset+7, size !== 8 ? (part1 >>> 21) | 0x80 : (part1 >>> 21) & 0x7F);
1326 case 7 : this.view.setUint8(offset+6, size !== 7 ? (part1 >>> 14) | 0x80 : (part1 >>> 14) & 0x7F);
1327 case 6 : this.view.setUint8(offset+5, size !== 6 ? (part1 >>> 7) | 0x80 : (part1 >>> 7) & 0x7F);
1328 case 5 : this.view.setUint8(offset+4, size !== 5 ? (part1 ) | 0x80 : (part1 ) & 0x7F);
1329 case 4 : this.view.setUint8(offset+3, size !== 4 ? (part0 >>> 21) | 0x80 : (part0 >>> 21) & 0x7F);
1330 case 3 : this.view.setUint8(offset+2, size !== 3 ? (part0 >>> 14) | 0x80 : (part0 >>> 14) & 0x7F);
1331 case 2 : this.view.setUint8(offset+1, size !== 2 ? (part0 >>> 7) | 0x80 : (part0 >>> 7) & 0x7F);
1332 case 1 : this.view.setUint8(offset , size !== 1 ? (part0 ) | 0x80 : (part0 ) & 0x7F);
1333 }
1334 if (relative) {
1335 this.offset += size;
1336 return this;
1337 } else {
1338 return size;
1339 }
1340 };
1341
1342 /**
1343 * Writes a zig-zag encoded 64bit base 128 variable-length integer.
1344 * @param {number|Long} value Value to write
1345 * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by the number of bytes
1346 * written if omitted.
1347 * @returns {!ByteBuffer|number} `this` if offset is omitted, else the actual number of bytes written.
1348 * @expose
1349 */
1350 ByteBufferPrototype.writeVarint64ZigZag = function(value, offset) {
1351 return this.writeVarint64(ByteBuffer.zigZagEncode64(value), offset);
1352 };
1353
1354 /**
1355 * Reads a 64bit base 128 variable-length integer. Requires Long.js.
1356 * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by the number of bytes
1357 * read if omitted.
1358 * @returns {!Long|!{value: Long, length: number}} The value read if offset is omitted, else the value read and
1359 * the actual number of bytes read.
1360 * @throws {Error} If it's not a valid varint
1361 * @expose
1362 */
1363 ByteBufferPrototype.readVarint64 = function(offset) {
1364 var relative = typeof offset === 'undefined';
1365 if (relative) offset = this.offset;
1366 if (!this.noAssert) {
1367 if (typeof offset !== 'number' || offset % 1 !== 0)
1368 throw TypeError("Illegal offset: "+offset+" (not an integer)");
1369 offset >>>= 0;
1370 if (offset < 0 || offset + 1 > this.buffer.byteLength)
1371 throw RangeError("Illegal offset: 0 <= "+offset+" (+"+1+") <= "+this.buffer.byteLength);
1372 }
1373 // ref: src/google/protobuf/io/coded_stream.cc
1374 var start = offset,
1375 part0 = 0,
1376 part1 = 0,
1377 part2 = 0,
1378 b = 0;
1379 b = this.view.getUint8(offset++); part0 = (b & 0x7F) ; if (b & 0x80) {
1380 b = this.view.getUint8(offset++); part0 |= (b & 0x7F) << 7; if (b & 0x80) {
1381 b = this.view.getUint8(offset++); part0 |= (b & 0x7F) << 14; if (b & 0x80) {
1382 b = this.view.getUint8(offset++); part0 |= (b & 0x7F) << 21; if (b & 0x80) {
1383 b = this.view.getUint8(offset++); part1 = (b & 0x7F) ; if (b & 0x80) {
1384 b = this.view.getUint8(offset++); part1 |= (b & 0x7F) << 7; if (b & 0x80) {
1385 b = this.view.getUint8(offset++); part1 |= (b & 0x7F) << 14; if (b & 0x80) {
1386 b = this.view.getUint8(offset++); part1 |= (b & 0x7F) << 21; if (b & 0x80) {
1387 b = this.view.getUint8(offset++); part2 = (b & 0x7F) ; if (b & 0x80) {
1388 b = this.view.getUint8(offset++); part2 |= (b & 0x7F) << 7; if (b & 0x80) {
1389 throw Error("Buffer overrun"); }}}}}}}}}}
1390 var value = Long.fromBits(part0 | (part1 << 28), (part1 >>> 4) | (part2) << 24, false);
1391 if (relative) {
1392 this.offset = offset;
1393 return value;
1394 } else {
1395 return {
1396 'value': value,
1397 'length': offset-start
1398 };
1399 }
1400 };
1401
1402 /**
1403 * Reads a zig-zag encoded 64bit base 128 variable-length integer. Requires Long.js.
1404 * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by the number of bytes
1405 * read if omitted.
1406 * @returns {!Long|!{value: Long, length: number}} The value read if offset is omitted, else the value read and
1407 * the actual number of bytes read.
1408 * @throws {Error} If it's not a valid varint
1409 * @expose
1410 */
1411 ByteBufferPrototype.readVarint64ZigZag = function(offset) {
1412 var val = this.readVarint64(offset);
1413 if (val && val['value'] instanceof Long)
1414 val["value"] = ByteBuffer.zigZagDecode64(val["value"]);
1415 else
1416 val = ByteBuffer.zigZagDecode64(val);
1417 return val;
1418 };
1419
1420 } // Long
1421
1422
1423 // types/strings/cstring
1424
1425 /**
1426 * Writes a NULL-terminated UTF8 encoded string. For this to work the specified string must not contain any NULL
1427 * characters itself.
1428 * @param {string} str String to write
1429 * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by the number of bytes
1430 * contained in `str` + 1 if omitted.
1431 * @returns {!ByteBuffer|number} this if offset is omitted, else the actual number of bytes written
1432 * @expose
1433 */
1434 ByteBufferPrototype.writeCString = function(str, offset) {
1435 var relative = typeof offset === 'undefined';
1436 if (relative) offset = this.offset;
1437 var i,
1438 k = str.length;
1439 if (!this.noAssert) {
1440 if (typeof str !== 'string')
1441 throw TypeError("Illegal str: Not a string");
1442 for (i=0; i<k; ++i) {
1443 if (str.charCodeAt(i) === 0)
1444 throw RangeError("Illegal str: Contains NULL-characters");
1445 }
1446 if (typeof offset !== 'number' || offset % 1 !== 0)
1447 throw TypeError("Illegal offset: "+offset+" (not an integer)");
1448 offset >>>= 0;
1449 if (offset < 0 || offset + 0 > this.buffer.byteLength)
1450 throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength);
1451 }
1452 var start = offset;
1453 // UTF8 strings do not contain zero bytes in between except for the zero character, so:
1454 k = utfx.calculateUTF16asUTF8(stringSource(str))[1];
1455 offset += k+1;
1456 var capacity12 = this.buffer.byteLength;
1457 if (offset > capacity12)
1458 this.resize((capacity12 *= 2) > offset ? capacity12 : offset);
1459 offset -= k+1;
1460 utfx.encodeUTF16toUTF8(stringSource(str), function(b) {
1461 this.view.setUint8(offset++, b);
1462 }.bind(this));
1463 this.view.setUint8(offset++, 0);
1464 if (relative) {
1465 this.offset = offset - start;
1466 return this;
1467 }
1468 return k;
1469 };
1470
1471 /**
1472 * Reads a NULL-terminated UTF8 encoded string. For this to work the string read must not contain any NULL characters
1473 * itself.
1474 * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by the number of bytes
1475 * read if omitted.
1476 * @returns {string|!{string: string, length: number}} The string read if offset is omitted, else the string
1477 * read and the actual number of bytes read.
1478 * @expose
1479 */
1480 ByteBufferPrototype.readCString = function(offset) {
1481 var relative = typeof offset === 'undefined';
1482 if (relative) offset = this.offset;
1483 if (!this.noAssert) {
1484 if (typeof offset !== 'number' || offset % 1 !== 0)
1485 throw TypeError("Illegal offset: "+offset+" (not an integer)");
1486 offset >>>= 0;
1487 if (offset < 0 || offset + 1 > this.buffer.byteLength)
1488 throw RangeError("Illegal offset: 0 <= "+offset+" (+"+1+") <= "+this.buffer.byteLength);
1489 }
1490 var start = offset,
1491 temp;
1492 // UTF8 strings do not contain zero bytes in between except for the zero character itself, so:
1493 var sd, b = -1;
1494 utfx.decodeUTF8toUTF16(function() {
1495 if (b === 0) return null;
1496 if (offset >= this.limit)
1497 throw RangeError("Illegal range: Truncated data, "+offset+" < "+this.limit);
1498 return (b = this.view.getUint8(offset++)) === 0 ? null : b;
1499 }.bind(this), sd = stringDestination(), true);
1500 if (relative) {
1501 this.offset = offset;
1502 return sd();
1503 } else {
1504 return {
1505 "string": sd(),
1506 "length": offset - start
1507 };
1508 }
1509 };
1510
1511 // types/strings/istring
1512
1513 /**
1514 * Writes a length as uint32 prefixed UTF8 encoded string.
1515 * @param {string} str String to write
1516 * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by the number of bytes
1517 * written if omitted.
1518 * @returns {!ByteBuffer|number} `this` if `offset` is omitted, else the actual number of bytes written
1519 * @expose
1520 * @see ByteBuffer#writeVarint32
1521 */
1522 ByteBufferPrototype.writeIString = function(str, offset) {
1523 var relative = typeof offset === 'undefined';
1524 if (relative) offset = this.offset;
1525 if (!this.noAssert) {
1526 if (typeof str !== 'string')
1527 throw TypeError("Illegal str: Not a string");
1528 if (typeof offset !== 'number' || offset % 1 !== 0)
1529 throw TypeError("Illegal offset: "+offset+" (not an integer)");
1530 offset >>>= 0;
1531 if (offset < 0 || offset + 0 > this.buffer.byteLength)
1532 throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength);
1533 }
1534 var start = offset,
1535 k;
1536 k = utfx.calculateUTF16asUTF8(stringSource(str), this.noAssert)[1];
1537 offset += 4+k;
1538 var capacity13 = this.buffer.byteLength;
1539 if (offset > capacity13)
1540 this.resize((capacity13 *= 2) > offset ? capacity13 : offset);
1541 offset -= 4+k;
1542 this.view.setUint32(offset, k, this.littleEndian);
1543 offset += 4;
1544 utfx.encodeUTF16toUTF8(stringSource(str), function(b) {
1545 this.view.setUint8(offset++, b);
1546 }.bind(this));
1547 if (offset !== start + 4 + k)
1548 throw RangeError("Illegal range: Truncated data, "+offset+" == "+(offset+4+k));
1549 if (relative) {
1550 this.offset = offset;
1551 return this;
1552 }
1553 return offset - start;
1554 };
1555
1556 /**
1557 * Reads a length as uint32 prefixed UTF8 encoded string.
1558 * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by the number of bytes
1559 * read if omitted.
1560 * @returns {string|!{string: string, length: number}} The string read if offset is omitted, else the string
1561 * read and the actual number of bytes read.
1562 * @expose
1563 * @see ByteBuffer#readVarint32
1564 */
1565 ByteBufferPrototype.readIString = function(offset) {
1566 var relative = typeof offset === 'undefined';
1567 if (relative) offset = this.offset;
1568 if (!this.noAssert) {
1569 if (typeof offset !== 'number' || offset % 1 !== 0)
1570 throw TypeError("Illegal offset: "+offset+" (not an integer)");
1571 offset >>>= 0;
1572 if (offset < 0 || offset + 4 > this.buffer.byteLength)
1573 throw RangeError("Illegal offset: 0 <= "+offset+" (+"+4+") <= "+this.buffer.byteLength);
1574 }
1575 var temp = 0,
1576 start = offset,
1577 str;
1578 temp = this.view.getUint32(offset, this.littleEndian);
1579 offset += 4;
1580 var k = offset + temp,
1581 sd;
1582 utfx.decodeUTF8toUTF16(function() {
1583 return offset < k ? this.view.getUint8(offset++) : null;
1584 }.bind(this), sd = stringDestination(), this.noAssert);
1585 str = sd();
1586 if (relative) {
1587 this.offset = offset;
1588 return str;
1589 } else {
1590 return {
1591 'string': str,
1592 'length': offset - start
1593 };
1594 }
1595 };
1596
1597 // types/strings/utf8string
1598
1599 /**
1600 * Metrics representing number of UTF8 characters. Evaluates to `c`.
1601 * @type {string}
1602 * @const
1603 * @expose
1604 */
1605 ByteBuffer.METRICS_CHARS = 'c';
1606
1607 /**
1608 * Metrics representing number of bytes. Evaluates to `b`.
1609 * @type {string}
1610 * @const
1611 * @expose
1612 */
1613 ByteBuffer.METRICS_BYTES = 'b';
1614
1615 /**
1616 * Writes an UTF8 encoded string.
1617 * @param {string} str String to write
1618 * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} if omitted.
1619 * @returns {!ByteBuffer|number} this if offset is omitted, else the actual number of bytes written.
1620 * @expose
1621 */
1622 ByteBufferPrototype.writeUTF8String = function(str, offset) {
1623 var relative = typeof offset === 'undefined';
1624 if (relative) offset = this.offset;
1625 if (!this.noAssert) {
1626 if (typeof offset !== 'number' || offset % 1 !== 0)
1627 throw TypeError("Illegal offset: "+offset+" (not an integer)");
1628 offset >>>= 0;
1629 if (offset < 0 || offset + 0 > this.buffer.byteLength)
1630 throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength);
1631 }
1632 var k;
1633 var start = offset;
1634 k = utfx.calculateUTF16asUTF8(stringSource(str))[1];
1635 offset += k;
1636 var capacity14 = this.buffer.byteLength;
1637 if (offset > capacity14)
1638 this.resize((capacity14 *= 2) > offset ? capacity14 : offset);
1639 offset -= k;
1640 utfx.encodeUTF16toUTF8(stringSource(str), function(b) {
1641 this.view.setUint8(offset++, b);
1642 }.bind(this));
1643 if (relative) {
1644 this.offset = offset;
1645 return this;
1646 }
1647 return offset - start;
1648 };
1649
1650 /**
1651 * Writes an UTF8 encoded string. This is an alias of {@link ByteBuffer#writeUTF8String}.
1652 * @function
1653 * @param {string} str String to write
1654 * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} if omitted.
1655 * @returns {!ByteBuffer|number} this if offset is omitted, else the actual number of bytes written.
1656 * @expose
1657 */
1658 ByteBufferPrototype.writeString = ByteBufferPrototype.writeUTF8String;
1659
1660 /**
1661 * Calculates the number of UTF8 characters of a string. JavaScript itself uses UTF-16, so that a string's
1662 * `length` property does not reflect its actual UTF8 size if it contains code points larger than 0xFFFF.
1663 * @function
1664 * @param {string} str String to calculate
1665 * @returns {number} Number of UTF8 characters
1666 * @expose
1667 */
1668 ByteBuffer.calculateUTF8Chars = function(str) {
1669 return utfx.calculateUTF16asUTF8(stringSource(str))[0];
1670 };
1671
1672 /**
1673 * Calculates the number of UTF8 bytes of a string.
1674 * @function
1675 * @param {string} str String to calculate
1676 * @returns {number} Number of UTF8 bytes
1677 * @expose
1678 */
1679 ByteBuffer.calculateUTF8Bytes = function(str) {
1680 return utfx.calculateUTF16asUTF8(stringSource(str))[1];
1681 };
1682
1683 /**
1684 * Reads an UTF8 encoded string.
1685 * @param {number} length Number of characters or bytes to read.
1686 * @param {string=} metrics Metrics specifying what `length` is meant to count. Defaults to
1687 * {@link ByteBuffer.METRICS_CHARS}.
1688 * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by the number of bytes
1689 * read if omitted.
1690 * @returns {string|!{string: string, length: number}} The string read if offset is omitted, else the string
1691 * read and the actual number of bytes read.
1692 * @expose
1693 */
1694 ByteBufferPrototype.readUTF8String = function(length, metrics, offset) {
1695 if (typeof metrics === 'number') {
1696 offset = metrics;
1697 metrics = undefined;
1698 }
1699 var relative = typeof offset === 'undefined';
1700 if (relative) offset = this.offset;
1701 if (typeof metrics === 'undefined') metrics = ByteBuffer.METRICS_CHARS;
1702 if (!this.noAssert) {
1703 if (typeof length !== 'number' || length % 1 !== 0)
1704 throw TypeError("Illegal length: "+length+" (not an integer)");
1705 length |= 0;
1706 if (typeof offset !== 'number' || offset % 1 !== 0)
1707 throw TypeError("Illegal offset: "+offset+" (not an integer)");
1708 offset >>>= 0;
1709 if (offset < 0 || offset + 0 > this.buffer.byteLength)
1710 throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength);
1711 }
1712 var i = 0,
1713 start = offset,
1714 sd;
1715 if (metrics === ByteBuffer.METRICS_CHARS) { // The same for node and the browser
1716 sd = stringDestination();
1717 utfx.decodeUTF8(function() {
1718 return i < length && offset < this.limit ? this.view.getUint8(offset++) : null;
1719 }.bind(this), function(cp) {
1720 ++i; utfx.UTF8toUTF16(cp, sd);
1721 }.bind(this));
1722 if (i !== length)
1723 throw RangeError("Illegal range: Truncated data, "+i+" == "+length);
1724 if (relative) {
1725 this.offset = offset;
1726 return sd();
1727 } else {
1728 return {
1729 "string": sd(),
1730 "length": offset - start
1731 };
1732 }
1733 } else if (metrics === ByteBuffer.METRICS_BYTES) {
1734 if (!this.noAssert) {
1735 if (typeof offset !== 'number' || offset % 1 !== 0)
1736 throw TypeError("Illegal offset: "+offset+" (not an integer)");
1737 offset >>>= 0;
1738 if (offset < 0 || offset + length > this.buffer.byteLength)
1739 throw RangeError("Illegal offset: 0 <= "+offset+" (+"+length+") <= "+this.buffer.byteLength);
1740 }
1741 var k = offset + length;
1742 utfx.decodeUTF8toUTF16(function() {
1743 return offset < k ? this.view.getUint8(offset++) : null;
1744 }.bind(this), sd = stringDestination(), this.noAssert);
1745 if (offset !== k)
1746 throw RangeError("Illegal range: Truncated data, "+offset+" == "+k);
1747 if (relative) {
1748 this.offset = offset;
1749 return sd();
1750 } else {
1751 return {
1752 'string': sd(),
1753 'length': offset - start
1754 };
1755 }
1756 } else
1757 throw TypeError("Unsupported metrics: "+metrics);
1758 };
1759
1760 /**
1761 * Reads an UTF8 encoded string. This is an alias of {@link ByteBuffer#readUTF8String}.
1762 * @function
1763 * @param {number} length Number of characters or bytes to read
1764 * @param {number=} metrics Metrics specifying what `n` is meant to count. Defaults to
1765 * {@link ByteBuffer.METRICS_CHARS}.
1766 * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by the number of bytes
1767 * read if omitted.
1768 * @returns {string|!{string: string, length: number}} The string read if offset is omitted, else the string
1769 * read and the actual number of bytes read.
1770 * @expose
1771 */
1772 ByteBufferPrototype.readString = ByteBufferPrototype.readUTF8String;
1773
1774 // types/strings/vstring
1775
1776 /**
1777 * Writes a length as varint32 prefixed UTF8 encoded string.
1778 * @param {string} str String to write
1779 * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by the number of bytes
1780 * written if omitted.
1781 * @returns {!ByteBuffer|number} `this` if `offset` is omitted, else the actual number of bytes written
1782 * @expose
1783 * @see ByteBuffer#writeVarint32
1784 */
1785 ByteBufferPrototype.writeVString = function(str, offset) {
1786 var relative = typeof offset === 'undefined';
1787 if (relative) offset = this.offset;
1788 if (!this.noAssert) {
1789 if (typeof str !== 'string')
1790 throw TypeError("Illegal str: Not a string");
1791 if (typeof offset !== 'number' || offset % 1 !== 0)
1792 throw TypeError("Illegal offset: "+offset+" (not an integer)");
1793 offset >>>= 0;
1794 if (offset < 0 || offset + 0 > this.buffer.byteLength)
1795 throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength);
1796 }
1797 var start = offset,
1798 k, l;
1799 k = utfx.calculateUTF16asUTF8(stringSource(str), this.noAssert)[1];
1800 l = ByteBuffer.calculateVarint32(k);
1801 offset += l+k;
1802 var capacity15 = this.buffer.byteLength;
1803 if (offset > capacity15)
1804 this.resize((capacity15 *= 2) > offset ? capacity15 : offset);
1805 offset -= l+k;
1806 offset += this.writeVarint32(k, offset);
1807 utfx.encodeUTF16toUTF8(stringSource(str), function(b) {
1808 this.view.setUint8(offset++, b);
1809 }.bind(this));
1810 if (offset !== start+k+l)
1811 throw RangeError("Illegal range: Truncated data, "+offset+" == "+(offset+k+l));
1812 if (relative) {
1813 this.offset = offset;
1814 return this;
1815 }
1816 return offset - start;
1817 };
1818
1819 /**
1820 * Reads a length as varint32 prefixed UTF8 encoded string.
1821 * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by the number of bytes
1822 * read if omitted.
1823 * @returns {string|!{string: string, length: number}} The string read if offset is omitted, else the string
1824 * read and the actual number of bytes read.
1825 * @expose
1826 * @see ByteBuffer#readVarint32
1827 */
1828 ByteBufferPrototype.readVString = function(offset) {
1829 var relative = typeof offset === 'undefined';
1830 if (relative) offset = this.offset;
1831 if (!this.noAssert) {
1832 if (typeof offset !== 'number' || offset % 1 !== 0)
1833 throw TypeError("Illegal offset: "+offset+" (not an integer)");
1834 offset >>>= 0;
1835 if (offset < 0 || offset + 1 > this.buffer.byteLength)
1836 throw RangeError("Illegal offset: 0 <= "+offset+" (+"+1+") <= "+this.buffer.byteLength);
1837 }
1838 var temp = this.readVarint32(offset),
1839 start = offset,
1840 str;
1841 offset += temp['length'];
1842 temp = temp['value'];
1843 var k = offset + temp,
1844 sd = stringDestination();
1845 utfx.decodeUTF8toUTF16(function() {
1846 return offset < k ? this.view.getUint8(offset++) : null;
1847 }.bind(this), sd, this.noAssert);
1848 str = sd();
1849 if (relative) {
1850 this.offset = offset;
1851 return str;
1852 } else {
1853 return {
1854 'string': str,
1855 'length': offset - start
1856 };
1857 }
1858 };
1859
1860
1861 /**
1862 * Appends some data to this ByteBuffer. This will overwrite any contents behind the specified offset up to the appended
1863 * data's length.
1864 * @param {!ByteBuffer|!ArrayBuffer|!Uint8Array|string} source Data to append. If `source` is a ByteBuffer, its offsets
1865 * will be modified according to the performed read operation.
1866 * @param {(string|number)=} encoding Encoding if `data` is a string ("base64", "hex", "binary", defaults to "utf8")
1867 * @param {number=} offset Offset to append at. Will use and increase {@link ByteBuffer#offset} by the number of bytes
1868 * read if omitted.
1869 * @returns {!ByteBuffer} this
1870 * @expose
1871 * @example A relative `<01 02>03.append(<04 05>)` will result in `<01 02 04 05>, 04 05|`
1872 * @example An absolute `<01 02>03.append(04 05>, 1)` will result in `<01 04>05, 04 05|`
1873 */
1874 ByteBufferPrototype.append = function(source, encoding, offset) {
1875 if (typeof encoding === 'number' || typeof encoding !== 'string') {
1876 offset = encoding;
1877 encoding = undefined;
1878 }
1879 var relative = typeof offset === 'undefined';
1880 if (relative) offset = this.offset;
1881 if (!this.noAssert) {
1882 if (typeof offset !== 'number' || offset % 1 !== 0)
1883 throw TypeError("Illegal offset: "+offset+" (not an integer)");
1884 offset >>>= 0;
1885 if (offset < 0 || offset + 0 > this.buffer.byteLength)
1886 throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength);
1887 }
1888 if (!(source instanceof ByteBuffer))
1889 source = ByteBuffer.wrap(source, encoding);
1890 var length = source.limit - source.offset;
1891 if (length <= 0) return this; // Nothing to append
1892 offset += length;
1893 var capacity16 = this.buffer.byteLength;
1894 if (offset > capacity16)
1895 this.resize((capacity16 *= 2) > offset ? capacity16 : offset);
1896 offset -= length;
1897 new Uint8Array(this.buffer, offset).set(new Uint8Array(source.buffer).subarray(source.offset, source.limit));
1898 source.offset += length;
1899 if (relative) this.offset += length;
1900 return this;
1901 };
1902
1903 /**
1904 * Appends this ByteBuffer's contents to another ByteBuffer. This will overwrite any contents behind the specified
1905 * offset up to the length of this ByteBuffer's data.
1906 * @param {!ByteBuffer} target Target ByteBuffer
1907 * @param {number=} offset Offset to append to. Will use and increase {@link ByteBuffer#offset} by the number of bytes
1908 * read if omitted.
1909 * @returns {!ByteBuffer} this
1910 * @expose
1911 * @see ByteBuffer#append
1912 */
1913 ByteBufferPrototype.appendTo = function(target, offset) {
1914 target.append(this, offset);
1915 return this;
1916 };
1917
1918 /**
1919 * Enables or disables assertions of argument types and offsets. Assertions are enabled by default but you can opt to
1920 * disable them if your code already makes sure that everything is valid.
1921 * @param {boolean} assert `true` to enable assertions, otherwise `false`
1922 * @returns {!ByteBuffer} this
1923 * @expose
1924 */
1925 ByteBufferPrototype.assert = function(assert) {
1926 this.noAssert = !assert;
1927 return this;
1928 };
1929
1930 /**
1931 * Gets the capacity of this ByteBuffer's backing buffer.
1932 * @returns {number} Capacity of the backing buffer
1933 * @expose
1934 */
1935 ByteBufferPrototype.capacity = function() {
1936 return this.buffer.byteLength;
1937 };
1938
1939 /**
1940 * Clears this ByteBuffer's offsets by setting {@link ByteBuffer#offset} to `0` and {@link ByteBuffer#limit} to the
1941 * backing buffer's capacity. Discards {@link ByteBuffer#markedOffset}.
1942 * @returns {!ByteBuffer} this
1943 * @expose
1944 */
1945 ByteBufferPrototype.clear = function() {
1946 this.offset = 0;
1947 this.limit = this.buffer.byteLength;
1948 this.markedOffset = -1;
1949 return this;
1950 };
1951
1952 /**
1953 * Creates a cloned instance of this ByteBuffer, preset with this ByteBuffer's values for {@link ByteBuffer#offset},
1954 * {@link ByteBuffer#markedOffset} and {@link ByteBuffer#limit}.
1955 * @param {boolean=} copy Whether to copy the backing buffer or to return another view on the same, defaults to `false`
1956 * @returns {!ByteBuffer} Cloned instance
1957 * @expose
1958 */
1959 ByteBufferPrototype.clone = function(copy) {
1960 var bb = new ByteBuffer(0, this.littleEndian, this.noAssert);
1961 if (copy) {
1962 var buffer = new ArrayBuffer(this.buffer.byteLength);
1963 new Uint8Array(buffer).set(this.buffer);
1964 bb.buffer = buffer;
1965 bb.view = new DataView(buffer);
1966 } else {
1967 bb.buffer = this.buffer;
1968 bb.view = this.view;
1969 }
1970 bb.offset = this.offset;
1971 bb.markedOffset = this.markedOffset;
1972 bb.limit = this.limit;
1973 return bb;
1974 };
1975
1976 /**
1977 * Compacts this ByteBuffer to be backed by a {@link ByteBuffer#buffer} of its contents' length. Contents are the bytes
1978 * between {@link ByteBuffer#offset} and {@link ByteBuffer#limit}. Will set `offset = 0` and `limit = capacity` and
1979 * adapt {@link ByteBuffer#markedOffset} to the same relative position if set.
1980 * @param {number=} begin Offset to start at, defaults to {@link ByteBuffer#offset}
1981 * @param {number=} end Offset to end at, defaults to {@link ByteBuffer#limit}
1982 * @returns {!ByteBuffer} this
1983 * @expose
1984 */
1985 ByteBufferPrototype.compact = function(begin, end) {
1986 if (typeof begin === 'undefined') begin = this.offset;
1987 if (typeof end === 'undefined') end = this.limit;
1988 if (!this.noAssert) {
1989 if (typeof begin !== 'number' || begin % 1 !== 0)
1990 throw TypeError("Illegal begin: Not an integer");
1991 begin >>>= 0;
1992 if (typeof end !== 'number' || end % 1 !== 0)
1993 throw TypeError("Illegal end: Not an integer");
1994 end >>>= 0;
1995 if (begin < 0 || begin > end || end > this.buffer.byteLength)
1996 throw RangeError("Illegal range: 0 <= "+begin+" <= "+end+" <= "+this.buffer.byteLength);
1997 }
1998 if (begin === 0 && end === this.buffer.byteLength)
1999 return this; // Already compacted
2000 var len = end - begin;
2001 if (len === 0) {
2002 this.buffer = EMPTY_BUFFER;
2003 this.view = null;
2004 if (this.markedOffset >= 0) this.markedOffset -= begin;
2005 this.offset = 0;
2006 this.limit = 0;
2007 return this;
2008 }
2009 var buffer = new ArrayBuffer(len);
2010 new Uint8Array(buffer).set(new Uint8Array(this.buffer).subarray(begin, end));
2011 this.buffer = buffer;
2012 this.view = new DataView(buffer);
2013 if (this.markedOffset >= 0) this.markedOffset -= begin;
2014 this.offset = 0;
2015 this.limit = len;
2016 return this;
2017 };
2018
2019 /**
2020 * Creates a copy of this ByteBuffer's contents. Contents are the bytes between {@link ByteBuffer#offset} and
2021 * {@link ByteBuffer#limit}.
2022 * @param {number=} begin Begin offset, defaults to {@link ByteBuffer#offset}.
2023 * @param {number=} end End offset, defaults to {@link ByteBuffer#limit}.
2024 * @returns {!ByteBuffer} Copy
2025 * @expose
2026 */
2027 ByteBufferPrototype.copy = function(begin, end) {
2028 if (typeof begin === 'undefined') begin = this.offset;
2029 if (typeof end === 'undefined') end = this.limit;
2030 if (!this.noAssert) {
2031 if (typeof begin !== 'number' || begin % 1 !== 0)
2032 throw TypeError("Illegal begin: Not an integer");
2033 begin >>>= 0;
2034 if (typeof end !== 'number' || end % 1 !== 0)
2035 throw TypeError("Illegal end: Not an integer");
2036 end >>>= 0;
2037 if (begin < 0 || begin > end || end > this.buffer.byteLength)
2038 throw RangeError("Illegal range: 0 <= "+begin+" <= "+end+" <= "+this.buffer.byteLength);
2039 }
2040 if (begin === end)
2041 return new ByteBuffer(0, this.littleEndian, this.noAssert);
2042 var capacity = end - begin,
2043 bb = new ByteBuffer(capacity, this.littleEndian, this.noAssert);
2044 bb.offset = 0;
2045 bb.limit = capacity;
2046 if (bb.markedOffset >= 0) bb.markedOffset -= begin;
2047 this.copyTo(bb, 0, begin, end);
2048 return bb;
2049 };
2050
2051 /**
2052 * Copies this ByteBuffer's contents to another ByteBuffer. Contents are the bytes between {@link ByteBuffer#offset} and
2053 * {@link ByteBuffer#limit}.
2054 * @param {!ByteBuffer} target Target ByteBuffer
2055 * @param {number=} targetOffset Offset to copy to. Will use and increase the target's {@link ByteBuffer#offset}
2056 * by the number of bytes copied if omitted.
2057 * @param {number=} sourceOffset Offset to start copying from. Will use and increase {@link ByteBuffer#offset} by the
2058 * number of bytes copied if omitted.
2059 * @param {number=} sourceLimit Offset to end copying from, defaults to {@link ByteBuffer#limit}
2060 * @returns {!ByteBuffer} this
2061 * @expose
2062 */
2063 ByteBufferPrototype.copyTo = function(target, targetOffset, sourceOffset, sourceLimit) {
2064 var relative,
2065 targetRelative;
2066 if (!this.noAssert) {
2067 if (!ByteBuffer.isByteBuffer(target))
2068 throw TypeError("Illegal target: Not a ByteBuffer");
2069 }
2070 targetOffset = (targetRelative = typeof targetOffset === 'undefined') ? target.offset : targetOffset | 0;
2071 sourceOffset = (relative = typeof sourceOffset === 'undefined') ? this.offset : sourceOffset | 0;
2072 sourceLimit = typeof sourceLimit === 'undefined' ? this.limit : sourceLimit | 0;
2073
2074 if (targetOffset < 0 || targetOffset > target.buffer.byteLength)
2075 throw RangeError("Illegal target range: 0 <= "+targetOffset+" <= "+target.buffer.byteLength);
2076 if (sourceOffset < 0 || sourceLimit > this.buffer.byteLength)
2077 throw RangeError("Illegal source range: 0 <= "+sourceOffset+" <= "+this.buffer.byteLength);
2078
2079 var len = sourceLimit - sourceOffset;
2080 if (len === 0)
2081 return target; // Nothing to copy
2082
2083 target.ensureCapacity(targetOffset + len);
2084
2085 new Uint8Array(target.buffer).set(new Uint8Array(this.buffer).subarray(sourceOffset, sourceLimit), targetOffset);
2086
2087 if (relative) this.offset += len;
2088 if (targetRelative) target.offset += len;
2089
2090 return this;
2091 };
2092
2093 /**
2094 * Makes sure that this ByteBuffer is backed by a {@link ByteBuffer#buffer} of at least the specified capacity. If the
2095 * current capacity is exceeded, it will be doubled. If double the current capacity is less than the required capacity,
2096 * the required capacity will be used instead.
2097 * @param {number} capacity Required capacity
2098 * @returns {!ByteBuffer} this
2099 * @expose
2100 */
2101 ByteBufferPrototype.ensureCapacity = function(capacity) {
2102 var current = this.buffer.byteLength;
2103 if (current < capacity)
2104 return this.resize((current *= 2) > capacity ? current : capacity);
2105 return this;
2106 };
2107
2108 /**
2109 * Overwrites this ByteBuffer's contents with the specified value. Contents are the bytes between
2110 * {@link ByteBuffer#offset} and {@link ByteBuffer#limit}.
2111 * @param {number|string} value Byte value to fill with. If given as a string, the first character is used.
2112 * @param {number=} begin Begin offset. Will use and increase {@link ByteBuffer#offset} by the number of bytes
2113 * written if omitted. defaults to {@link ByteBuffer#offset}.
2114 * @param {number=} end End offset, defaults to {@link ByteBuffer#limit}.
2115 * @returns {!ByteBuffer} this
2116 * @expose
2117 * @example `someByteBuffer.clear().fill(0)` fills the entire backing buffer with zeroes
2118 */
2119 ByteBufferPrototype.fill = function(value, begin, end) {
2120 var relative = typeof begin === 'undefined';
2121 if (relative) begin = this.offset;
2122 if (typeof value === 'string' && value.length > 0)
2123 value = value.charCodeAt(0);
2124 if (typeof begin === 'undefined') begin = this.offset;
2125 if (typeof end === 'undefined') end = this.limit;
2126 if (!this.noAssert) {
2127 if (typeof value !== 'number' || value % 1 !== 0)
2128 throw TypeError("Illegal value: "+value+" (not an integer)");
2129 value |= 0;
2130 if (typeof begin !== 'number' || begin % 1 !== 0)
2131 throw TypeError("Illegal begin: Not an integer");
2132 begin >>>= 0;
2133 if (typeof end !== 'number' || end % 1 !== 0)
2134 throw TypeError("Illegal end: Not an integer");
2135 end >>>= 0;
2136 if (begin < 0 || begin > end || end > this.buffer.byteLength)
2137 throw RangeError("Illegal range: 0 <= "+begin+" <= "+end+" <= "+this.buffer.byteLength);
2138 }
2139 if (begin >= end) return this; // Nothing to fill
2140 while (begin < end) this.view.setUint8(begin++, value);
2141 if (relative) {
2142 this.offset = begin;
2143 }
2144 return this;
2145 };
2146
2147 /**
2148 * Makes this ByteBuffer ready for a new sequence of write or relative read operations. Sets `limit = offset` and
2149 * `offset = 0`. Make sure always to flip a ByteBuffer when all relative read or write operations are complete.
2150 * @returns {!ByteBuffer} this
2151 * @expose
2152 */
2153 ByteBufferPrototype.flip = function() {
2154 this.limit = this.offset;
2155 this.offset = 0;
2156 return this;
2157 };
2158 /**
2159 * Marks an offset on this ByteBuffer to be used later.
2160 * @param {number=} offset Offset to mark. Defaults to {@link ByteBuffer#offset}.
2161 * @returns {!ByteBuffer} this
2162 * @throws {TypeError} If `offset` is not a valid number
2163 * @throws {RangeError} If `offset` is out of bounds
2164 * @see ByteBuffer#reset
2165 * @expose
2166 */
2167 ByteBufferPrototype.mark = function(offset) {
2168 offset = typeof offset === 'undefined' ? this.offset : offset;
2169 if (!this.noAssert) {
2170 if (typeof offset !== 'number' || offset % 1 !== 0)
2171 throw TypeError("Illegal offset: "+offset+" (not an integer)");
2172 offset >>>= 0;
2173 if (offset < 0 || offset + 0 > this.buffer.byteLength)
2174 throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength);
2175 }
2176 this.markedOffset = offset;
2177 return this;
2178 };
2179 /**
2180 * Sets the byte order.
2181 * @param {boolean} littleEndian `true` for little endian byte order, `false` for big endian
2182 * @returns {!ByteBuffer} this
2183 * @expose
2184 */
2185 ByteBufferPrototype.order = function(littleEndian) {
2186 if (!this.noAssert) {
2187 if (typeof littleEndian !== 'boolean')
2188 throw TypeError("Illegal littleEndian: Not a boolean");
2189 }
2190 this.littleEndian = !!littleEndian;
2191 return this;
2192 };
2193
2194 /**
2195 * Switches (to) little endian byte order.
2196 * @param {boolean=} littleEndian Defaults to `true`, otherwise uses big endian
2197 * @returns {!ByteBuffer} this
2198 * @expose
2199 */
2200 ByteBufferPrototype.LE = function(littleEndian) {
2201 this.littleEndian = typeof littleEndian !== 'undefined' ? !!littleEndian : true;
2202 return this;
2203 };
2204
2205 /**
2206 * Switches (to) big endian byte order.
2207 * @param {boolean=} bigEndian Defaults to `true`, otherwise uses little endian
2208 * @returns {!ByteBuffer} this
2209 * @expose
2210 */
2211 ByteBufferPrototype.BE = function(bigEndian) {
2212 this.littleEndian = typeof bigEndian !== 'undefined' ? !bigEndian : false;
2213 return this;
2214 };
2215 /**
2216 * Prepends some data to this ByteBuffer. This will overwrite any contents before the specified offset up to the
2217 * prepended data's length. If there is not enough space available before the specified `offset`, the backing buffer
2218 * will be resized and its contents moved accordingly.
2219 * @param {!ByteBuffer|string|!ArrayBuffer} source Data to prepend. If `source` is a ByteBuffer, its offset will be
2220 * modified according to the performed read operation.
2221 * @param {(string|number)=} encoding Encoding if `data` is a string ("base64", "hex", "binary", defaults to "utf8")
2222 * @param {number=} offset Offset to prepend at. Will use and decrease {@link ByteBuffer#offset} by the number of bytes
2223 * prepended if omitted.
2224 * @returns {!ByteBuffer} this
2225 * @expose
2226 * @example A relative `00<01 02 03>.prepend(<04 05>)` results in `<04 05 01 02 03>, 04 05|`
2227 * @example An absolute `00<01 02 03>.prepend(<04 05>, 2)` results in `04<05 02 03>, 04 05|`
2228 */
2229 ByteBufferPrototype.prepend = function(source, encoding, offset) {
2230 if (typeof encoding === 'number' || typeof encoding !== 'string') {
2231 offset = encoding;
2232 encoding = undefined;
2233 }
2234 var relative = typeof offset === 'undefined';
2235 if (relative) offset = this.offset;
2236 if (!this.noAssert) {
2237 if (typeof offset !== 'number' || offset % 1 !== 0)
2238 throw TypeError("Illegal offset: "+offset+" (not an integer)");
2239 offset >>>= 0;
2240 if (offset < 0 || offset + 0 > this.buffer.byteLength)
2241 throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength);
2242 }
2243 if (!(source instanceof ByteBuffer))
2244 source = ByteBuffer.wrap(source, encoding);
2245 var len = source.limit - source.offset;
2246 if (len <= 0) return this; // Nothing to prepend
2247 var diff = len - offset;
2248 var arrayView;
2249 if (diff > 0) { // Not enough space before offset, so resize + move
2250 var buffer = new ArrayBuffer(this.buffer.byteLength + diff);
2251 arrayView = new Uint8Array(buffer);
2252 arrayView.set(new Uint8Array(this.buffer).subarray(offset, this.buffer.byteLength), len);
2253 this.buffer = buffer;
2254 this.view = new DataView(buffer);
2255 this.offset += diff;
2256 if (this.markedOffset >= 0) this.markedOffset += diff;
2257 this.limit += diff;
2258 offset += diff;
2259 } else {
2260 arrayView = new Uint8Array(this.buffer);
2261 }
2262 arrayView.set(new Uint8Array(source.buffer).subarray(source.offset, source.limit), offset - len);
2263 source.offset = source.limit;
2264 if (relative)
2265 this.offset -= len;
2266 return this;
2267 };
2268
2269 /**
2270 * Prepends this ByteBuffer to another ByteBuffer. This will overwrite any contents before the specified offset up to the
2271 * prepended data's length. If there is not enough space available before the specified `offset`, the backing buffer
2272 * will be resized and its contents moved accordingly.
2273 * @param {!ByteBuffer} target Target ByteBuffer
2274 * @param {number=} offset Offset to prepend at. Will use and decrease {@link ByteBuffer#offset} by the number of bytes
2275 * prepended if omitted.
2276 * @returns {!ByteBuffer} this
2277 * @expose
2278 * @see ByteBuffer#prepend
2279 */
2280 ByteBufferPrototype.prependTo = function(target, offset) {
2281 target.prepend(this, offset);
2282 return this;
2283 };
2284 /**
2285 * Prints debug information about this ByteBuffer's contents.
2286 * @param {function(string)=} out Output function to call, defaults to console.log
2287 * @expose
2288 */
2289 ByteBufferPrototype.printDebug = function(out) {
2290 if (typeof out !== 'function') out = console.log.bind(console);
2291 out(
2292 this.toString()+"\n"+
2293 "-------------------------------------------------------------------\n"+
2294 this.toDebug(/* columns */ true)
2295 );
2296 };
2297
2298 /**
2299 * Gets the number of remaining readable bytes. Contents are the bytes between {@link ByteBuffer#offset} and
2300 * {@link ByteBuffer#limit}, so this returns `limit - offset`.
2301 * @returns {number} Remaining readable bytes. May be negative if `offset > limit`.
2302 * @expose
2303 */
2304 ByteBufferPrototype.remaining = function() {
2305 return this.limit - this.offset;
2306 };
2307 /**
2308 * Resets this ByteBuffer's {@link ByteBuffer#offset}. If an offset has been marked through {@link ByteBuffer#mark}
2309 * before, `offset` will be set to {@link ByteBuffer#markedOffset}, which will then be discarded. If no offset has been
2310 * marked, sets `offset = 0`.
2311 * @returns {!ByteBuffer} this
2312 * @see ByteBuffer#mark
2313 * @expose
2314 */
2315 ByteBufferPrototype.reset = function() {
2316 if (this.markedOffset >= 0) {
2317 this.offset = this.markedOffset;
2318 this.markedOffset = -1;
2319 } else {
2320 this.offset = 0;
2321 }
2322 return this;
2323 };
2324 /**
2325 * Resizes this ByteBuffer to be backed by a buffer of at least the given capacity. Will do nothing if already that
2326 * large or larger.
2327 * @param {number} capacity Capacity required
2328 * @returns {!ByteBuffer} this
2329 * @throws {TypeError} If `capacity` is not a number
2330 * @throws {RangeError} If `capacity < 0`
2331 * @expose
2332 */
2333 ByteBufferPrototype.resize = function(capacity) {
2334 if (!this.noAssert) {
2335 if (typeof capacity !== 'number' || capacity % 1 !== 0)
2336 throw TypeError("Illegal capacity: "+capacity+" (not an integer)");
2337 capacity |= 0;
2338 if (capacity < 0)
2339 throw RangeError("Illegal capacity: 0 <= "+capacity);
2340 }
2341 if (this.buffer.byteLength < capacity) {
2342 var buffer = new ArrayBuffer(capacity);
2343 new Uint8Array(buffer).set(new Uint8Array(this.buffer));
2344 this.buffer = buffer;
2345 this.view = new DataView(buffer);
2346 }
2347 return this;
2348 };
2349 /**
2350 * Reverses this ByteBuffer's contents.
2351 * @param {number=} begin Offset to start at, defaults to {@link ByteBuffer#offset}
2352 * @param {number=} end Offset to end at, defaults to {@link ByteBuffer#limit}
2353 * @returns {!ByteBuffer} this
2354 * @expose
2355 */
2356 ByteBufferPrototype.reverse = function(begin, end) {
2357 if (typeof begin === 'undefined') begin = this.offset;
2358 if (typeof end === 'undefined') end = this.limit;
2359 if (!this.noAssert) {
2360 if (typeof begin !== 'number' || begin % 1 !== 0)
2361 throw TypeError("Illegal begin: Not an integer");
2362 begin >>>= 0;
2363 if (typeof end !== 'number' || end % 1 !== 0)
2364 throw TypeError("Illegal end: Not an integer");
2365 end >>>= 0;
2366 if (begin < 0 || begin > end || end > this.buffer.byteLength)
2367 throw RangeError("Illegal range: 0 <= "+begin+" <= "+end+" <= "+this.buffer.byteLength);
2368 }
2369 if (begin === end)
2370 return this; // Nothing to reverse
2371 Array.prototype.reverse.call(new Uint8Array(this.buffer).subarray(begin, end));
2372 this.view = new DataView(this.buffer); // FIXME: Why exactly is this necessary?
2373 return this;
2374 };
2375 /**
2376 * Skips the next `length` bytes. This will just advance
2377 * @param {number} length Number of bytes to skip. May also be negative to move the offset back.
2378 * @returns {!ByteBuffer} this
2379 * @expose
2380 */
2381 ByteBufferPrototype.skip = function(length) {
2382 if (!this.noAssert) {
2383 if (typeof length !== 'number' || length % 1 !== 0)
2384 throw TypeError("Illegal length: "+length+" (not an integer)");
2385 length |= 0;
2386 }
2387 var offset = this.offset + length;
2388 if (!this.noAssert) {
2389 if (offset < 0 || offset > this.buffer.byteLength)
2390 throw RangeError("Illegal length: 0 <= "+this.offset+" + "+length+" <= "+this.buffer.byteLength);
2391 }
2392 this.offset = offset;
2393 return this;
2394 };
2395
2396 /**
2397 * Slices this ByteBuffer by creating a cloned instance with `offset = begin` and `limit = end`.
2398 * @param {number=} begin Begin offset, defaults to {@link ByteBuffer#offset}.
2399 * @param {number=} end End offset, defaults to {@link ByteBuffer#limit}.
2400 * @returns {!ByteBuffer} Clone of this ByteBuffer with slicing applied, backed by the same {@link ByteBuffer#buffer}
2401 * @expose
2402 */
2403 ByteBufferPrototype.slice = function(begin, end) {
2404 if (typeof begin === 'undefined') begin = this.offset;
2405 if (typeof end === 'undefined') end = this.limit;
2406 if (!this.noAssert) {
2407 if (typeof begin !== 'number' || begin % 1 !== 0)
2408 throw TypeError("Illegal begin: Not an integer");
2409 begin >>>= 0;
2410 if (typeof end !== 'number' || end % 1 !== 0)
2411 throw TypeError("Illegal end: Not an integer");
2412 end >>>= 0;
2413 if (begin < 0 || begin > end || end > this.buffer.byteLength)
2414 throw RangeError("Illegal range: 0 <= "+begin+" <= "+end+" <= "+this.buffer.byteLength);
2415 }
2416 var bb = this.clone();
2417 bb.offset = begin;
2418 bb.limit = end;
2419 return bb;
2420 };
2421 /**
2422 * Returns a copy of the backing buffer that contains this ByteBuffer's contents. Contents are the bytes between
2423 * {@link ByteBuffer#offset} and {@link ByteBuffer#limit}. Will transparently {@link ByteBuffer#flip} this
2424 * ByteBuffer if `offset > limit` but the actual offsets remain untouched.
2425 * @param {boolean=} forceCopy If `true` returns a copy, otherwise returns a view referencing the same memory if
2426 * possible. Defaults to `false`
2427 * @returns {!ArrayBuffer} Contents as an ArrayBuffer
2428 * @expose
2429 */
2430 ByteBufferPrototype.toBuffer = function(forceCopy) {
2431 var offset = this.offset,
2432 limit = this.limit;
2433 if (offset > limit) {
2434 var t = offset;
2435 offset = limit;
2436 limit = t;
2437 }
2438 if (!this.noAssert) {
2439 if (typeof offset !== 'number' || offset % 1 !== 0)
2440 throw TypeError("Illegal offset: Not an integer");
2441 offset >>>= 0;
2442 if (typeof limit !== 'number' || limit % 1 !== 0)
2443 throw TypeError("Illegal limit: Not an integer");
2444 limit >>>= 0;
2445 if (offset < 0 || offset > limit || limit > this.buffer.byteLength)
2446 throw RangeError("Illegal range: 0 <= "+offset+" <= "+limit+" <= "+this.buffer.byteLength);
2447 }
2448 // NOTE: It's not possible to have another ArrayBuffer reference the same memory as the backing buffer. This is
2449 // possible with Uint8Array#subarray only, but we have to return an ArrayBuffer by contract. So:
2450 if (!forceCopy && offset === 0 && limit === this.buffer.byteLength) {
2451 return this.buffer;
2452 }
2453 if (offset === limit) {
2454 return EMPTY_BUFFER;
2455 }
2456 var buffer = new ArrayBuffer(limit - offset);
2457 new Uint8Array(buffer).set(new Uint8Array(this.buffer).subarray(offset, limit), 0);
2458 return buffer;
2459 };
2460
2461 /**
2462 * Returns a raw buffer compacted to contain this ByteBuffer's contents. Contents are the bytes between
2463 * {@link ByteBuffer#offset} and {@link ByteBuffer#limit}. Will transparently {@link ByteBuffer#flip} this
2464 * ByteBuffer if `offset > limit` but the actual offsets remain untouched. This is an alias of
2465 * {@link ByteBuffer#toBuffer}.
2466 * @function
2467 * @param {boolean=} forceCopy If `true` returns a copy, otherwise returns a view referencing the same memory.
2468 * Defaults to `false`
2469 * @returns {!ArrayBuffer} Contents as an ArrayBuffer
2470 * @expose
2471 */
2472 ByteBufferPrototype.toArrayBuffer = ByteBufferPrototype.toBuffer;
2473
2474
2475 /**
2476 * Converts the ByteBuffer's contents to a string.
2477 * @param {string=} encoding Output encoding. Returns an informative string representation if omitted but also allows
2478 * direct conversion to "utf8", "hex", "base64" and "binary" encoding. "debug" returns a hex representation with
2479 * highlighted offsets.
2480 * @param {number=} begin Offset to begin at, defaults to {@link ByteBuffer#offset}
2481 * @param {number=} end Offset to end at, defaults to {@link ByteBuffer#limit}
2482 * @returns {string} String representation
2483 * @throws {Error} If `encoding` is invalid
2484 * @expose
2485 */
2486 ByteBufferPrototype.toString = function(encoding, begin, end) {
2487 if (typeof encoding === 'undefined')
2488 return "ByteBufferAB(offset="+this.offset+",markedOffset="+this.markedOffset+",limit="+this.limit+",capacity="+this.capacity()+")";
2489 if (typeof encoding === 'number')
2490 encoding = "utf8",
2491 begin = encoding,
2492 end = begin;
2493 switch (encoding) {
2494 case "utf8":
2495 return this.toUTF8(begin, end);
2496 case "base64":
2497 return this.toBase64(begin, end);
2498 case "hex":
2499 return this.toHex(begin, end);
2500 case "binary":
2501 return this.toBinary(begin, end);
2502 case "debug":
2503 return this.toDebug();
2504 case "columns":
2505 return this.toColumns();
2506 default:
2507 throw Error("Unsupported encoding: "+encoding);
2508 }
2509 };
2510
2511 // lxiv-embeddable
2512
2513 /**
2514 * lxiv-embeddable (c) 2014 Daniel Wirtz <dcode@dcode.io>
2515 * Released under the Apache License, Version 2.0
2516 * see: https://github.com/dcodeIO/lxiv for details
2517 */
2518 var lxiv = function() {
2519 "use strict";
2520
2521 /**
2522 * lxiv namespace.
2523 * @type {!Object.<string,*>}
2524 * @exports lxiv
2525 */
2526 var lxiv = {};
2527
2528 /**
2529 * Character codes for output.
2530 * @type {!Array.<number>}
2531 * @inner
2532 */
2533 var aout = [
2534 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
2535 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102,
2536 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118,
2537 119, 120, 121, 122, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 43, 47
2538 ];
2539
2540 /**
2541 * Character codes for input.
2542 * @type {!Array.<number>}
2543 * @inner
2544 */
2545 var ain = [];
2546 for (var i=0, k=aout.length; i<k; ++i)
2547 ain[aout[i]] = i;
2548
2549 /**
2550 * Encodes bytes to base64 char codes.
2551 * @param {!function():number|null} src Bytes source as a function returning the next byte respectively `null` if
2552 * there are no more bytes left.
2553 * @param {!function(number)} dst Characters destination as a function successively called with each encoded char
2554 * code.
2555 */
2556 lxiv.encode = function(src, dst) {
2557 var b, t;
2558 while ((b = src()) !== null) {
2559 dst(aout[(b>>2)&0x3f]);
2560 t = (b&0x3)<<4;
2561 if ((b = src()) !== null) {
2562 t |= (b>>4)&0xf;
2563 dst(aout[(t|((b>>4)&0xf))&0x3f]);
2564 t = (b&0xf)<<2;
2565 if ((b = src()) !== null)
2566 dst(aout[(t|((b>>6)&0x3))&0x3f]),
2567 dst(aout[b&0x3f]);
2568 else
2569 dst(aout[t&0x3f]),
2570 dst(61);
2571 } else
2572 dst(aout[t&0x3f]),
2573 dst(61),
2574 dst(61);
2575 }
2576 };
2577
2578 /**
2579 * Decodes base64 char codes to bytes.
2580 * @param {!function():number|null} src Characters source as a function returning the next char code respectively
2581 * `null` if there are no more characters left.
2582 * @param {!function(number)} dst Bytes destination as a function successively called with the next byte.
2583 * @throws {Error} If a character code is invalid
2584 */
2585 lxiv.decode = function(src, dst) {
2586 var c, t1, t2;
2587 function fail(c) {
2588 throw Error("Illegal character code: "+c);
2589 }
2590 while ((c = src()) !== null) {
2591 t1 = ain[c];
2592 if (typeof t1 === 'undefined') fail(c);
2593 if ((c = src()) !== null) {
2594 t2 = ain[c];
2595 if (typeof t2 === 'undefined') fail(c);
2596 dst((t1<<2)>>>0|(t2&0x30)>>4);
2597 if ((c = src()) !== null) {
2598 t1 = ain[c];
2599 if (typeof t1 === 'undefined')
2600 if (c === 61) break; else fail(c);
2601 dst(((t2&0xf)<<4)>>>0|(t1&0x3c)>>2);
2602 if ((c = src()) !== null) {
2603 t2 = ain[c];
2604 if (typeof t2 === 'undefined')
2605 if (c === 61) break; else fail(c);
2606 dst(((t1&0x3)<<6)>>>0|t2);
2607 }
2608 }
2609 }
2610 }
2611 };
2612
2613 /**
2614 * Tests if a string is valid base64.
2615 * @param {string} str String to test
2616 * @returns {boolean} `true` if valid, otherwise `false`
2617 */
2618 lxiv.test = function(str) {
2619 return /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(str);
2620 };
2621
2622 return lxiv;
2623 }();
2624
2625 // encodings/base64
2626
2627 /**
2628 * Encodes this ByteBuffer's contents to a base64 encoded string.
2629 * @param {number=} begin Offset to begin at, defaults to {@link ByteBuffer#offset}.
2630 * @param {number=} end Offset to end at, defaults to {@link ByteBuffer#limit}.
2631 * @returns {string} Base64 encoded string
2632 * @expose
2633 */
2634 ByteBufferPrototype.toBase64 = function(begin, end) {
2635 if (typeof begin === 'undefined')
2636 begin = this.offset;
2637 if (typeof end === 'undefined')
2638 end = this.limit;
2639 if (!this.noAssert) {
2640 if (typeof begin !== 'number' || begin % 1 !== 0)
2641 throw TypeError("Illegal begin: Not an integer");
2642 begin >>>= 0;
2643 if (typeof end !== 'number' || end % 1 !== 0)
2644 throw TypeError("Illegal end: Not an integer");
2645 end >>>= 0;
2646 if (begin < 0 || begin > end || end > this.buffer.byteLength)
2647 throw RangeError("Illegal range: 0 <= "+begin+" <= "+end+" <= "+this.buffer.byteLength);
2648 }
2649 var sd; lxiv.encode(function() {
2650 return begin < end ? this.view.getUint8(begin++) : null;
2651 }.bind(this), sd = stringDestination());
2652 return sd();
2653 };
2654
2655 /**
2656 * Decodes a base64 encoded string to a ByteBuffer.
2657 * @param {string} str String to decode
2658 * @param {boolean=} littleEndian Whether to use little or big endian byte order. Defaults to
2659 * {@link ByteBuffer.DEFAULT_ENDIAN}.
2660 * @param {boolean=} noAssert Whether to skip assertions of offsets and values. Defaults to
2661 * {@link ByteBuffer.DEFAULT_NOASSERT}.
2662 * @returns {!ByteBuffer} ByteBuffer
2663 * @expose
2664 */
2665 ByteBuffer.fromBase64 = function(str, littleEndian, noAssert) {
2666 if (!noAssert) {
2667 if (typeof str !== 'string')
2668 throw TypeError("Illegal str: Not a string");
2669 if (str.length % 4 !== 0)
2670 throw TypeError("Illegal str: Length not a multiple of 4");
2671 }
2672 var bb = new ByteBuffer(str.length/4*3, littleEndian, noAssert),
2673 i = 0;
2674 lxiv.decode(stringSource(str), function(b) {
2675 bb.view.setUint8(i++, b);
2676 });
2677 bb.limit = i;
2678 return bb;
2679 };
2680
2681 /**
2682 * Encodes a binary string to base64 like `window.btoa` does.
2683 * @param {string} str Binary string
2684 * @returns {string} Base64 encoded string
2685 * @see https://developer.mozilla.org/en-US/docs/Web/API/Window.btoa
2686 * @expose
2687 */
2688 ByteBuffer.btoa = function(str) {
2689 return ByteBuffer.fromBinary(str).toBase64();
2690 };
2691
2692 /**
2693 * Decodes a base64 encoded string to binary like `window.atob` does.
2694 * @param {string} b64 Base64 encoded string
2695 * @returns {string} Binary string
2696 * @see https://developer.mozilla.org/en-US/docs/Web/API/Window.atob
2697 * @expose
2698 */
2699 ByteBuffer.atob = function(b64) {
2700 return ByteBuffer.fromBase64(b64).toBinary();
2701 };
2702
2703 // encodings/binary
2704
2705 /**
2706 * Encodes this ByteBuffer to a binary encoded string, that is using only characters 0x00-0xFF as bytes.
2707 * @param {number=} begin Offset to begin at. Defaults to {@link ByteBuffer#offset}.
2708 * @param {number=} end Offset to end at. Defaults to {@link ByteBuffer#limit}.
2709 * @returns {string} Binary encoded string
2710 * @throws {RangeError} If `offset > limit`
2711 * @expose
2712 */
2713 ByteBufferPrototype.toBinary = function(begin, end) {
2714 begin = typeof begin === 'undefined' ? this.offset : begin;
2715 end = typeof end === 'undefined' ? this.limit : end;
2716 if (!this.noAssert) {
2717 if (typeof begin !== 'number' || begin % 1 !== 0)
2718 throw TypeError("Illegal begin: Not an integer");
2719 begin >>>= 0;
2720 if (typeof end !== 'number' || end % 1 !== 0)
2721 throw TypeError("Illegal end: Not an integer");
2722 end >>>= 0;
2723 if (begin < 0 || begin > end || end > this.buffer.byteLength)
2724 throw RangeError("Illegal range: 0 <= "+begin+" <= "+end+" <= "+this.buffer.byteLength);
2725 }
2726 if (begin === end)
2727 return "";
2728 var cc = [], pt = [];
2729 while (begin < end) {
2730 cc.push(this.view.getUint8(begin++));
2731 if (cc.length >= 1024)
2732 pt.push(String.fromCharCode.apply(String, cc)),
2733 cc = [];
2734 }
2735 return pt.join('') + String.fromCharCode.apply(String, cc);
2736 };
2737
2738 /**
2739 * Decodes a binary encoded string, that is using only characters 0x00-0xFF as bytes, to a ByteBuffer.
2740 * @param {string} str String to decode
2741 * @param {boolean=} littleEndian Whether to use little or big endian byte order. Defaults to
2742 * {@link ByteBuffer.DEFAULT_ENDIAN}.
2743 * @param {boolean=} noAssert Whether to skip assertions of offsets and values. Defaults to
2744 * {@link ByteBuffer.DEFAULT_NOASSERT}.
2745 * @returns {!ByteBuffer} ByteBuffer
2746 * @expose
2747 */
2748 ByteBuffer.fromBinary = function(str, littleEndian, noAssert) {
2749 if (!noAssert) {
2750 if (typeof str !== 'string')
2751 throw TypeError("Illegal str: Not a string");
2752 }
2753 var i = 0, k = str.length, charCode,
2754 bb = new ByteBuffer(k, littleEndian, noAssert);
2755 while (i<k) {
2756 charCode = str.charCodeAt(i);
2757 if (!noAssert && charCode > 255)
2758 throw RangeError("Illegal charCode at "+i+": 0 <= "+charCode+" <= 255");
2759 bb.view.setUint8(i++, charCode);
2760 }
2761 bb.limit = k;
2762 return bb;
2763 };
2764
2765 // encodings/debug
2766
2767 /**
2768 * Encodes this ByteBuffer to a hex encoded string with marked offsets. Offset symbols are:
2769 * * `<` : offset,
2770 * * `'` : markedOffset,
2771 * * `>` : limit,
2772 * * `|` : offset and limit,
2773 * * `[` : offset and markedOffset,
2774 * * `]` : markedOffset and limit,
2775 * * `!` : offset, markedOffset and limit
2776 * @param {boolean=} columns If `true` returns two columns hex + ascii, defaults to `false`
2777 * @returns {string|!Array.<string>} Debug string or array of lines if `asArray = true`
2778 * @expose
2779 * @example `>00'01 02<03` contains four bytes with `limit=0, markedOffset=1, offset=3`
2780 * @example `00[01 02 03>` contains four bytes with `offset=markedOffset=1, limit=4`
2781 * @example `00|01 02 03` contains four bytes with `offset=limit=1, markedOffset=-1`
2782 * @example `|` contains zero bytes with `offset=limit=0, markedOffset=-1`
2783 */
2784 ByteBufferPrototype.toDebug = function(columns) {
2785 var i = -1,
2786 k = this.buffer.byteLength,
2787 b,
2788 hex = "",
2789 asc = "",
2790 out = "";
2791 while (i<k) {
2792 if (i !== -1) {
2793 b = this.view.getUint8(i);
2794 if (b < 0x10) hex += "0"+b.toString(16).toUpperCase();
2795 else hex += b.toString(16).toUpperCase();
2796 if (columns) {
2797 asc += b > 32 && b < 127 ? String.fromCharCode(b) : '.';
2798 }
2799 }
2800 ++i;
2801 if (columns) {
2802 if (i > 0 && i % 16 === 0 && i !== k) {
2803 while (hex.length < 3*16+3) hex += " ";
2804 out += hex+asc+"\n";
2805 hex = asc = "";
2806 }
2807 }
2808 if (i === this.offset && i === this.limit)
2809 hex += i === this.markedOffset ? "!" : "|";
2810 else if (i === this.offset)
2811 hex += i === this.markedOffset ? "[" : "<";
2812 else if (i === this.limit)
2813 hex += i === this.markedOffset ? "]" : ">";
2814 else
2815 hex += i === this.markedOffset ? "'" : (columns || (i !== 0 && i !== k) ? " " : "");
2816 }
2817 if (columns && hex !== " ") {
2818 while (hex.length < 3*16+3) hex += " ";
2819 out += hex+asc+"\n";
2820 }
2821 return columns ? out : hex;
2822 };
2823
2824 /**
2825 * Decodes a hex encoded string with marked offsets to a ByteBuffer.
2826 * @param {string} str Debug string to decode (not be generated with `columns = true`)
2827 * @param {boolean=} littleEndian Whether to use little or big endian byte order. Defaults to
2828 * {@link ByteBuffer.DEFAULT_ENDIAN}.
2829 * @param {boolean=} noAssert Whether to skip assertions of offsets and values. Defaults to
2830 * {@link ByteBuffer.DEFAULT_NOASSERT}.
2831 * @returns {!ByteBuffer} ByteBuffer
2832 * @expose
2833 * @see ByteBuffer#toDebug
2834 */
2835 ByteBuffer.fromDebug = function(str, littleEndian, noAssert) {
2836 var k = str.length,
2837 bb = new ByteBuffer(((k+1)/3)|0, littleEndian, noAssert);
2838 var i = 0, j = 0, ch, b,
2839 rs = false, // Require symbol next
2840 ho = false, hm = false, hl = false, // Already has offset, markedOffset, limit?
2841 fail = false;
2842 while (i<k) {
2843 switch (ch = str.charAt(i++)) {
2844 case '!':
2845 if (!noAssert) {
2846 if (ho || hm || hl) {
2847 fail = true; break;
2848 }
2849 ho = hm = hl = true;
2850 }
2851 bb.offset = bb.markedOffset = bb.limit = j;
2852 rs = false;
2853 break;
2854 case '|':
2855 if (!noAssert) {
2856 if (ho || hl) {
2857 fail = true; break;
2858 }
2859 ho = hl = true;
2860 }
2861 bb.offset = bb.limit = j;
2862 rs = false;
2863 break;
2864 case '[':
2865 if (!noAssert) {
2866 if (ho || hm) {
2867 fail = true; break;
2868 }
2869 ho = hm = true;
2870 }
2871 bb.offset = bb.markedOffset = j;
2872 rs = false;
2873 break;
2874 case '<':
2875 if (!noAssert) {
2876 if (ho) {
2877 fail = true; break;
2878 }
2879 ho = true;
2880 }
2881 bb.offset = j;
2882 rs = false;
2883 break;
2884 case ']':
2885 if (!noAssert) {
2886 if (hl || hm) {
2887 fail = true; break;
2888 }
2889 hl = hm = true;
2890 }
2891 bb.limit = bb.markedOffset = j;
2892 rs = false;
2893 break;
2894 case '>':
2895 if (!noAssert) {
2896 if (hl) {
2897 fail = true; break;
2898 }
2899 hl = true;
2900 }
2901 bb.limit = j;
2902 rs = false;
2903 break;
2904 case "'":
2905 if (!noAssert) {
2906 if (hm) {
2907 fail = true; break;
2908 }
2909 hm = true;
2910 }
2911 bb.markedOffset = j;
2912 rs = false;
2913 break;
2914 case ' ':
2915 rs = false;
2916 break;
2917 default:
2918 if (!noAssert) {
2919 if (rs) {
2920 fail = true; break;
2921 }
2922 }
2923 b = parseInt(ch+str.charAt(i++), 16);
2924 if (!noAssert) {
2925 if (isNaN(b) || b < 0 || b > 255)
2926 throw TypeError("Illegal str: Not a debug encoded string");
2927 }
2928 bb.view.setUint8(j++, b);
2929 rs = true;
2930 }
2931 if (fail)
2932 throw TypeError("Illegal str: Invalid symbol at "+i);
2933 }
2934 if (!noAssert) {
2935 if (!ho || !hl)
2936 throw TypeError("Illegal str: Missing offset or limit");
2937 if (j<bb.buffer.byteLength)
2938 throw TypeError("Illegal str: Not a debug encoded string (is it hex?) "+j+" < "+k);
2939 }
2940 return bb;
2941 };
2942
2943 // encodings/hex
2944
2945 /**
2946 * Encodes this ByteBuffer's contents to a hex encoded string.
2947 * @param {number=} begin Offset to begin at. Defaults to {@link ByteBuffer#offset}.
2948 * @param {number=} end Offset to end at. Defaults to {@link ByteBuffer#limit}.
2949 * @returns {string} Hex encoded string
2950 * @expose
2951 */
2952 ByteBufferPrototype.toHex = function(begin, end) {
2953 begin = typeof begin === 'undefined' ? this.offset : begin;
2954 end = typeof end === 'undefined' ? this.limit : end;
2955 if (!this.noAssert) {
2956 if (typeof begin !== 'number' || begin % 1 !== 0)
2957 throw TypeError("Illegal begin: Not an integer");
2958 begin >>>= 0;
2959 if (typeof end !== 'number' || end % 1 !== 0)
2960 throw TypeError("Illegal end: Not an integer");
2961 end >>>= 0;
2962 if (begin < 0 || begin > end || end > this.buffer.byteLength)
2963 throw RangeError("Illegal range: 0 <= "+begin+" <= "+end+" <= "+this.buffer.byteLength);
2964 }
2965 var out = new Array(end - begin),
2966 b;
2967 while (begin < end) {
2968 b = this.view.getUint8(begin++);
2969 if (b < 0x10)
2970 out.push("0", b.toString(16));
2971 else out.push(b.toString(16));
2972 }
2973 return out.join('');
2974 };
2975
2976 /**
2977 * Decodes a hex encoded string to a ByteBuffer.
2978 * @param {string} str String to decode
2979 * @param {boolean=} littleEndian Whether to use little or big endian byte order. Defaults to
2980 * {@link ByteBuffer.DEFAULT_ENDIAN}.
2981 * @param {boolean=} noAssert Whether to skip assertions of offsets and values. Defaults to
2982 * {@link ByteBuffer.DEFAULT_NOASSERT}.
2983 * @returns {!ByteBuffer} ByteBuffer
2984 * @expose
2985 */
2986 ByteBuffer.fromHex = function(str, littleEndian, noAssert) {
2987 if (!noAssert) {
2988 if (typeof str !== 'string')
2989 throw TypeError("Illegal str: Not a string");
2990 if (str.length % 2 !== 0)
2991 throw TypeError("Illegal str: Length not a multiple of 2");
2992 }
2993 var k = str.length,
2994 bb = new ByteBuffer((k / 2) | 0, littleEndian),
2995 b;
2996 for (var i=0, j=0; i<k; i+=2) {
2997 b = parseInt(str.substring(i, i+2), 16);
2998 if (!noAssert)
2999 if (!isFinite(b) || b < 0 || b > 255)
3000 throw TypeError("Illegal str: Contains non-hex characters");
3001 bb.view.setUint8(j++, b);
3002 }
3003 bb.limit = j;
3004 return bb;
3005 };
3006
3007 // utfx-embeddable
3008
3009 /**
3010 * utfx-embeddable (c) 2014 Daniel Wirtz <dcode@dcode.io>
3011 * Released under the Apache License, Version 2.0
3012 * see: https://github.com/dcodeIO/utfx for details
3013 */
3014 var utfx = function() {
3015 "use strict";
3016
3017 /**
3018 * utfx namespace.
3019 * @inner
3020 * @type {!Object.<string,*>}
3021 */
3022 var utfx = {};
3023
3024 /**
3025 * Maximum valid code point.
3026 * @type {number}
3027 * @const
3028 */
3029 utfx.MAX_CODEPOINT = 0x10FFFF;
3030
3031 /**
3032 * Encodes UTF8 code points to UTF8 bytes.
3033 * @param {(!function():number|null) | number} src Code points source, either as a function returning the next code point
3034 * respectively `null` if there are no more code points left or a single numeric code point.
3035 * @param {!function(number)} dst Bytes destination as a function successively called with the next byte
3036 */
3037 utfx.encodeUTF8 = function(src, dst) {
3038 var cp = null;
3039 if (typeof src === 'number')
3040 cp = src,
3041 src = function() { return null; };
3042 while (cp !== null || (cp = src()) !== null) {
3043 if (cp < 0x80)
3044 dst(cp&0x7F);
3045 else if (cp < 0x800)
3046 dst(((cp>>6)&0x1F)|0xC0),
3047 dst((cp&0x3F)|0x80);
3048 else if (cp < 0x10000)
3049 dst(((cp>>12)&0x0F)|0xE0),
3050 dst(((cp>>6)&0x3F)|0x80),
3051 dst((cp&0x3F)|0x80);
3052 else
3053 dst(((cp>>18)&0x07)|0xF0),
3054 dst(((cp>>12)&0x3F)|0x80),
3055 dst(((cp>>6)&0x3F)|0x80),
3056 dst((cp&0x3F)|0x80);
3057 cp = null;
3058 }
3059 };
3060
3061 /**
3062 * Decodes UTF8 bytes to UTF8 code points.
3063 * @param {!function():number|null} src Bytes source as a function returning the next byte respectively `null` if there
3064 * are no more bytes left.
3065 * @param {!function(number)} dst Code points destination as a function successively called with each decoded code point.
3066 * @throws {RangeError} If a starting byte is invalid in UTF8
3067 * @throws {Error} If the last sequence is truncated. Has an array property `bytes` holding the
3068 * remaining bytes.
3069 */
3070 utfx.decodeUTF8 = function(src, dst) {
3071 var a, b, c, d, fail = function(b) {
3072 b = b.slice(0, b.indexOf(null));
3073 var err = Error(b.toString());
3074 err.name = "TruncatedError";
3075 err['bytes'] = b;
3076 throw err;
3077 };
3078 while ((a = src()) !== null) {
3079 if ((a&0x80) === 0)
3080 dst(a);
3081 else if ((a&0xE0) === 0xC0)
3082 ((b = src()) === null) && fail([a, b]),
3083 dst(((a&0x1F)<<6) | (b&0x3F));
3084 else if ((a&0xF0) === 0xE0)
3085 ((b=src()) === null || (c=src()) === null) && fail([a, b, c]),
3086 dst(((a&0x0F)<<12) | ((b&0x3F)<<6) | (c&0x3F));
3087 else if ((a&0xF8) === 0xF0)
3088 ((b=src()) === null || (c=src()) === null || (d=src()) === null) && fail([a, b, c ,d]),
3089 dst(((a&0x07)<<18) | ((b&0x3F)<<12) | ((c&0x3F)<<6) | (d&0x3F));
3090 else throw RangeError("Illegal starting byte: "+a);
3091 }
3092 };
3093
3094 /**
3095 * Converts UTF16 characters to UTF8 code points.
3096 * @param {!function():number|null} src Characters source as a function returning the next char code respectively
3097 * `null` if there are no more characters left.
3098 * @param {!function(number)} dst Code points destination as a function successively called with each converted code
3099 * point.
3100 */
3101 utfx.UTF16toUTF8 = function(src, dst) {
3102 var c1, c2 = null;
3103 while (true) {
3104 if ((c1 = c2 !== null ? c2 : src()) === null)
3105 break;
3106 if (c1 >= 0xD800 && c1 <= 0xDFFF) {
3107 if ((c2 = src()) !== null) {
3108 if (c2 >= 0xDC00 && c2 <= 0xDFFF) {
3109 dst((c1-0xD800)*0x400+c2-0xDC00+0x10000);
3110 c2 = null; continue;
3111 }
3112 }
3113 }
3114 dst(c1);
3115 }
3116 if (c2 !== null) dst(c2);
3117 };
3118
3119 /**
3120 * Converts UTF8 code points to UTF16 characters.
3121 * @param {(!function():number|null) | number} src Code points source, either as a function returning the next code point
3122 * respectively `null` if there are no more code points left or a single numeric code point.
3123 * @param {!function(number)} dst Characters destination as a function successively called with each converted char code.
3124 * @throws {RangeError} If a code point is out of range
3125 */
3126 utfx.UTF8toUTF16 = function(src, dst) {
3127 var cp = null;
3128 if (typeof src === 'number')
3129 cp = src, src = function() { return null; };
3130 while (cp !== null || (cp = src()) !== null) {
3131 if (cp <= 0xFFFF)
3132 dst(cp);
3133 else
3134 cp -= 0x10000,
3135 dst((cp>>10)+0xD800),
3136 dst((cp%0x400)+0xDC00);
3137 cp = null;
3138 }
3139 };
3140
3141 /**
3142 * Converts and encodes UTF16 characters to UTF8 bytes.
3143 * @param {!function():number|null} src Characters source as a function returning the next char code respectively `null`
3144 * if there are no more characters left.
3145 * @param {!function(number)} dst Bytes destination as a function successively called with the next byte.
3146 */
3147 utfx.encodeUTF16toUTF8 = function(src, dst) {
3148 utfx.UTF16toUTF8(src, function(cp) {
3149 utfx.encodeUTF8(cp, dst);
3150 });
3151 };
3152
3153 /**
3154 * Decodes and converts UTF8 bytes to UTF16 characters.
3155 * @param {!function():number|null} src Bytes source as a function returning the next byte respectively `null` if there
3156 * are no more bytes left.
3157 * @param {!function(number)} dst Characters destination as a function successively called with each converted char code.
3158 * @throws {RangeError} If a starting byte is invalid in UTF8
3159 * @throws {Error} If the last sequence is truncated. Has an array property `bytes` holding the remaining bytes.
3160 */
3161 utfx.decodeUTF8toUTF16 = function(src, dst) {
3162 utfx.decodeUTF8(src, function(cp) {
3163 utfx.UTF8toUTF16(cp, dst);
3164 });
3165 };
3166
3167 /**
3168 * Calculates the byte length of an UTF8 code point.
3169 * @param {number} cp UTF8 code point
3170 * @returns {number} Byte length
3171 */
3172 utfx.calculateCodePoint = function(cp) {
3173 return (cp < 0x80) ? 1 : (cp < 0x800) ? 2 : (cp < 0x10000) ? 3 : 4;
3174 };
3175
3176 /**
3177 * Calculates the number of UTF8 bytes required to store UTF8 code points.
3178 * @param {(!function():number|null)} src Code points source as a function returning the next code point respectively
3179 * `null` if there are no more code points left.
3180 * @returns {number} The number of UTF8 bytes required
3181 */
3182 utfx.calculateUTF8 = function(src) {
3183 var cp, l=0;
3184 while ((cp = src()) !== null)
3185 l += utfx.calculateCodePoint(cp);
3186 return l;
3187 };
3188
3189 /**
3190 * Calculates the number of UTF8 code points respectively UTF8 bytes required to store UTF16 char codes.
3191 * @param {(!function():number|null)} src Characters source as a function returning the next char code respectively
3192 * `null` if there are no more characters left.
3193 * @returns {!Array.<number>} The number of UTF8 code points at index 0 and the number of UTF8 bytes required at index 1.
3194 */
3195 utfx.calculateUTF16asUTF8 = function(src) {
3196 var n=0, l=0;
3197 utfx.UTF16toUTF8(src, function(cp) {
3198 ++n; l += utfx.calculateCodePoint(cp);
3199 });
3200 return [n,l];
3201 };
3202
3203 return utfx;
3204 }();
3205
3206 // encodings/utf8
3207
3208 /**
3209 * Encodes this ByteBuffer's contents between {@link ByteBuffer#offset} and {@link ByteBuffer#limit} to an UTF8 encoded
3210 * string.
3211 * @returns {string} Hex encoded string
3212 * @throws {RangeError} If `offset > limit`
3213 * @expose
3214 */
3215 ByteBufferPrototype.toUTF8 = function(begin, end) {
3216 if (typeof begin === 'undefined') begin = this.offset;
3217 if (typeof end === 'undefined') end = this.limit;
3218 if (!this.noAssert) {
3219 if (typeof begin !== 'number' || begin % 1 !== 0)
3220 throw TypeError("Illegal begin: Not an integer");
3221 begin >>>= 0;
3222 if (typeof end !== 'number' || end % 1 !== 0)
3223 throw TypeError("Illegal end: Not an integer");
3224 end >>>= 0;
3225 if (begin < 0 || begin > end || end > this.buffer.byteLength)
3226 throw RangeError("Illegal range: 0 <= "+begin+" <= "+end+" <= "+this.buffer.byteLength);
3227 }
3228 var sd; try {
3229 utfx.decodeUTF8toUTF16(function() {
3230 return begin < end ? this.view.getUint8(begin++) : null;
3231 }.bind(this), sd = stringDestination());
3232 } catch (e) {
3233 if (begin !== end)
3234 throw RangeError("Illegal range: Truncated data, "+begin+" != "+end);
3235 }
3236 return sd();
3237 };
3238
3239 /**
3240 * Decodes an UTF8 encoded string to a ByteBuffer.
3241 * @param {string} str String to decode
3242 * @param {boolean=} littleEndian Whether to use little or big endian byte order. Defaults to
3243 * {@link ByteBuffer.DEFAULT_ENDIAN}.
3244 * @param {boolean=} noAssert Whether to skip assertions of offsets and values. Defaults to
3245 * {@link ByteBuffer.DEFAULT_NOASSERT}.
3246 * @returns {!ByteBuffer} ByteBuffer
3247 * @expose
3248 */
3249 ByteBuffer.fromUTF8 = function(str, littleEndian, noAssert) {
3250 if (!noAssert)
3251 if (typeof str !== 'string')
3252 throw TypeError("Illegal str: Not a string");
3253 var bb = new ByteBuffer(utfx.calculateUTF16asUTF8(stringSource(str), true)[1], littleEndian, noAssert),
3254 i = 0;
3255 utfx.encodeUTF16toUTF8(stringSource(str), function(b) {
3256 bb.view.setUint8(i++, b);
3257 });
3258 bb.limit = i;
3259 return bb;
3260 };
3261
3262
3263 return ByteBuffer;
3264 }
3265
3266 /* CommonJS */ if (typeof require === 'function' && typeof module === 'object' && module && module.id && typeof exports === 'object' && exports)
3267 module['exports'] = (function() {
3268 var Long; try { Long = require("long"); } catch (e) {}
3269 return loadByteBuffer(Long);
3270 })();
3271 /* AMD */ else if (typeof define === 'function' && define["amd"])
3272 define("ByteBuffer", ["Long"], function(Long) { return loadByteBuffer(Long); });
3273 /* Global */ else
3274 (global["dcodeIO"] = global["dcodeIO"] || {})["ByteBuffer"] = loadByteBuffer(global["dcodeIO"]["Long"]);
3275
3276})(this);