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