Passed
Push — master ( 1835a5...4c50ba )
by Rafael S.
02:23
created

integer.js ➔ ???   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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