• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/**
2 * @fileoverview Helper methods for typed arrays.
3 */
4goog.module('protobuf.binary.typedArrays');
5
6const {assert} = goog.require('goog.asserts');
7
8/**
9 * @param {!ArrayBuffer} buffer1
10 * @param {!ArrayBuffer} buffer2
11 * @return {boolean}
12 */
13function arrayBufferEqual(buffer1, buffer2) {
14  if (!buffer1 || !buffer2) {
15    throw new Error('Buffer shouldn\'t be empty');
16  }
17
18  const array1 = new Uint8Array(buffer1);
19  const array2 = new Uint8Array(buffer2);
20
21  return uint8ArrayEqual(array1, array2);
22}
23
24/**
25 * @param {!Uint8Array} array1
26 * @param {!Uint8Array} array2
27 * @return {boolean}
28 */
29function uint8ArrayEqual(array1, array2) {
30  if (array1 === array2) {
31    return true;
32  }
33
34  if (array1.byteLength !== array2.byteLength) {
35    return false;
36  }
37
38  for (let i = 0; i < array1.byteLength; i++) {
39    if (array1[i] !== array2[i]) {
40      return false;
41    }
42  }
43  return true;
44}
45
46/**
47 * ArrayBuffer.prototype.slice, but fallback to manual copy if missing.
48 * @param {!ArrayBuffer} buffer
49 * @param {number} start
50 * @param {number=} end
51 * @return {!ArrayBuffer} New array buffer with given the contents of `buffer`.
52 */
53function arrayBufferSlice(buffer, start, end = undefined) {
54  // The fallback isn't fully compatible with ArrayBuffer.slice, enforce
55  // strict requirements on start/end.
56  // Spec:
57  // https://www.ecma-international.org/ecma-262/6.0/#sec-arraybuffer.prototype.slice
58  assert(start >= 0);
59  assert(end === undefined || (end >= 0 && end <= buffer.byteLength));
60  assert((end === undefined ? buffer.byteLength : end) >= start);
61  if (buffer.slice) {
62    const slicedBuffer = buffer.slice(start, end);
63    // The ArrayBuffer.prototype.slice function was flawed before iOS 12.2. This
64    // causes 0-length results when passing undefined or a number greater
65    // than 32 bits for the optional end value. In these cases, we fall back to
66    // using our own slice implementation.
67    // More details: https://bugs.webkit.org/show_bug.cgi?id=185127
68    if (slicedBuffer.byteLength !== 0 || (start === end)) {
69      return slicedBuffer;
70    }
71  }
72  const realEnd = end == null ? buffer.byteLength : end;
73  const length = realEnd - start;
74  assert(length >= 0);
75  const view = new Uint8Array(buffer, start, length);
76  // A TypedArray constructed from another Typed array copies the data.
77  const clone = new Uint8Array(view);
78
79  return clone.buffer;
80}
81
82/**
83 * Returns a new Uint8Array with the size and contents of the given
84 * ArrayBufferView. ArrayBufferView is an interface implemented by DataView,
85 * Uint8Array and all typed arrays.
86 * @param {!ArrayBufferView} view
87 * @return {!Uint8Array}
88 */
89function cloneArrayBufferView(view) {
90  return new Uint8Array(arrayBufferSlice(
91      view.buffer, view.byteOffset, view.byteOffset + view.byteLength));
92}
93
94/**
95 * Returns a 32 bit number for the corresponding Uint8Array.
96 * @param {!Uint8Array} array
97 * @return {number}
98 */
99function hashUint8Array(array) {
100  const prime = 31;
101  let result = 17;
102
103  for (let i = 0; i < array.length; i++) {
104    // '| 0' ensures signed 32 bits
105    result = (result * prime + array[i]) | 0;
106  }
107  return result;
108}
109
110exports = {
111  arrayBufferEqual,
112  uint8ArrayEqual,
113  arrayBufferSlice,
114  cloneArrayBufferView,
115  hashUint8Array,
116};
117