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