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. var value = this.dataView_.getUint8(this.position_);
  81. } catch (exception) {
  82. this.throwOutOfBounds_();
  83. }
  84. this.position_ += 1;
  85. return value;
  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. var value = this.dataView_.getUint16(this.position_, this.littleEndian_);
  96. } catch (exception) {
  97. this.throwOutOfBounds_();
  98. }
  99. this.position_ += 2;
  100. return value;
  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. var value = this.dataView_.getUint32(this.position_, this.littleEndian_);
  111. } catch (exception) {
  112. this.throwOutOfBounds_();
  113. }
  114. this.position_ += 4;
  115. return value;
  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. var value = this.dataView_.getInt32(this.position_, this.littleEndian_);
  126. } catch (exception) {
  127. this.throwOutOfBounds_();
  128. }
  129. this.position_ += 4;
  130. return value;
  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. var 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. var value = this.dataView_.buffer.slice(
  176. this.position_, 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. * Keeps reading until it reaches a byte that equals to zero. The text is
  195. * assumed to be UTF-8.
  196. * @return {string}
  197. * @export
  198. */
  199. shaka.util.DataViewReader.prototype.readTerminatedString = function() {
  200. var start = this.position_;
  201. while (this.hasMoreData()) {
  202. var value = this.dataView_.getUint8(this.position_);
  203. if (value == 0) break;
  204. this.position_ += 1;
  205. }
  206. var ret = this.dataView_.buffer.slice(start, this.position_);
  207. // skip string termination
  208. this.position_ += 1;
  209. return shaka.util.StringUtils.fromUTF8(ret);
  210. };
  211. /**
  212. * @throws {shaka.util.Error}
  213. * @private
  214. */
  215. shaka.util.DataViewReader.prototype.throwOutOfBounds_ = function() {
  216. throw new shaka.util.Error(
  217. shaka.util.Error.Severity.CRITICAL,
  218. shaka.util.Error.Category.MEDIA,
  219. shaka.util.Error.Code.BUFFER_READ_OUT_OF_BOUNDS);
  220. };