buffer_decoder.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. /**
  2. * @fileoverview A buffer implementation that can decode data for protobufs.
  3. */
  4. goog.module('protobuf.binary.BufferDecoder');
  5. const ByteString = goog.require('protobuf.ByteString');
  6. const functions = goog.require('goog.functions');
  7. const {POLYFILL_TEXT_ENCODING, checkCriticalElementIndex, checkCriticalPositionIndex, checkState} = goog.require('protobuf.internal.checks');
  8. const {byteStringFromUint8ArrayUnsafe} = goog.require('protobuf.byteStringInternal');
  9. const {concatenateByteArrays} = goog.require('protobuf.binary.uint8arrays');
  10. const {decode} = goog.require('protobuf.binary.textencoding');
  11. /**
  12. * Returns a valid utf-8 decoder function based on TextDecoder if available or
  13. * a polyfill.
  14. * Some of the environments we run in do not have TextDecoder defined.
  15. * TextDecoder is faster than our polyfill so we prefer it over the polyfill.
  16. * @return {function(!DataView): string}
  17. */
  18. function getStringDecoderFunction() {
  19. if (goog.global['TextDecoder']) {
  20. const textDecoder = new goog.global['TextDecoder']('utf-8', {fatal: true});
  21. return bytes => textDecoder.decode(bytes);
  22. }
  23. if (POLYFILL_TEXT_ENCODING) {
  24. return decode;
  25. } else {
  26. throw new Error(
  27. 'TextDecoder is missing. ' +
  28. 'Enable protobuf.defines.POLYFILL_TEXT_ENCODING.');
  29. }
  30. }
  31. /** @type {function(): function(!DataView): string} */
  32. const stringDecoderFunction =
  33. functions.cacheReturnValue(() => getStringDecoderFunction());
  34. /** @type {function(): !DataView} */
  35. const emptyDataView =
  36. functions.cacheReturnValue(() => new DataView(new ArrayBuffer(0)));
  37. class BufferDecoder {
  38. /**
  39. * @param {!Array<!BufferDecoder>} bufferDecoders
  40. * @return {!BufferDecoder}
  41. */
  42. static merge(bufferDecoders) {
  43. const uint8Arrays = bufferDecoders.map(b => b.asUint8Array());
  44. const bytesArray = concatenateByteArrays(uint8Arrays);
  45. return BufferDecoder.fromArrayBuffer(bytesArray.buffer);
  46. }
  47. /**
  48. * @param {!ArrayBuffer} arrayBuffer
  49. * @return {!BufferDecoder}
  50. */
  51. static fromArrayBuffer(arrayBuffer) {
  52. return new BufferDecoder(
  53. new DataView(arrayBuffer), 0, arrayBuffer.byteLength);
  54. }
  55. /**
  56. * @param {!DataView} dataView
  57. * @param {number} startIndex
  58. * @param {number} length
  59. * @private
  60. */
  61. constructor(dataView, startIndex, length) {
  62. /** @private @const {!DataView} */
  63. this.dataView_ = dataView;
  64. /** @private @const {number} */
  65. this.startIndex_ = startIndex;
  66. /** @private @const {number} */
  67. this.endIndex_ = this.startIndex_ + length;
  68. }
  69. /**
  70. * Returns the start index of the underlying buffer.
  71. * @return {number}
  72. */
  73. startIndex() {
  74. return this.startIndex_;
  75. }
  76. /**
  77. * Returns the end index of the underlying buffer.
  78. * @return {number}
  79. */
  80. endIndex() {
  81. return this.endIndex_;
  82. }
  83. /**
  84. * Returns the length of the underlying buffer.
  85. * @return {number}
  86. */
  87. length() {
  88. return this.endIndex_ - this.startIndex_;
  89. }
  90. /**
  91. * Returns a float32 from a given index
  92. * @param {number} index
  93. * @return {number}
  94. */
  95. getFloat32(index) {
  96. return this.dataView_.getFloat32(index, true);
  97. }
  98. /**
  99. * Returns a float64 from a given index
  100. * @param {number} index
  101. * @return {number}
  102. */
  103. getFloat64(index) {
  104. return this.dataView_.getFloat64(index, true);
  105. }
  106. /**
  107. * Returns an int32 from a given index
  108. * @param {number} index
  109. * @return {number}
  110. */
  111. getInt32(index) {
  112. return this.dataView_.getInt32(index, true);
  113. }
  114. /**
  115. * @param {number} index
  116. * @return {number}
  117. */
  118. getUint8(index) {
  119. return this.dataView_.getUint8(index);
  120. }
  121. /**
  122. * Returns a uint32 from a given index
  123. * @param {number} index
  124. * @return {number}
  125. */
  126. getUint32(index) {
  127. return this.dataView_.getUint32(index, true);
  128. }
  129. /**
  130. * Returns two JS numbers each representing 32 bits of a 64 bit number.
  131. * @param {number} index
  132. * @return {{lowBits: number, highBits: number, dataStart: number}}
  133. */
  134. getVarint(index) {
  135. let start = index;
  136. let lowBits = 0;
  137. let highBits = 0;
  138. for (let shift = 0; shift < 28; shift += 7) {
  139. const b = this.dataView_.getUint8(start++);
  140. lowBits |= (b & 0x7F) << shift;
  141. if ((b & 0x80) === 0) {
  142. return {lowBits, highBits, dataStart: start};
  143. }
  144. }
  145. const middleByte = this.dataView_.getUint8(start++);
  146. // last four bits of the first 32 bit number
  147. lowBits |= (middleByte & 0x0F) << 28;
  148. // 3 upper bits are part of the next 32 bit number
  149. highBits = (middleByte & 0x70) >> 4;
  150. if ((middleByte & 0x80) === 0) {
  151. return {lowBits, highBits, dataStart: start};
  152. }
  153. for (let shift = 3; shift <= 31; shift += 7) {
  154. const b = this.dataView_.getUint8(start++);
  155. highBits |= (b & 0x7F) << shift;
  156. if ((b & 0x80) === 0) {
  157. return {lowBits, highBits, dataStart: start};
  158. }
  159. }
  160. checkState(false, 'Data is longer than 10 bytes');
  161. return {lowBits, highBits, dataStart: start};
  162. }
  163. /**
  164. * Skips over a varint at a given index and returns the next position.
  165. * @param {number} index Start of the data.
  166. * @return {number} Position of the first byte after the varint.
  167. * @package
  168. */
  169. skipVarint(index) {
  170. let cursor = index;
  171. checkCriticalElementIndex(cursor, this.endIndex());
  172. while (this.dataView_.getUint8(cursor++) & 0x80) {
  173. checkCriticalElementIndex(cursor, this.endIndex());
  174. }
  175. checkCriticalPositionIndex(cursor, index + 10);
  176. return cursor;
  177. }
  178. /**
  179. * @param {number} startIndex
  180. * @param {number} length
  181. * @return {!BufferDecoder}
  182. */
  183. subBufferDecoder(startIndex, length) {
  184. checkState(
  185. startIndex >= this.startIndex(),
  186. `Current start: ${this.startIndex()}, subBufferDecoder start: ${
  187. startIndex}`);
  188. checkState(length >= 0, `Length: ${length}`);
  189. checkState(
  190. startIndex + length <= this.endIndex(),
  191. `Current end: ${this.endIndex()}, subBufferDecoder start: ${
  192. startIndex}, subBufferDecoder length: ${length}`);
  193. return new BufferDecoder(this.dataView_, startIndex, length);
  194. }
  195. /**
  196. * Returns the buffer as a string.
  197. * @return {string}
  198. */
  199. asString() {
  200. // TODO: Remove this check when we no longer need to support IE
  201. const stringDataView = this.length() === 0 ?
  202. emptyDataView() :
  203. new DataView(this.dataView_.buffer, this.startIndex_, this.length());
  204. return stringDecoderFunction()(stringDataView);
  205. }
  206. /**
  207. * Returns the buffer as a ByteString.
  208. * @return {!ByteString}
  209. */
  210. asByteString() {
  211. return byteStringFromUint8ArrayUnsafe(this.asUint8Array());
  212. }
  213. /**
  214. * Returns the DataView as an Uint8Array. DO NOT MODIFY or expose the
  215. * underlying buffer.
  216. *
  217. * @package
  218. * @return {!Uint8Array}
  219. */
  220. asUint8Array() {
  221. return new Uint8Array(
  222. this.dataView_.buffer, this.startIndex_, this.length());
  223. }
  224. }
  225. exports = BufferDecoder;