blob: 9260038941636b9b558de48845604c0eb7682a3f [file] [log] [blame]
Feng Xiaoe841bac2015-12-11 17:09:20 -08001// Protocol Buffers - Google's data interchange format
2// Copyright 2008 Google Inc. All rights reserved.
3// https://developers.google.com/protocol-buffers/
4//
5// Redistribution and use in source and binary forms, with or without
6// modification, are permitted provided that the following conditions are
7// met:
8//
9// * Redistributions of source code must retain the above copyright
10// notice, this list of conditions and the following disclaimer.
11// * Redistributions in binary form must reproduce the above
12// copyright notice, this list of conditions and the following disclaimer
13// in the documentation and/or other materials provided with the
14// distribution.
15// * Neither the name of Google Inc. nor the names of its
16// contributors may be used to endorse or promote products derived from
17// this software without specific prior written permission.
18//
19// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31/**
32 * @fileoverview This file contains helper code used by jspb.BinaryReader
33 * and BinaryWriter.
34 *
35 * @author aappleby@google.com (Austin Appleby)
36 */
37
38goog.provide('jspb.utils');
39
40goog.require('goog.asserts');
41goog.require('goog.crypt.base64');
42goog.require('goog.string');
43goog.require('jspb.BinaryConstants');
44
45
46/**
47 * Javascript can't natively handle 64-bit data types, so to manipulate them we
48 * have to split them into two 32-bit halves and do the math manually.
49 *
50 * Instead of instantiating and passing small structures around to do this, we
51 * instead just use two global temporary values. This one stores the low 32
52 * bits of a split value - for example, if the original value was a 64-bit
53 * integer, this temporary value will contain the low 32 bits of that integer.
54 * If the original value was a double, this temporary value will contain the
55 * low 32 bits of the binary representation of that double, etcetera.
56 * @type {number}
57 */
58jspb.utils.split64Low = 0;
59
60
61/**
62 * And correspondingly, this temporary variable will contain the high 32 bits
63 * of whatever value was split.
64 * @type {number}
65 */
66jspb.utils.split64High = 0;
67
68
69/**
70 * Splits an unsigned Javascript integer into two 32-bit halves and stores it
71 * in the temp values above.
72 * @param {number} value The number to split.
73 */
74jspb.utils.splitUint64 = function(value) {
75 // Extract low 32 bits and high 32 bits as unsigned integers.
76 var lowBits = value >>> 0;
77 var highBits = Math.floor((value - lowBits) /
78 jspb.BinaryConstants.TWO_TO_32) >>> 0;
79
80 jspb.utils.split64Low = lowBits;
81 jspb.utils.split64High = highBits;
82};
83
84
85/**
86 * Splits a signed Javascript integer into two 32-bit halves and stores it in
87 * the temp values above.
88 * @param {number} value The number to split.
89 */
90jspb.utils.splitInt64 = function(value) {
91 // Convert to sign-magnitude representation.
92 var sign = (value < 0);
93 value = Math.abs(value);
94
95 // Extract low 32 bits and high 32 bits as unsigned integers.
96 var lowBits = value >>> 0;
97 var highBits = Math.floor((value - lowBits) /
98 jspb.BinaryConstants.TWO_TO_32);
99 highBits = highBits >>> 0;
100
101 // Perform two's complement conversion if the sign bit was set.
102 if (sign) {
103 highBits = ~highBits >>> 0;
104 lowBits = ~lowBits >>> 0;
105 lowBits += 1;
106 if (lowBits > 0xFFFFFFFF) {
107 lowBits = 0;
108 highBits++;
109 if (highBits > 0xFFFFFFFF) highBits = 0;
110 }
111 }
112
113 jspb.utils.split64Low = lowBits;
114 jspb.utils.split64High = highBits;
115};
116
117
118/**
119 * Convers a signed Javascript integer into zigzag format, splits it into two
120 * 32-bit halves, and stores it in the temp values above.
121 * @param {number} value The number to split.
122 */
123jspb.utils.splitZigzag64 = function(value) {
124 // Convert to sign-magnitude and scale by 2 before we split the value.
125 var sign = (value < 0);
126 value = Math.abs(value) * 2;
127
128 jspb.utils.splitUint64(value);
129 var lowBits = jspb.utils.split64Low;
130 var highBits = jspb.utils.split64High;
131
132 // If the value is negative, subtract 1 from the split representation so we
133 // don't lose the sign bit due to precision issues.
134 if (sign) {
135 if (lowBits == 0) {
136 if (highBits == 0) {
137 lowBits = 0xFFFFFFFF;
138 highBits = 0xFFFFFFFF;
139 } else {
140 highBits--;
141 lowBits = 0xFFFFFFFF;
142 }
143 } else {
144 lowBits--;
145 }
146 }
147
148 jspb.utils.split64Low = lowBits;
149 jspb.utils.split64High = highBits;
150};
151
152
153/**
154 * Converts a floating-point number into 32-bit IEEE representation and stores
155 * it in the temp values above.
156 * @param {number} value
157 */
158jspb.utils.splitFloat32 = function(value) {
159 var sign = (value < 0) ? 1 : 0;
160 value = sign ? -value : value;
161 var exp;
162 var mant;
163
164 // Handle zeros.
165 if (value === 0) {
166 if ((1 / value) > 0) {
167 // Positive zero.
168 jspb.utils.split64High = 0;
169 jspb.utils.split64Low = 0x00000000;
170 } else {
171 // Negative zero.
172 jspb.utils.split64High = 0;
173 jspb.utils.split64Low = 0x80000000;
174 }
175 return;
176 }
177
178 // Handle nans.
179 if (isNaN(value)) {
180 jspb.utils.split64High = 0;
181 jspb.utils.split64Low = 0x7FFFFFFF;
182 return;
183 }
184
185 // Handle infinities.
186 if (value > jspb.BinaryConstants.FLOAT32_MAX) {
187 jspb.utils.split64High = 0;
188 jspb.utils.split64Low = ((sign << 31) | (0x7F800000)) >>> 0;
189 return;
190 }
191
192 // Handle denormals.
193 if (value < jspb.BinaryConstants.FLOAT32_MIN) {
194 // Number is a denormal.
195 mant = Math.round(value / Math.pow(2, -149));
196 jspb.utils.split64High = 0;
197 jspb.utils.split64Low = ((sign << 31) | mant) >>> 0;
198 return;
199 }
200
201 exp = Math.floor(Math.log(value) / Math.LN2);
202 mant = value * Math.pow(2, -exp);
203 mant = Math.round(mant * jspb.BinaryConstants.TWO_TO_23) & 0x7FFFFF;
204
205 jspb.utils.split64High = 0;
206 jspb.utils.split64Low = ((sign << 31) | ((exp + 127) << 23) | mant) >>> 0;
207};
208
209
210/**
211 * Converts a floating-point number into 64-bit IEEE representation and stores
212 * it in the temp values above.
213 * @param {number} value
214 */
215jspb.utils.splitFloat64 = function(value) {
216 var sign = (value < 0) ? 1 : 0;
217 value = sign ? -value : value;
218
219 // Handle zeros.
220 if (value === 0) {
221 if ((1 / value) > 0) {
222 // Positive zero.
223 jspb.utils.split64High = 0x00000000;
224 jspb.utils.split64Low = 0x00000000;
225 } else {
226 // Negative zero.
227 jspb.utils.split64High = 0x80000000;
228 jspb.utils.split64Low = 0x00000000;
229 }
230 return;
231 }
232
233 // Handle nans.
234 if (isNaN(value)) {
235 jspb.utils.split64High = 0x7FFFFFFF;
236 jspb.utils.split64Low = 0xFFFFFFFF;
237 return;
238 }
239
240 // Handle infinities.
241 if (value > jspb.BinaryConstants.FLOAT64_MAX) {
242 jspb.utils.split64High = ((sign << 31) | (0x7FF00000)) >>> 0;
243 jspb.utils.split64Low = 0;
244 return;
245 }
246
247 // Handle denormals.
248 if (value < jspb.BinaryConstants.FLOAT64_MIN) {
249 // Number is a denormal.
250 var mant = value / Math.pow(2, -1074);
251 var mantHigh = (mant / jspb.BinaryConstants.TWO_TO_32);
252 jspb.utils.split64High = ((sign << 31) | mantHigh) >>> 0;
253 jspb.utils.split64Low = (mant >>> 0);
254 return;
255 }
256
257 var exp = Math.floor(Math.log(value) / Math.LN2);
258 if (exp == 1024) exp = 1023;
259 var mant = value * Math.pow(2, -exp);
260
261 var mantHigh = (mant * jspb.BinaryConstants.TWO_TO_20) & 0xFFFFF;
262 var mantLow = (mant * jspb.BinaryConstants.TWO_TO_52) >>> 0;
263
264 jspb.utils.split64High =
265 ((sign << 31) | ((exp + 1023) << 20) | mantHigh) >>> 0;
266 jspb.utils.split64Low = mantLow;
267};
268
269
270/**
271 * Converts an 8-character hash string into two 32-bit numbers and stores them
272 * in the temp values above.
273 * @param {string} hash
274 */
275jspb.utils.splitHash64 = function(hash) {
276 var a = hash.charCodeAt(0);
277 var b = hash.charCodeAt(1);
278 var c = hash.charCodeAt(2);
279 var d = hash.charCodeAt(3);
280 var e = hash.charCodeAt(4);
281 var f = hash.charCodeAt(5);
282 var g = hash.charCodeAt(6);
283 var h = hash.charCodeAt(7);
284
285 jspb.utils.split64Low = (a + (b << 8) + (c << 16) + (d << 24)) >>> 0;
286 jspb.utils.split64High = (e + (f << 8) + (g << 16) + (h << 24)) >>> 0;
287};
288
289
290/**
291 * Joins two 32-bit values into a 64-bit unsigned integer. Precision will be
292 * lost if the result is greater than 2^52.
293 * @param {number} bitsLow
294 * @param {number} bitsHigh
295 * @return {number}
296 */
297jspb.utils.joinUint64 = function(bitsLow, bitsHigh) {
298 return bitsHigh * jspb.BinaryConstants.TWO_TO_32 + bitsLow;
299};
300
301
302/**
303 * Joins two 32-bit values into a 64-bit signed integer. Precision will be lost
304 * if the result is greater than 2^52.
305 * @param {number} bitsLow
306 * @param {number} bitsHigh
307 * @return {number}
308 */
309jspb.utils.joinInt64 = function(bitsLow, bitsHigh) {
310 // If the high bit is set, do a manual two's complement conversion.
311 var sign = (bitsHigh & 0x80000000);
312 if (sign) {
313 bitsLow = (~bitsLow + 1) >>> 0;
314 bitsHigh = ~bitsHigh >>> 0;
315 if (bitsLow == 0) {
316 bitsHigh = (bitsHigh + 1) >>> 0;
317 }
318 }
319
320 var result = jspb.utils.joinUint64(bitsLow, bitsHigh);
321 return sign ? -result : result;
322};
323
324
325/**
326 * Joins two 32-bit values into a 64-bit unsigned integer and applies zigzag
327 * decoding. Precision will be lost if the result is greater than 2^52.
328 * @param {number} bitsLow
329 * @param {number} bitsHigh
330 * @return {number}
331 */
332jspb.utils.joinZigzag64 = function(bitsLow, bitsHigh) {
333 // Extract the sign bit and shift right by one.
334 var sign = bitsLow & 1;
335 bitsLow = ((bitsLow >>> 1) | (bitsHigh << 31)) >>> 0;
336 bitsHigh = bitsHigh >>> 1;
337
338 // Increment the split value if the sign bit was set.
339 if (sign) {
340 bitsLow = (bitsLow + 1) >>> 0;
341 if (bitsLow == 0) {
342 bitsHigh = (bitsHigh + 1) >>> 0;
343 }
344 }
345
346 var result = jspb.utils.joinUint64(bitsLow, bitsHigh);
347 return sign ? -result : result;
348};
349
350
351/**
352 * Joins two 32-bit values into a 32-bit IEEE floating point number and
353 * converts it back into a Javascript number.
354 * @param {number} bitsLow The low 32 bits of the binary number;
355 * @param {number} bitsHigh The high 32 bits of the binary number.
356 * @return {number}
357 */
358jspb.utils.joinFloat32 = function(bitsLow, bitsHigh) {
359 var sign = ((bitsLow >> 31) * 2 + 1);
360 var exp = (bitsLow >>> 23) & 0xFF;
361 var mant = bitsLow & 0x7FFFFF;
362
363 if (exp == 0xFF) {
364 if (mant) {
365 return NaN;
366 } else {
367 return sign * Infinity;
368 }
369 }
370
371 if (exp == 0) {
372 // Denormal.
373 return sign * Math.pow(2, -149) * mant;
374 } else {
375 return sign * Math.pow(2, exp - 150) *
376 (mant + Math.pow(2, 23));
377 }
378};
379
380
381/**
382 * Joins two 32-bit values into a 64-bit IEEE floating point number and
383 * converts it back into a Javascript number.
384 * @param {number} bitsLow The low 32 bits of the binary number;
385 * @param {number} bitsHigh The high 32 bits of the binary number.
386 * @return {number}
387 */
388jspb.utils.joinFloat64 = function(bitsLow, bitsHigh) {
389 var sign = ((bitsHigh >> 31) * 2 + 1);
390 var exp = (bitsHigh >>> 20) & 0x7FF;
391 var mant = jspb.BinaryConstants.TWO_TO_32 * (bitsHigh & 0xFFFFF) + bitsLow;
392
393 if (exp == 0x7FF) {
394 if (mant) {
395 return NaN;
396 } else {
397 return sign * Infinity;
398 }
399 }
400
401 if (exp == 0) {
402 // Denormal.
403 return sign * Math.pow(2, -1074) * mant;
404 } else {
405 return sign * Math.pow(2, exp - 1075) *
406 (mant + jspb.BinaryConstants.TWO_TO_52);
407 }
408};
409
410
411/**
412 * Joins two 32-bit values into an 8-character hash string.
413 * @param {number} bitsLow
414 * @param {number} bitsHigh
415 * @return {string}
416 */
417jspb.utils.joinHash64 = function(bitsLow, bitsHigh) {
418 var a = (bitsLow >>> 0) & 0xFF;
419 var b = (bitsLow >>> 8) & 0xFF;
420 var c = (bitsLow >>> 16) & 0xFF;
421 var d = (bitsLow >>> 24) & 0xFF;
422 var e = (bitsHigh >>> 0) & 0xFF;
423 var f = (bitsHigh >>> 8) & 0xFF;
424 var g = (bitsHigh >>> 16) & 0xFF;
425 var h = (bitsHigh >>> 24) & 0xFF;
426
427 return String.fromCharCode(a, b, c, d, e, f, g, h);
428};
429
430
431/**
432 * Individual digits for number->string conversion.
433 * @const {!Array.<number>}
434 */
435jspb.utils.DIGITS = [
436 '0', '1', '2', '3', '4', '5', '6', '7',
437 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
438];
439
440
441/**
442 * Losslessly converts a 64-bit unsigned integer in 32:32 split representation
443 * into a decimal string.
444 * @param {number} bitsLow The low 32 bits of the binary number;
445 * @param {number} bitsHigh The high 32 bits of the binary number.
446 * @return {string} The binary number represented as a string.
447 */
448jspb.utils.joinUnsignedDecimalString = function(bitsLow, bitsHigh) {
449 // Skip the expensive conversion if the number is small enough to use the
450 // built-in conversions.
451 if (bitsHigh <= 0x1FFFFF) {
452 return '' + (jspb.BinaryConstants.TWO_TO_32 * bitsHigh + bitsLow);
453 }
454
455 // What this code is doing is essentially converting the input number from
456 // base-2 to base-1e7, which allows us to represent the 64-bit range with
457 // only 3 (very large) digits. Those digits are then trivial to convert to
458 // a base-10 string.
459
460 // The magic numbers used here are -
461 // 2^24 = 16777216 = (1,6777216) in base-1e7.
462 // 2^48 = 281474976710656 = (2,8147497,6710656) in base-1e7.
463
464 // Split 32:32 representation into 16:24:24 representation so our
465 // intermediate digits don't overflow.
466 var low = bitsLow & 0xFFFFFF;
467 var mid = (((bitsLow >>> 24) | (bitsHigh << 8)) >>> 0) & 0xFFFFFF;
468 var high = (bitsHigh >> 16) & 0xFFFF;
469
470 // Assemble our three base-1e7 digits, ignoring carries. The maximum
471 // value in a digit at this step is representable as a 48-bit integer, which
472 // can be stored in a 64-bit floating point number.
473 var digitA = low + (mid * 6777216) + (high * 6710656);
474 var digitB = mid + (high * 8147497);
475 var digitC = (high * 2);
476
477 // Apply carries from A to B and from B to C.
478 var base = 10000000;
479 if (digitA >= base) {
480 digitB += Math.floor(digitA / base);
481 digitA %= base;
482 }
483
484 if (digitB >= base) {
485 digitC += Math.floor(digitB / base);
486 digitB %= base;
487 }
488
489 // Convert base-1e7 digits to base-10, omitting leading zeroes.
490 var table = jspb.utils.DIGITS;
491 var start = false;
492 var result = '';
493
494 function emit(digit) {
495 var temp = base;
496 for (var i = 0; i < 7; i++) {
497 temp /= 10;
498 var decimalDigit = ((digit / temp) % 10) >>> 0;
499 if ((decimalDigit == 0) && !start) continue;
500 start = true;
501 result += table[decimalDigit];
502 }
503 }
504
505 if (digitC || start) emit(digitC);
506 if (digitB || start) emit(digitB);
507 if (digitA || start) emit(digitA);
508
509 return result;
510};
511
512
513/**
514 * Losslessly converts a 64-bit signed integer in 32:32 split representation
515 * into a decimal string.
516 * @param {number} bitsLow The low 32 bits of the binary number;
517 * @param {number} bitsHigh The high 32 bits of the binary number.
518 * @return {string} The binary number represented as a string.
519 */
520jspb.utils.joinSignedDecimalString = function(bitsLow, bitsHigh) {
521 // If we're treating the input as a signed value and the high bit is set, do
522 // a manual two's complement conversion before the decimal conversion.
523 var negative = (bitsHigh & 0x80000000);
524 if (negative) {
525 bitsLow = (~bitsLow + 1) >>> 0;
526 var carry = (bitsLow == 0) ? 1 : 0;
527 bitsHigh = (~bitsHigh + carry) >>> 0;
528 }
529
530 var result = jspb.utils.joinUnsignedDecimalString(bitsLow, bitsHigh);
531 return negative ? '-' + result : result;
532};
533
534
535/**
536 * Convert an 8-character hash string representing either a signed or unsigned
537 * 64-bit integer into its decimal representation without losing accuracy.
538 * @param {string} hash The hash string to convert.
539 * @param {boolean} signed True if we should treat the hash string as encoding
540 * a signed integer.
541 * @return {string}
542 */
543jspb.utils.hash64ToDecimalString = function(hash, signed) {
544 jspb.utils.splitHash64(hash);
545 var bitsLow = jspb.utils.split64Low;
546 var bitsHigh = jspb.utils.split64High;
547 return signed ?
548 jspb.utils.joinSignedDecimalString(bitsLow, bitsHigh) :
549 jspb.utils.joinUnsignedDecimalString(bitsLow, bitsHigh);
550};
551
552
553/**
554 * Converts an array of 8-character hash strings into their decimal
555 * representations.
556 * @param {!Array.<string>} hashes The array of hash strings to convert.
557 * @param {boolean} signed True if we should treat the hash string as encoding
558 * a signed integer.
559 * @return {!Array.<string>}
560 */
561jspb.utils.hash64ArrayToDecimalStrings = function(hashes, signed) {
562 var result = new Array(hashes.length);
563 for (var i = 0; i < hashes.length; i++) {
564 result[i] = jspb.utils.hash64ToDecimalString(hashes[i], signed);
565 }
566 return result;
567};
568
569
570/**
571 * Converts an 8-character hash string into its hexadecimal representation.
572 * @param {string} hash
573 * @return {string}
574 */
575jspb.utils.hash64ToHexString = function(hash) {
576 var temp = new Array(18);
577 temp[0] = '0';
578 temp[1] = 'x';
579
580 for (var i = 0; i < 8; i++) {
581 var c = hash.charCodeAt(7 - i);
582 temp[i * 2 + 2] = jspb.utils.DIGITS[c >> 4];
583 temp[i * 2 + 3] = jspb.utils.DIGITS[c & 0xF];
584 }
585
586 var result = temp.join('');
587 return result;
588};
589
590
591/**
592 * Converts a '0x<16 digits>' hex string into its hash string representation.
593 * @param {string} hex
594 * @return {string}
595 */
596jspb.utils.hexStringToHash64 = function(hex) {
597 hex = hex.toLowerCase();
598 goog.asserts.assert(hex.length == 18);
599 goog.asserts.assert(hex[0] == '0');
600 goog.asserts.assert(hex[1] == 'x');
601
602 var result = '';
603 for (var i = 0; i < 8; i++) {
604 var hi = jspb.utils.DIGITS.indexOf(hex[i * 2 + 2]);
605 var lo = jspb.utils.DIGITS.indexOf(hex[i * 2 + 3]);
606 result = String.fromCharCode(hi * 16 + lo) + result;
607 }
608
609 return result;
610};
611
612
613/**
614 * Convert an 8-character hash string representing either a signed or unsigned
615 * 64-bit integer into a Javascript number. Will lose accuracy if the result is
616 * larger than 2^52.
617 * @param {string} hash The hash string to convert.
618 * @param {boolean} signed True if the has should be interpreted as a signed
619 * number.
620 * @return {number}
621 */
622jspb.utils.hash64ToNumber = function(hash, signed) {
623 jspb.utils.splitHash64(hash);
624 var bitsLow = jspb.utils.split64Low;
625 var bitsHigh = jspb.utils.split64High;
626 return signed ? jspb.utils.joinInt64(bitsLow, bitsHigh) :
627 jspb.utils.joinUint64(bitsLow, bitsHigh);
628};
629
630
631/**
632 * Convert a Javascript number into an 8-character hash string. Will lose
633 * precision if the value is non-integral or greater than 2^64.
634 * @param {number} value The integer to convert.
635 * @return {string}
636 */
637jspb.utils.numberToHash64 = function(value) {
638 jspb.utils.splitInt64(value);
639 return jspb.utils.joinHash64(jspb.utils.split64Low,
640 jspb.utils.split64High);
641};
642
643
644/**
645 * Counts the number of contiguous varints in a buffer.
646 * @param {!Uint8Array} buffer The buffer to scan.
647 * @param {number} start The starting point in the buffer to scan.
648 * @param {number} end The end point in the buffer to scan.
649 * @return {number} The number of varints in the buffer.
650 */
651jspb.utils.countVarints = function(buffer, start, end) {
652 // Count how many high bits of each byte were set in the buffer.
653 var count = 0;
654 for (var i = start; i < end; i++) {
655 count += buffer[i] >> 7;
656 }
657
658 // The number of varints in the buffer equals the size of the buffer minus
659 // the number of non-terminal bytes in the buffer (those with the high bit
660 // set).
661 return (end - start) - count;
662};
663
664
665/**
666 * Counts the number of contiguous varint fields with the given field number in
667 * the buffer.
668 * @param {!Uint8Array} buffer The buffer to scan.
669 * @param {number} start The starting point in the buffer to scan.
670 * @param {number} end The end point in the buffer to scan.
671 * @param {number} field The field number to count.
672 * @return {number} The number of matching fields in the buffer.
673 */
674jspb.utils.countVarintFields = function(buffer, start, end, field) {
675 var count = 0;
676 var cursor = start;
677 var tag = field * 8 + jspb.BinaryConstants.WireType.VARINT;
678
679 if (tag < 128) {
680 // Single-byte field tag, we can use a slightly quicker count.
681 while (cursor < end) {
682 // Skip the field tag, or exit if we find a non-matching tag.
683 if (buffer[cursor++] != tag) return count;
684
685 // Field tag matches, we've found a valid field.
686 count++;
687
688 // Skip the varint.
689 while (1) {
690 var x = buffer[cursor++];
691 if ((x & 0x80) == 0) break;
692 }
693 }
694 } else {
695 while (cursor < end) {
696 // Skip the field tag, or exit if we find a non-matching tag.
697 var temp = tag;
698 while (temp > 128) {
699 if (buffer[cursor] != ((temp & 0x7F) | 0x80)) return count;
700 cursor++;
701 temp >>= 7;
702 }
703 if (buffer[cursor++] != temp) return count;
704
705 // Field tag matches, we've found a valid field.
706 count++;
707
708 // Skip the varint.
709 while (1) {
710 var x = buffer[cursor++];
711 if ((x & 0x80) == 0) break;
712 }
713 }
714 }
715 return count;
716};
717
718
719/**
720 * Counts the number of contiguous fixed32 fields with the given tag in the
721 * buffer.
722 * @param {!Uint8Array} buffer The buffer to scan.
723 * @param {number} start The starting point in the buffer to scan.
724 * @param {number} end The end point in the buffer to scan.
725 * @param {number} tag The tag value to count.
726 * @param {number} stride The number of bytes to skip per field.
727 * @return {number} The number of fields with a matching tag in the buffer.
728 * @private
729 */
730jspb.utils.countFixedFields_ =
731 function(buffer, start, end, tag, stride) {
732 var count = 0;
733 var cursor = start;
734
735 if (tag < 128) {
736 // Single-byte field tag, we can use a slightly quicker count.
737 while (cursor < end) {
738 // Skip the field tag, or exit if we find a non-matching tag.
739 if (buffer[cursor++] != tag) return count;
740
741 // Field tag matches, we've found a valid field.
742 count++;
743
744 // Skip the value.
745 cursor += stride;
746 }
747 } else {
748 while (cursor < end) {
749 // Skip the field tag, or exit if we find a non-matching tag.
750 var temp = tag;
751 while (temp > 128) {
752 if (buffer[cursor++] != ((temp & 0x7F) | 0x80)) return count;
753 temp >>= 7;
754 }
755 if (buffer[cursor++] != temp) return count;
756
757 // Field tag matches, we've found a valid field.
758 count++;
759
760 // Skip the value.
761 cursor += stride;
762 }
763 }
764 return count;
765};
766
767
768/**
769 * Counts the number of contiguous fixed32 fields with the given field number
770 * in the buffer.
771 * @param {!Uint8Array} buffer The buffer to scan.
772 * @param {number} start The starting point in the buffer to scan.
773 * @param {number} end The end point in the buffer to scan.
774 * @param {number} field The field number to count.
775 * @return {number} The number of matching fields in the buffer.
776 */
777jspb.utils.countFixed32Fields = function(buffer, start, end, field) {
778 var tag = field * 8 + jspb.BinaryConstants.WireType.FIXED32;
779 return jspb.utils.countFixedFields_(buffer, start, end, tag, 4);
780};
781
782
783/**
784 * Counts the number of contiguous fixed64 fields with the given field number
785 * in the buffer.
786 * @param {!Uint8Array} buffer The buffer to scan.
787 * @param {number} start The starting point in the buffer to scan.
788 * @param {number} end The end point in the buffer to scan.
789 * @param {number} field The field number to count
790 * @return {number} The number of matching fields in the buffer.
791 */
792jspb.utils.countFixed64Fields = function(buffer, start, end, field) {
793 var tag = field * 8 + jspb.BinaryConstants.WireType.FIXED64;
794 return jspb.utils.countFixedFields_(buffer, start, end, tag, 8);
795};
796
797
798/**
799 * Counts the number of contiguous delimited fields with the given field number
800 * in the buffer.
801 * @param {!Uint8Array} buffer The buffer to scan.
802 * @param {number} start The starting point in the buffer to scan.
803 * @param {number} end The end point in the buffer to scan.
804 * @param {number} field The field number to count.
805 * @return {number} The number of matching fields in the buffer.
806 */
807jspb.utils.countDelimitedFields = function(buffer, start, end, field) {
808 var count = 0;
809 var cursor = start;
810 var tag = field * 8 + jspb.BinaryConstants.WireType.DELIMITED;
811
812 while (cursor < end) {
813 // Skip the field tag, or exit if we find a non-matching tag.
814 var temp = tag;
815 while (temp > 128) {
816 if (buffer[cursor++] != ((temp & 0x7F) | 0x80)) return count;
817 temp >>= 7;
818 }
819 if (buffer[cursor++] != temp) return count;
820
821 // Field tag matches, we've found a valid field.
822 count++;
823
824 // Decode the length prefix.
825 var length = 0;
826 var shift = 1;
827 while (1) {
828 temp = buffer[cursor++];
829 length += (temp & 0x7f) * shift;
830 shift *= 128;
831 if ((temp & 0x80) == 0) break;
832 }
833
834 // Advance the cursor past the blob.
835 cursor += length;
836 }
837 return count;
838};
839
840
841/**
842 * Clones a scalar field. Pulling this out to a helper method saves us a few
843 * bytes of generated code.
844 * @param {Array} array
845 * @return {Array}
846 */
847jspb.utils.cloneRepeatedScalarField = function(array) {
848 return array ? array.slice() : null;
849};
850
851
852/**
853 * Clones an array of messages using the provided cloner function.
854 * @param {Array.<jspb.BinaryMessage>} messages
855 * @param {jspb.ClonerFunction} cloner
856 * @return {Array.<jspb.BinaryMessage>}
857 */
858jspb.utils.cloneRepeatedMessageField = function(messages, cloner) {
859 if (messages === null) return null;
860 var result = [];
861 for (var i = 0; i < messages.length; i++) {
862 result.push(cloner(messages[i]));
863 }
864 return result;
865};
866
867
868/**
869 * Clones an array of byte blobs.
870 * @param {Array.<Uint8Array>} blobs
871 * @return {Array.<Uint8Array>}
872 */
873jspb.utils.cloneRepeatedBlobField = function(blobs) {
874 if (blobs === null) return null;
875 var result = [];
876 for (var i = 0; i < blobs.length; i++) {
877 result.push(new Uint8Array(blobs[i]));
878 }
879 return result;
880};
881
882
883/**
884 * String-ify bytes for text format. Should be optimized away in non-debug.
885 * The returned string uses \xXX escapes for all values and is itself quoted.
886 * [1, 31] serializes to '"\x01\x1f"'.
887 * @param {jspb.ByteSource} byteSource The bytes to serialize.
888 * @param {boolean=} opt_stringIsRawBytes The string is interpreted as a series
889 * of raw bytes rather than base64 data.
890 * @return {string} Stringified bytes for text format.
891 */
892jspb.utils.debugBytesToTextFormat = function(byteSource,
893 opt_stringIsRawBytes) {
894 var s = '"';
895 if (byteSource) {
896 var bytes =
897 jspb.utils.byteSourceToUint8Array(byteSource, opt_stringIsRawBytes);
898 for (var i = 0; i < bytes.length; i++) {
899 s += '\\x';
900 if (bytes[i] < 16) s += '0';
901 s += bytes[i].toString(16);
902 }
903 }
904 return s + '"';
905};
906
907
908/**
909 * String-ify a scalar for text format. Should be optimized away in non-debug.
910 * @param {string|number|boolean} scalar The scalar to stringify.
911 * @return {string} Stringified scalar for text format.
912 */
913jspb.utils.debugScalarToTextFormat = function(scalar) {
914 if (goog.isString(scalar)) {
915 return goog.string.quote(scalar);
916 } else {
917 return scalar.toString();
918 }
919};
920
921
922/**
923 * Utility function: convert a string with codepoints 0--255 inclusive to a
924 * Uint8Array. If any codepoints greater than 255 exist in the string, throws an
925 * exception.
926 * @param {string} str
927 * @return {!Uint8Array}
928 * @private
929 */
930jspb.utils.stringToByteArray_ = function(str) {
931 var arr = new Uint8Array(str.length);
932 for (var i = 0; i < str.length; i++) {
933 var codepoint = str.charCodeAt(i);
934 if (codepoint > 255) {
935 throw new Error('Conversion error: string contains codepoint ' +
936 'outside of byte range');
937 }
938 arr[i] = codepoint;
939 }
940 return arr;
941};
942
943
944/**
945 * Converts any type defined in jspb.ByteSource into a Uint8Array.
946 * @param {!jspb.ByteSource} data
947 * @param {boolean=} opt_stringIsRawBytes Interpret a string as a series of raw
948 * bytes (encoded as codepoints 0--255 inclusive) rather than base64 data
949 * (default behavior).
950 * @return {!Uint8Array}
951 * @suppress {invalidCasts}
952 */
953jspb.utils.byteSourceToUint8Array = function(data, opt_stringIsRawBytes) {
954 if (data.constructor === Uint8Array) {
955 return /** @type {!Uint8Array} */(data);
956 }
957
958 if (data.constructor === ArrayBuffer) {
959 data = /** @type {!ArrayBuffer} */(data);
960 return /** @type {!Uint8Array} */(new Uint8Array(data));
961 }
962
963 if (data.constructor === Array) {
964 data = /** @type {!Array.<number>} */(data);
965 return /** @type {!Uint8Array} */(new Uint8Array(data));
966 }
967
968 if (data.constructor === String) {
969 data = /** @type {string} */(data);
970 if (opt_stringIsRawBytes) {
971 return jspb.utils.stringToByteArray_(data);
972 } else {
973 return goog.crypt.base64.decodeStringToUint8Array(data);
974 }
975 }
976
977 goog.asserts.fail('Type not convertible to Uint8Array.');
978 return /** @type {!Uint8Array} */(new Uint8Array(0));
979};