Passed
Push — master ( be32bc...8c127f )
by Rafael S.
02:26
created

integer.js ➔ read   A

Complexity

Conditions 2
Paths 6

Size

Total Lines 10
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 8
nc 6
dl 0
loc 10
rs 10
c 0
b 0
f 0
nop 2
1
/*
2
 * Copyright (c) 2017-2018 Rafael da Silva Rocha.
3
 *
4
 * Permission is hereby granted, free of charge, to any person obtaining
5
 * a copy of this software and associated documentation files (the
6
 * "Software"), to deal in the Software without restriction, including
7
 * without limitation the rights to use, copy, modify, merge, publish,
8
 * distribute, sublicense, and/or sell copies of the Software, and to
9
 * permit persons to whom the Software is furnished to do so, subject to
10
 * the following conditions:
11
 *
12
 * The above copyright notice and this permission notice shall be
13
 * included in all copies or substantial portions of the Software.
14
 *
15
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
 *
23
 */
24
25
/**
26
 * @fileoverview Pack and unpack two's complement ints and unsigned ints.
27
 * @see https://github.com/rochars/byte-data
28
 */
29
30
/**
31
 * A class to pack and unpack two's complement ints and unsigned ints.
32
 */
33
export default class Integer {
34
35
  /**
36
   * @param {number} bits Number of bits used by the data.
37
   * @param {boolean} signed True for signed types.
38
   * @throws {Error} if the number of bits is smaller than 1 or greater than 64.
39
   */
40
  constructor(bits, signed) {
41
    /**
42
     * The max number of bits used by the data.
43
     * @type {number}
44
     * @private
45
     */
46
    this.bits = bits;
47
    /**
48
     * If this type it is signed or not.
49
     * @type {boolean}
50
     * @private
51
     */
52
    this.signed = signed;
53
    /**
54
     * The number of bytes used by the data.
55
     * @type {number}
56
     * @private
57
     */
58
    this.offset = 0;
59
    /**
60
     * Min value for numbers of this type.
61
     * @type {number}
62
     * @private
63
     */
64
    this.min = -Infinity;
65
    /**
66
     * Max value for numbers of this type.
67
     * @type {number}
68
     * @private
69
     */
70
    this.max = Infinity;
71
    /**
72
     * The practical number of bits used by the data.
73
     * @type {number}
74
     * @private
75
     */
76
    this.realBits_ = this.bits;
77
    /**
78
     * The mask to be used in the last byte.
79
     * @type {number}
80
     * @private
81
     */
82
    this.lastByteMask_ = 255;
83
    this.build_();
84
  }
85
86
  /**
87
   * Read one integer number from a byte buffer.
88
   * @param {!Uint8Array} bytes An array of bytes.
89
   * @param {number=} i The index to read.
90
   * @return {number}
91
   */
92
  read(bytes, i=0) {
93
    let num = 0;
94
    let x = this.offset - 1;
95
    while (x > 0) {
96
      num = (bytes[x + i] << x * 8) | num;
97
      x--;
98
    }
99
    num = (bytes[i] | num) >>> 0;
100
    return this.overflow_(this.sign_(num));
101
  }
102
103
  /**
104
   * Write one integer number to a byte buffer.
105
   * @param {!Array<number>} bytes An array of bytes.
106
   * @param {number} number The number.
107
   * @param {number=} j The index being written in the byte buffer.
108
   * @return {number} The next index to write on the byte buffer.
109
   */
110
  write(bytes, number, j=0) {
111
    number = this.overflow_(number);
112
    bytes[j++] = number & 255;
113
    for (let i = 2; i <= this.offset; i++) {
114
      bytes[j++] = Math.floor(number / Math.pow(2, ((i - 1) * 8))) & 255;
115
    }
116
    return j;
117
  }
118
119
  /**
120
   * Write one integer number to a byte buffer.
121
   * @param {!Array<number>} bytes An array of bytes.
122
   * @param {number} number The number.
123
   * @param {number=} j The index being written in the byte buffer.
124
   * @return {number} The next index to write on the byte buffer.
125
   * @private
126
   */
127
  writeEsoteric_(bytes, number, j=0) {
128
    number = this.overflow_(number);
129
    j = this.writeFirstByte_(bytes, number, j);
130
    for (let i = 2; i < this.offset; i++) {
131
      bytes[j++] = Math.floor(number / Math.pow(2, ((i - 1) * 8))) & 255;
132
    }
133
    if (this.bits > 8) {
134
      bytes[j++] = Math.floor(
135
          number / Math.pow(2, ((this.offset - 1) * 8))) &
136
        this.lastByteMask_;
137
    }
138
    return j;
139
  }
140
141
  /**
142
   * Read a integer number from a byte buffer by turning int bytes
143
   * to a string of bits. Used for data with more than 32 bits.
144
   * @param {!Uint8Array} bytes An array of bytes.
145
   * @param {number=} i The index to read.
146
   * @return {number}
147
   * @private
148
   */
149
  readBits_(bytes, i=0) {
150
    let binary = '';
151
    let j = 0;
152
    while(j < this.offset) {
153
      let bits = bytes[i + j].toString(2);
154
      binary = new Array(9 - bits.length).join('0') + bits + binary;
155
      j++;
156
    }
157
    return this.overflow_(this.sign_(parseInt(binary, 2)));
158
  }
159
160
  /**
161
   * Build the type.
162
   * @throws {Error} if the number of bits is smaller than 1 or greater than 64.
163
   * @private
164
   */
165
  build_() {
166
    this.setRealBits_();
167
    this.setLastByteMask_();
168
    this.setMinMax_();
169
    this.offset = this.bits < 8 ? 1 : Math.ceil(this.realBits_ / 8);
170
    if ((this.realBits_ != this.bits) || this.bits < 8 || this.bits > 32) {
171
      this.write = this.writeEsoteric_;
172
      this.read = this.readBits_;
173
    }
174
  }
175
176
  /**
177
   * Sign a number.
178
   * @param {number} num The number.
179
   * @return {number}
180
   * @private
181
   */
182
  sign_(num) {
183
    if (num > this.max) {
184
      num -= (this.max * 2) + 2;
185
    }
186
    return num;
187
  }
188
189
  /**
190
   * Limit the value according to the bit depth in case of
191
   * overflow or underflow.
192
   * @param {number} value The data.
193
   * @return {number}
194
   * @private
195
   */
196
  overflow_(value) {
197
    if (value > this.max) {
198
      throw new Error('Overflow.');
199
    } else if (value < this.min) {
200
      throw new Error('Underflow.');
201
    }
202
    return value;
203
  }
204
205
  /**
206
   * Set the minimum and maximum values for the type.
207
   * @private
208
   */
209
  setMinMax_() {
210
    let max = Math.pow(2, this.bits);
211
    if (this.signed) {
212
      this.max = max / 2 -1;
213
      this.min = -max / 2;
214
    } else {
215
      this.max = max - 1;
216
      this.min = 0;
217
    }
218
  }
219
220
  /**
221
   * Set the practical bit number for data with bit count different
222
   * from the standard types (8, 16, 32, 40, 48, 64) and more than 8 bits.
223
   * @private
224
   */
225
  setRealBits_() {
226
    if (this.bits > 8) {
227
      this.realBits_ = ((this.bits - 1) | 7) + 1;
228
    }
229
  }
230
231
  /**
232
   * Set the mask that should be used when writing the last byte.
233
   * @private
234
   */
235
  setLastByteMask_() {
236
    let r = 8 - (this.realBits_ - this.bits);
237
    this.lastByteMask_ = Math.pow(2, r > 0 ? r : 8) -1;
238
  }
239
240
  /**
241
   * Write the first byte of a integer number.
242
   * @param {!Array<number>} bytes An array of bytes.
243
   * @param {number} number The number.
244
   * @param {number} j The index being written in the byte buffer.
245
   * @return {number} The next index to write on the byte buffer.
246
   * @private
247
   */
248
  writeFirstByte_(bytes, number, j) {
249
    if (this.bits < 8) {
250
      bytes[j++] = number < 0 ? number + Math.pow(2, this.bits) : number;
251
    } else {
252
      bytes[j++] = number & 255;
253
    }
254
    return j;
255
  }
256
}
257