bytestring.js 4.6 KB

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