Source: lib/util/data_view_reader.js

  1. /**
  2. * @license
  3. * Copyright 2016 Google Inc.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. goog.provide('shaka.util.DataViewReader');
  18. goog.require('goog.asserts');
  19. goog.require('shaka.util.Error');
  20. goog.require('shaka.util.StringUtils');
  21. /**
  22. * Creates a DataViewReader, which abstracts a DataView object.
  23. *
  24. * @param {!DataView} dataView The DataView.
  25. * @param {shaka.util.DataViewReader.Endianness} endianness The endianness.
  26. *
  27. * @struct
  28. * @constructor
  29. * @export
  30. */
  31. shaka.util.DataViewReader = function(dataView, endianness) {
  32. /** @private {!DataView} */
  33. this.dataView_ = dataView;
  34. /** @private {boolean} */
  35. this.littleEndian_ =
  36. endianness == shaka.util.DataViewReader.Endianness.LITTLE_ENDIAN;
  37. /** @private {number} */
  38. this.position_ = 0;
  39. };
  40. /**
  41. * Endianness.
  42. * @enum {number}
  43. * @export
  44. */
  45. shaka.util.DataViewReader.Endianness = {
  46. BIG_ENDIAN: 0,
  47. LITTLE_ENDIAN: 1
  48. };
  49. /**
  50. * @return {boolean} True if the reader has more data, false otherwise.
  51. * @export
  52. */
  53. shaka.util.DataViewReader.prototype.hasMoreData = function() {
  54. return this.position_ < this.dataView_.byteLength;
  55. };
  56. /**
  57. * Gets the current byte position.
  58. * @return {number}
  59. * @export
  60. */
  61. shaka.util.DataViewReader.prototype.getPosition = function() {
  62. return this.position_;
  63. };
  64. /**
  65. * Gets the byte length of the DataView.
  66. * @return {number}
  67. * @export
  68. */
  69. shaka.util.DataViewReader.prototype.getLength = function() {
  70. return this.dataView_.byteLength;
  71. };
  72. /**
  73. * Reads an unsigned 8 bit integer, and advances the reader.
  74. * @return {number} The integer.
  75. * @throws {shaka.util.Error} when reading past the end of the data view.
  76. * @export
  77. */
  78. shaka.util.DataViewReader.prototype.readUint8 = function() {
  79. try {
  80. let value = this.dataView_.getUint8(this.position_);
  81. this.position_ += 1;
  82. return value;
  83. } catch (exception) {
  84. this.throwOutOfBounds_();
  85. }
  86. };
  87. /**
  88. * Reads an unsigned 16 bit integer, and advances the reader.
  89. * @return {number} The integer.
  90. * @throws {shaka.util.Error} when reading past the end of the data view.
  91. * @export
  92. */
  93. shaka.util.DataViewReader.prototype.readUint16 = function() {
  94. try {
  95. let value = this.dataView_.getUint16(this.position_, this.littleEndian_);
  96. this.position_ += 2;
  97. return value;
  98. } catch (exception) {
  99. this.throwOutOfBounds_();
  100. }
  101. };
  102. /**
  103. * Reads an unsigned 32 bit integer, and advances the reader.
  104. * @return {number} The integer.
  105. * @throws {shaka.util.Error} when reading past the end of the data view.
  106. * @export
  107. */
  108. shaka.util.DataViewReader.prototype.readUint32 = function() {
  109. try {
  110. let value = this.dataView_.getUint32(this.position_, this.littleEndian_);
  111. this.position_ += 4;
  112. return value;
  113. } catch (exception) {
  114. this.throwOutOfBounds_();
  115. }
  116. };
  117. /**
  118. * Reads a signed 32 bit integer, and advances the reader.
  119. * @return {number} The integer.
  120. * @throws {shaka.util.Error} when reading past the end of the data view.
  121. * @export
  122. */
  123. shaka.util.DataViewReader.prototype.readInt32 = function() {
  124. try {
  125. let value = this.dataView_.getInt32(this.position_, this.littleEndian_);
  126. this.position_ += 4;
  127. return value;
  128. } catch (exception) {
  129. this.throwOutOfBounds_();
  130. }
  131. };
  132. /**
  133. * Reads an unsigned 64 bit integer, and advances the reader.
  134. * @return {number} The integer.
  135. * @throws {shaka.util.Error} when reading past the end of the data view or
  136. * when reading an integer too large to store accurately in JavaScript.
  137. * @export
  138. */
  139. shaka.util.DataViewReader.prototype.readUint64 = function() {
  140. let low, high;
  141. try {
  142. if (this.littleEndian_) {
  143. low = this.dataView_.getUint32(this.position_, true);
  144. high = this.dataView_.getUint32(this.position_ + 4, true);
  145. } else {
  146. high = this.dataView_.getUint32(this.position_, false);
  147. low = this.dataView_.getUint32(this.position_ + 4, false);
  148. }
  149. } catch (exception) {
  150. this.throwOutOfBounds_();
  151. }
  152. if (high > 0x1FFFFF) {
  153. throw new shaka.util.Error(
  154. shaka.util.Error.Severity.CRITICAL,
  155. shaka.util.Error.Category.MEDIA,
  156. shaka.util.Error.Code.JS_INTEGER_OVERFLOW);
  157. }
  158. this.position_ += 8;
  159. // NOTE: This is subtle, but in JavaScript you can't shift left by 32 and get
  160. // the full range of 53-bit values possible. You must multiply by 2^32.
  161. return (high * Math.pow(2, 32)) + low;
  162. };
  163. /**
  164. * Reads the specified number of raw bytes.
  165. * @param {number} bytes The number of bytes to read.
  166. * @return {!Uint8Array}
  167. * @throws {shaka.util.Error} when reading past the end of the data view.
  168. * @export
  169. */
  170. shaka.util.DataViewReader.prototype.readBytes = function(bytes) {
  171. goog.asserts.assert(bytes >= 0, 'Bad call to DataViewReader.readBytes');
  172. if (this.position_ + bytes > this.dataView_.byteLength) {
  173. this.throwOutOfBounds_();
  174. }
  175. let value = new Uint8Array(
  176. this.dataView_.buffer, this.dataView_.byteOffset + this.position_, bytes);
  177. this.position_ += bytes;
  178. return new Uint8Array(value);
  179. };
  180. /**
  181. * Skips the specified number of bytes.
  182. * @param {number} bytes The number of bytes to skip.
  183. * @throws {shaka.util.Error} when skipping past the end of the data view.
  184. * @export
  185. */
  186. shaka.util.DataViewReader.prototype.skip = function(bytes) {
  187. goog.asserts.assert(bytes >= 0, 'Bad call to DataViewReader.skip');
  188. if (this.position_ + bytes > this.dataView_.byteLength) {
  189. this.throwOutOfBounds_();
  190. }
  191. this.position_ += bytes;
  192. };
  193. /**
  194. * Rewinds the specified number of bytes.
  195. * @param {number} bytes The number of bytes to rewind.
  196. * @throws {shaka.util.Error} when rewinding past the beginning of the data
  197. * view.
  198. * @export
  199. */
  200. shaka.util.DataViewReader.prototype.rewind = function(bytes) {
  201. goog.asserts.assert(bytes >= 0, 'Bad call to DataViewReader.rewind');
  202. if (this.position_ < bytes) {
  203. this.throwOutOfBounds_();
  204. }
  205. this.position_ -= bytes;
  206. };
  207. /**
  208. * Seeks to a specified position.
  209. * @param {number} position The desired byte position within the DataView.
  210. * @throws {shaka.util.Error} when seeking outside the range of the data view.
  211. * @export
  212. */
  213. shaka.util.DataViewReader.prototype.seek = function(position) {
  214. goog.asserts.assert(position >= 0, 'Bad call to DataViewReader.seek');
  215. if (position < 0 || position > this.dataView_.byteLength) {
  216. this.throwOutOfBounds_();
  217. }
  218. this.position_ = position;
  219. };
  220. /**
  221. * Keeps reading until it reaches a byte that equals to zero. The text is
  222. * assumed to be UTF-8.
  223. * @return {string}
  224. * @export
  225. */
  226. shaka.util.DataViewReader.prototype.readTerminatedString = function() {
  227. let start = this.position_;
  228. while (this.hasMoreData()) {
  229. let value = this.dataView_.getUint8(this.position_);
  230. if (value == 0) break;
  231. this.position_ += 1;
  232. }
  233. let ret = new Uint8Array(
  234. this.dataView_.buffer, this.dataView_.byteOffset + start,
  235. this.position_ - start);
  236. // Skip string termination.
  237. this.position_ += 1;
  238. return shaka.util.StringUtils.fromUTF8(ret);
  239. };
  240. /**
  241. * @throws {shaka.util.Error}
  242. * @private
  243. */
  244. shaka.util.DataViewReader.prototype.throwOutOfBounds_ = function() {
  245. throw new shaka.util.Error(
  246. shaka.util.Error.Severity.CRITICAL,
  247. shaka.util.Error.Category.MEDIA,
  248. shaka.util.Error.Code.BUFFER_READ_OUT_OF_BOUNDS);
  249. };