UNPKG

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