• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/**
2 * @fileoverview Provides ByteString as a basic data type for protos.
3 */
4goog.module('protobuf.ByteString');
5
6const base64 = goog.require('goog.crypt.base64');
7const {arrayBufferSlice, cloneArrayBufferView, hashUint8Array, uint8ArrayEqual} = goog.require('protobuf.binary.typedArrays');
8
9/**
10 * Immutable sequence of bytes.
11 *
12 * Bytes can be obtained as an ArrayBuffer or a base64 encoded string.
13 * @final
14 */
15class ByteString {
16  /**
17   * @param {?Uint8Array} bytes
18   * @param {?string} base64
19   * @private
20   */
21  constructor(bytes, base64) {
22    /** @private {?Uint8Array}*/
23    this.bytes_ = bytes;
24    /** @private {?string} */
25    this.base64_ = base64;
26    /** @private {number} */
27    this.hashCode_ = 0;
28  }
29
30  /**
31   * Constructs a ByteString instance from a base64 string.
32   * @param {string} value
33   * @return {!ByteString}
34   */
35  static fromBase64String(value) {
36    if (value == null) {
37      throw new Error('value must not be null');
38    }
39    return new ByteString(/* bytes */ null, value);
40  }
41
42  /**
43   * Constructs a ByteString from an array buffer.
44   * @param {!ArrayBuffer} bytes
45   * @param {number=} start
46   * @param {number=} end
47   * @return {!ByteString}
48   */
49  static fromArrayBuffer(bytes, start = 0, end = undefined) {
50    return new ByteString(
51        new Uint8Array(arrayBufferSlice(bytes, start, end)), /* base64 */ null);
52  }
53
54  /**
55   * Constructs a ByteString from any ArrayBufferView (e.g. DataView,
56   * TypedArray, Uint8Array, etc.).
57   * @param {!ArrayBufferView} bytes
58   * @return {!ByteString}
59   */
60  static fromArrayBufferView(bytes) {
61    return new ByteString(cloneArrayBufferView(bytes), /* base64 */ null);
62  }
63
64  /**
65   * Constructs a ByteString from an Uint8Array. DON'T MODIFY the underlying
66   * ArrayBuffer, since the ByteString directly uses it without making a copy.
67   *
68   * This method exists so that internal APIs can construct a ByteString without
69   * paying the penalty of copying an ArrayBuffer when that ArrayBuffer is not
70   * supposed to change. It is exposed to a limited number of internal classes
71   * through bytestring_internal.js.
72   *
73   * @param {!Uint8Array} bytes
74   * @return {!ByteString}
75   * @package
76   */
77  static fromUint8ArrayUnsafe(bytes) {
78    return new ByteString(bytes, /* base64 */ null);
79  }
80
81  /**
82   * Returns this ByteString as an ArrayBuffer.
83   * @return {!ArrayBuffer}
84   */
85  toArrayBuffer() {
86    const bytes = this.ensureBytes_();
87    return arrayBufferSlice(
88        bytes.buffer, bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
89  }
90
91  /**
92   * Returns this ByteString as an Uint8Array. DON'T MODIFY the returned array,
93   * since the ByteString holds the reference to the same array.
94   *
95   * This method exists so that internal APIs can get contents of a ByteString
96   * without paying the penalty of copying an ArrayBuffer. It is exposed to a
97   * limited number of internal classes through bytestring_internal.js.
98   * @return {!Uint8Array}
99   * @package
100   */
101  toUint8ArrayUnsafe() {
102    return this.ensureBytes_();
103  }
104
105  /**
106   * Returns this ByteString as a base64 encoded string.
107   * @return {string}
108   */
109  toBase64String() {
110    return this.ensureBase64String_();
111  }
112
113  /**
114   * Returns true for Bytestrings that contain identical values.
115   * @param {*} other
116   * @return {boolean}
117   */
118  equals(other) {
119    if (this === other) {
120      return true;
121    }
122
123    if (!(other instanceof ByteString)) {
124      return false;
125    }
126
127    const otherByteString = /** @type {!ByteString} */ (other);
128    return uint8ArrayEqual(this.ensureBytes_(), otherByteString.ensureBytes_());
129  }
130
131  /**
132   * Returns a number (int32) that is suitable for using in hashed structures.
133   * @return {number}
134   */
135  hashCode() {
136    if (this.hashCode_ == 0) {
137      this.hashCode_ = hashUint8Array(this.ensureBytes_());
138    }
139    return this.hashCode_;
140  }
141
142  /**
143   * Returns true if the bytestring is empty.
144   * @return {boolean}
145   */
146  isEmpty() {
147    if (this.bytes_ != null && this.bytes_.byteLength == 0) {
148      return true;
149    }
150    if (this.base64_ != null && this.base64_.length == 0) {
151      return true;
152    }
153    return false;
154  }
155
156  /**
157   * @return {!Uint8Array}
158   * @private
159   */
160  ensureBytes_() {
161    if (this.bytes_) {
162      return this.bytes_;
163    }
164    return this.bytes_ = base64.decodeStringToUint8Array(
165               /** @type {string} */ (this.base64_));
166  }
167
168  /**
169   * @return {string}
170   * @private
171   */
172  ensureBase64String_() {
173    if (this.base64_ == null) {
174      this.base64_ = base64.encodeByteArray(this.bytes_);
175    }
176    return this.base64_;
177  }
178}
179
180/** @const {!ByteString} */
181ByteString.EMPTY = new ByteString(new Uint8Array(0), null);
182
183exports = ByteString;
184