buffer_decoder.js 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  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, checkCriticalPositionIndex, checkCriticalState, 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_ = startIndex + length;
  68. /** @private {number} */
  69. this.cursor_ = startIndex;
  70. }
  71. /**
  72. * Returns the start index of the underlying buffer.
  73. * @return {number}
  74. */
  75. startIndex() {
  76. return this.startIndex_;
  77. }
  78. /**
  79. * Returns the end index of the underlying buffer.
  80. * @return {number}
  81. */
  82. endIndex() {
  83. return this.endIndex_;
  84. }
  85. /**
  86. * Returns the length of the underlying buffer.
  87. * @return {number}
  88. */
  89. length() {
  90. return this.endIndex_ - this.startIndex_;
  91. }
  92. /**
  93. * Returns the start position of the next data, i.e. end position of the last
  94. * read data + 1.
  95. * @return {number}
  96. */
  97. cursor() {
  98. return this.cursor_;
  99. }
  100. /**
  101. * Sets the cursor to the specified position.
  102. * @param {number} position
  103. */
  104. setCursor(position) {
  105. this.cursor_ = position;
  106. }
  107. /**
  108. * Returns if there is more data to read after the current cursor position.
  109. * @return {boolean}
  110. */
  111. hasNext() {
  112. return this.cursor_ < this.endIndex_;
  113. }
  114. /**
  115. * Returns a float32 from a given index
  116. * @param {number} index
  117. * @return {number}
  118. */
  119. getFloat32(index) {
  120. this.cursor_ = index + 4;
  121. return this.dataView_.getFloat32(index, true);
  122. }
  123. /**
  124. * Returns a float64 from a given index
  125. * @param {number} index
  126. * @return {number}
  127. */
  128. getFloat64(index) {
  129. this.cursor_ = index + 8;
  130. return this.dataView_.getFloat64(index, true);
  131. }
  132. /**
  133. * Returns an int32 from a given index
  134. * @param {number} index
  135. * @return {number}
  136. */
  137. getInt32(index) {
  138. this.cursor_ = index + 4;
  139. return this.dataView_.getInt32(index, true);
  140. }
  141. /**
  142. * Returns a uint32 from a given index
  143. * @param {number} index
  144. * @return {number}
  145. */
  146. getUint32(index) {
  147. this.cursor_ = index + 4;
  148. return this.dataView_.getUint32(index, true);
  149. }
  150. /**
  151. * Returns two JS numbers each representing 32 bits of a 64 bit number. Also
  152. * sets the cursor to the start of the next block of data.
  153. * @param {number} index
  154. * @return {{lowBits: number, highBits: number}}
  155. */
  156. getVarint(index) {
  157. this.cursor_ = index;
  158. let lowBits = 0;
  159. let highBits = 0;
  160. for (let shift = 0; shift < 28; shift += 7) {
  161. const b = this.dataView_.getUint8(this.cursor_++);
  162. lowBits |= (b & 0x7F) << shift;
  163. if ((b & 0x80) === 0) {
  164. return {lowBits, highBits};
  165. }
  166. }
  167. const middleByte = this.dataView_.getUint8(this.cursor_++);
  168. // last four bits of the first 32 bit number
  169. lowBits |= (middleByte & 0x0F) << 28;
  170. // 3 upper bits are part of the next 32 bit number
  171. highBits = (middleByte & 0x70) >> 4;
  172. if ((middleByte & 0x80) === 0) {
  173. return {lowBits, highBits};
  174. }
  175. for (let shift = 3; shift <= 31; shift += 7) {
  176. const b = this.dataView_.getUint8(this.cursor_++);
  177. highBits |= (b & 0x7F) << shift;
  178. if ((b & 0x80) === 0) {
  179. return {lowBits, highBits};
  180. }
  181. }
  182. checkCriticalState(false, 'Data is longer than 10 bytes');
  183. return {lowBits, highBits};
  184. }
  185. /**
  186. * Returns an unsigned int32 number at the current cursor position. The upper
  187. * bits are discarded if the varint is longer than 32 bits. Also sets the
  188. * cursor to the start of the next block of data.
  189. * @return {number}
  190. */
  191. getUnsignedVarint32() {
  192. let b = this.dataView_.getUint8(this.cursor_++);
  193. let result = b & 0x7F;
  194. if ((b & 0x80) === 0) {
  195. return result;
  196. }
  197. b = this.dataView_.getUint8(this.cursor_++);
  198. result |= (b & 0x7F) << 7;
  199. if ((b & 0x80) === 0) {
  200. return result;
  201. }
  202. b = this.dataView_.getUint8(this.cursor_++);
  203. result |= (b & 0x7F) << 14;
  204. if ((b & 0x80) === 0) {
  205. return result;
  206. }
  207. b = this.dataView_.getUint8(this.cursor_++);
  208. result |= (b & 0x7F) << 21;
  209. if ((b & 0x80) === 0) {
  210. return result;
  211. }
  212. // Extract only last 4 bits
  213. b = this.dataView_.getUint8(this.cursor_++);
  214. result |= (b & 0x0F) << 28;
  215. for (let readBytes = 5; ((b & 0x80) !== 0) && readBytes < 10; readBytes++) {
  216. b = this.dataView_.getUint8(this.cursor_++);
  217. }
  218. checkCriticalState((b & 0x80) === 0, 'Data is longer than 10 bytes');
  219. // Result can be have 32 bits, convert it to unsigned
  220. return result >>> 0;
  221. }
  222. /**
  223. * Returns an unsigned int32 number at the specified index. The upper bits are
  224. * discarded if the varint is longer than 32 bits. Also sets the cursor to the
  225. * start of the next block of data.
  226. * @param {number} index
  227. * @return {number}
  228. */
  229. getUnsignedVarint32At(index) {
  230. this.cursor_ = index;
  231. return this.getUnsignedVarint32();
  232. }
  233. /**
  234. * Seeks forward by the given amount.
  235. * @param {number} skipAmount
  236. * @package
  237. */
  238. skip(skipAmount) {
  239. this.cursor_ += skipAmount;
  240. checkCriticalPositionIndex(this.cursor_, this.endIndex_);
  241. }
  242. /**
  243. * Skips over a varint from the current cursor position.
  244. * @package
  245. */
  246. skipVarint() {
  247. const startIndex = this.cursor_;
  248. while (this.dataView_.getUint8(this.cursor_++) & 0x80) {
  249. }
  250. checkCriticalPositionIndex(this.cursor_, startIndex + 10);
  251. }
  252. /**
  253. * @param {number} startIndex
  254. * @param {number} length
  255. * @return {!BufferDecoder}
  256. */
  257. subBufferDecoder(startIndex, length) {
  258. checkState(
  259. startIndex >= this.startIndex(),
  260. `Current start: ${this.startIndex()}, subBufferDecoder start: ${
  261. startIndex}`);
  262. checkState(length >= 0, `Length: ${length}`);
  263. checkState(
  264. startIndex + length <= this.endIndex(),
  265. `Current end: ${this.endIndex()}, subBufferDecoder start: ${
  266. startIndex}, subBufferDecoder length: ${length}`);
  267. return new BufferDecoder(this.dataView_, startIndex, length);
  268. }
  269. /**
  270. * Returns the buffer as a string.
  271. * @return {string}
  272. */
  273. asString() {
  274. // TODO: Remove this check when we no longer need to support IE
  275. const stringDataView = this.length() === 0 ?
  276. emptyDataView() :
  277. new DataView(this.dataView_.buffer, this.startIndex_, this.length());
  278. return stringDecoderFunction()(stringDataView);
  279. }
  280. /**
  281. * Returns the buffer as a ByteString.
  282. * @return {!ByteString}
  283. */
  284. asByteString() {
  285. return byteStringFromUint8ArrayUnsafe(this.asUint8Array());
  286. }
  287. /**
  288. * Returns the DataView as an Uint8Array. DO NOT MODIFY or expose the
  289. * underlying buffer.
  290. *
  291. * @package
  292. * @return {!Uint8Array}
  293. */
  294. asUint8Array() {
  295. return new Uint8Array(
  296. this.dataView_.buffer, this.startIndex_, this.length());
  297. }
  298. }
  299. exports = BufferDecoder;