1/* -*- Mode: js; js-indent-level: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
2/* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
4 * You can obtain one at http://mozilla.org/MPL/2.0/. */
5/* jshint esnext: true, moz: true */
6
7"use strict";
8
9var EXPORTED_SYMBOLS = ["DataReader"];
10
11class DataReader {
12  // `data` is `Uint8Array`
13  constructor(data, startByte = 0) {
14    this._data = data;
15    this._cursor = startByte;
16  }
17
18  get buffer() {
19    return this._data.buffer;
20  }
21
22  get data() {
23    return this._data;
24  }
25
26  get eof() {
27    return this._cursor >= this._data.length;
28  }
29
30  getBytes(length = 1) {
31    if (!length) {
32      return new Uint8Array();
33    }
34
35    let end = this._cursor + length;
36    if (end > this._data.length) {
37      return new Uint8Array();
38    }
39
40    let uint8Array = new Uint8Array(this.buffer.slice(this._cursor, end));
41    this._cursor += length;
42
43    return uint8Array;
44  }
45
46  getString(length) {
47    let uint8Array = this.getBytes(length);
48    return _uint8ArrayToString(uint8Array);
49  }
50
51  getValue(length) {
52    let uint8Array = this.getBytes(length);
53    return _uint8ArrayToValue(uint8Array);
54  }
55
56  getLabel(decompressData) {
57    let parts = [];
58    let partLength;
59
60    while ((partLength = this.getValue(1))) {
61      // If a length has been specified instead of a pointer,
62      // read the string of the specified length.
63      if (partLength !== 0xc0) {
64        parts.push(this.getString(partLength));
65        continue;
66      }
67
68      // TODO: Handle case where we have a pointer to the label
69      parts.push(String.fromCharCode(0xc0) + this.getString(1));
70      break;
71    }
72
73    let label = parts.join(".");
74
75    return _decompressLabel(label, decompressData || this._data);
76  }
77}
78
79/**
80 * @private
81 */
82function _uint8ArrayToValue(uint8Array) {
83  let length = uint8Array.length;
84  if (length === 0) {
85    return null;
86  }
87
88  let value = 0;
89  for (let i = 0; i < length; i++) {
90    value = value << 8;
91    value += uint8Array[i];
92  }
93
94  return value;
95}
96
97/**
98 * @private
99 */
100function _uint8ArrayToString(uint8Array) {
101  let length = uint8Array.length;
102  if (length === 0) {
103    return "";
104  }
105
106  let results = [];
107  for (let i = 0; i < length; i += 1024) {
108    results.push(
109      String.fromCharCode.apply(null, uint8Array.subarray(i, i + 1024))
110    );
111  }
112
113  return results.join("");
114}
115
116/**
117 * @private
118 */
119function _decompressLabel(label, decompressData) {
120  let result = "";
121
122  for (let i = 0, length = label.length; i < length; i++) {
123    if (label.charCodeAt(i) !== 0xc0) {
124      result += label.charAt(i);
125      continue;
126    }
127
128    i++;
129
130    let reader = new DataReader(decompressData, label.charCodeAt(i));
131    result += _decompressLabel(reader.getLabel(), decompressData);
132  }
133
134  return result;
135}
136