Passed
Push — master ( 8e3d5b...ea5ece )
by Rafael S.
01:50
created

type.js ➔ ???   B

Complexity

Conditions 3
Paths 4

Size

Total Lines 76

Duplication

Lines 0
Ratio 0 %

Importance

Changes 23
Bugs 14 Features 0
Metric Value
c 23
b 14
f 0
nc 4
dl 0
loc 76
rs 8.9667
cc 3
nop 1

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
/**
2
 * type: The Type class.
3
 * Copyright (c) 2017 Rafael da Silva Rocha.
4
 * https://github.com/rochars/byte-data
5
 */
6
7
/** @private */
8
let f32 = new Float32Array(1);
9
/** @private */
10
let i32 = new Int32Array(f32.buffer);
11
/** @private */
12
let f64 = new Float64Array(1);
13
/** @private */
14
let ui32 = new Uint32Array(f64.buffer);
15
16
/**
17
 * A class to represent byte-data types.
18
 */
19
class Type {
20
21
    /**
22
     * @param {Object} options The type definition.
23
     * @param {number} options.bits Number of bits used by data of this type.
24
     * @param {boolean} options.char True for string/char types.
25
     * @param {boolean} options.float True for float types.
26
     *    Available only for 16, 32 and 64-bit data.
27
     * @param {boolean} options.be True for signed types.
28
     * @param {boolean} options.signed True for signed types.
29
     */
30
    constructor(options) {
31
        /**
32
         * The max number of bits used by data of this type.
33
         * @type {number}
34
         */
35
        this.bits = options["bits"];
36
        /**
37
         * If this type is a char or not.
38
         * @type {boolean}
39
         */
40
        this.char = options["char"];
41
        /**
42
         * If this type is a floating-point number or not.
43
         * @type {boolean}
44
         */
45
        this.float = options["float"];
46
        /**
47
         * If this type is big-endian or not.
48
         * @type {boolean}
49
         */
50
        this.be = options["be"];
51
        /**
52
         * If this type it is signed or not.
53
         * @type {boolean}
54
         */
55
        this.signed = this.float ? true : options["signed"];
56
        /**
57
         * The base used to represent data of this type.
58
         * Default is 10.
59
         * @type {number}
60
         */
61
        this.base = options["base"] ? options["base"] : 10;
62
        /**
63
         * The function to read values of this type from buffers.
64
         * @type {Function}
65
         * @ignore
66
         */
67
        this.reader = null;
68
        /**
69
         * The function to write values of this type to buffers.
70
         * @type {Function}
71
         * @ignore
72
         */
73
        this.writer = null;
74
        /**
75
         * The number of bytes used by data of this type.
76
         * @type {number}
77
         * @ignore
78
         */
79
        this.offset = 0;
80
        /**
81
         * Min value for numbers of this type.
82
         * @type {number}
83
         * @ignore
84
         */
85
        this.min = -Infinity;
86
        /**
87
         * Max value for numbers of this type.
88
         * @type {number}
89
         * @ignore
90
         */
91
        this.max = Infinity;
92
        /**
93
         * The word size.
94
         * @type {number}
95
         * @ignore
96
         */
97
        this.realBits = this.bits;
98
        /**
99
         * The mask to be used in the last byte of this type.
100
         * @type {number}
101
         * @ignore
102
         */
103
        this.lastByteMask = 255;
104
        this.build_();
105
    }
106
107
    /**
108
     * Sign a number according to the type.
109
     * @param {number} num The number.
110
     * @return {number}
111
     * @ignore
112
     */
113
    sign(num) {
114
        if (num > this.max) {
115
            num -= (this.max * 2) + 2;
116
        }
117
        return num;
118
    }
119
120
    /**
121
     * Limit the value according to the bit depth in case of
122
     * overflow or underflow.
123
     * @param {number} value The data.
124
     * @return {number}
125
     * @ignore
126
     */
127
    overflow(value) {
128
        if (value > this.max) {
129
            value = this.max;
130
        } else if (value < this.min) {
131
            value = this.min;
132
        }
133
        return value;
134
    }
135
136
    /**
137
     * Read a integer number from a byte buffer.
138
     * @param {!Array<number>|Uint8Array} bytes An array of bytes.
139
     * @param {number} i The index to read.
140
     * @param {Object} type The type if other than this.
141
     * @return {number}
142
     * @private
143
     */
144
    read_(bytes, i, type=this) {
145
        let num = 0;
146
        let x = type.offset - 1;
147
        while (x > 0) {
148
            //num = (bytes[x + i] * Math.pow(2, x * 8)) | num;
149
            num = (bytes[x + i] << x * 8) | num;
150
            x--;
151
        }
152
        num = (bytes[i] | num) >>> 0;
153
        return this.overflow(this.sign(num));
154
    }
155
156
    /**
157
     * Read a integer number from a byte buffer by turning the bytes
158
     * to a string of bits.
159
     * @param {!Array<number>|Uint8Array} bytes An array of bytes.
160
     * @param {number} i The index to read.
161
     * @param {Object} type The type if other than this.
162
     * @return {number}
163
     * @private
164
     */
165
    readBits_(bytes, i, type=this) {
166
        let binary = "";
167
        let j = 0;
168
        while(j < type.offset) {
169
            let bits = bytes[i + j].toString(2);
170
            binary = Array(9 - bits.length).join("0") + bits + binary;
171
            j++;
172
        }
173
        return this.overflow(this.sign(parseInt(binary, 2)));
174
    }
175
176
    /**
177
     * Read 1 16-bit float from from bytes.
178
     * Thanks https://stackoverflow.com/a/8796597
179
     * @param {!Array<number>|Uint8Array} bytes An array of bytes.
180
     * @param {number} i The index to read.
181
     * @return {number}
182
     * @private
183
     */
184
    read16F_(bytes, i) {
185
        let int = this.read_(bytes, i, {"bits": 16, "offset": 2});
186
        let exponent = (int & 0x7C00) >> 10;
187
        let fraction = int & 0x03FF;
188
        let floatValue;
189
        if (exponent) {
190
            floatValue =  Math.pow(2, exponent - 15) * (1 + fraction / 0x400);
191
        } else {
192
            floatValue = 6.103515625e-5 * (fraction / 0x400);
193
        }
194
        return  floatValue * (int >> 15 ? -1 : 1);
195
    }
196
197
    /**
198
     * Read 1 32-bit float from bytes.
199
     * @param {!Array<number>|Uint8Array} bytes An array of bytes.
200
     * @param {number} i The index to read.
201
     * @return {number}
202
     * @private
203
     */
204
    read32F_(bytes, i) {
205
        i32[0] = this.read_(bytes, i, {"bits": 32, "offset": 4});
206
        return f32[0];
207
    }
208
209
    /**
210
     * Read 1 64-bit double from bytes.
211
     * Thanks https://gist.github.com/kg/2192799
212
     * @param {!Array<number>|Uint8Array} bytes An array of bytes.
213
     * @param {number} i The index to read.
214
     * @return {number}
215
     * @private
216
     */
217
    read64F_(bytes, i) {
218
        ui32[0] = this.read_(bytes, i, {"bits": 32, "offset": 4});
219
        ui32[1] = this.read_(bytes, i + 4, {"bits": 32, "offset": 4});
220
        return f64[0];
221
    }
222
223
    /**
224
     * Read 1 char from bytes.
225
     * @param {!Array<number>|Uint8Array} bytes An array of bytes.
226
     * @param {number} i The index to read.
227
     * @return {string}
228
     * @private
229
     */
230
    readChar_(bytes, i) {
231
        let chrs = "";
232
        let j = 0;
233
        while(j < this.offset) {
234
            chrs += String.fromCharCode(bytes[i+j]);
235
            j++;
236
        }
237
        return chrs;
238
    }
239
240
    /**
241
     * Write one integer number to a byte buffer.
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
     * @param {Object} type The type.
246
     * @return {number} The next index to write on the byte buffer.
247
     * @private
248
     */
249
    write_(bytes, number, j, type=this) {
250
        number = this.overflow(number);
251
        let mask = 255;
252
        let len = type.offset;
253
        j = this.writeFirstByte_(bytes, number, j, type);
254
        for (let i = 2; i <= len; i++) {
255
            if (i == len) {
256
                mask = type.lastByteMask;
257
            }
258
            bytes[j++] = Math.floor(number / Math.pow(2, ((i - 1) * 8))) & mask;
259
        }
260
        return j;
261
    }
262
263
    /**
264
     * Write one 64-bit float as a binary value.
265
     * @param {!Array<number>} bytes An array of bytes.
266
     * @param {number} number The number to write as bytes.
267
     * @param {number} j The index being written in the byte buffer.
268
     * @return {number} The next index to write on the byte buffer.
269
     * @private
270
     */
271
    write64F_(bytes, number, j) {
272
        f64[0] = number;
273
        let type = {bits: 32, offset: 4, lastByteMask:255};
274
        j = this.write_(bytes, ui32[0], j, type);
275
        return this.write_(bytes, ui32[1], j, type);
276
    }
277
278
    /**
279
     * Write one 32-bit float as a binary value.
280
     * @param {!Array<number>} bytes An array of bytes.
281
     * @param {number} number The number to write as bytes.
282
     * @param {number} j The index being written in the byte buffer.
283
     * @param {Object} type The type.
284
     * @return {number} The next index to write on the byte buffer.
285
     * @private
286
     */
287
    write32F_(bytes, number, j, type) {
288
        f32[0] = number;
289
        j = this.write_(bytes, i32[0], j, type);
290
        return j;
291
    }
292
293
    /**
294
     * Write one 16-bit float as a binary value.
295
     * @param {!Array<number>} bytes An array of bytes.
296
     * @param {number} number The number to write as bytes.
297
     * @param {number} j The index being written in the byte buffer.
298
     * @return {number} The next index to write on the byte buffer.
299
     * @private
300
     */
301
    write16F_(bytes, number, j) {
302
        f32[0] = number;
303
        let x = i32[0];
304
        let bits = (x >> 16) & 0x8000;
305
        let m = (x >> 12) & 0x07ff;
306
        let e = (x >> 23) & 0xff;
307
        if (e >= 103) {
308
            bits |= ((e - 112) << 10) | (m >> 1);
309
            bits += m & 1;
310
        }
311
        bytes[j++] = bits & 0xFF;
312
        bytes[j++] = bits >>> 8 & 0xFF;
313
        return j;
314
    }
315
    
316
    /**
317
     * Write one char as a byte.
318
     * @param {!Array<number>} bytes An array of bytes.
319
     * @param {string} string The string to write as bytes.
320
     * @param {number} j The index being written in the byte buffer.
321
     * @return {number} The next index to write on the byte buffer.
322
     * @private
323
     */
324
    writeChar_(bytes, string, j) {
325
        bytes[j++] = string.charCodeAt(0);
326
        return j;
327
    }
328
329
    /**
330
     * Build the type.
331
     * @private
332
     */
333
    build_() {
334
        this.setRealBits_();
335
        this.setLastByteMask_();
336
        this.offset = this.bits < 8 ? 1 : Math.ceil(this.realBits / 8);
337
        this.setReader_();
338
        this.setWriter_();
339
        if (!this.float) {
340
            this.setMinMax_();
341
        }
342
    }
343
344
    /**
345
     * Set the function to read data of this type.
346
     * @private
347
     */
348
    setReader_() {
349
        if (this.float) {
350
            if (this.bits == 16) {
351
                this.reader = this.read16F_;
352
            } else if(this.bits == 32) {
353
                this.reader = this.read32F_;
354
            } else if(this.bits == 64) {
355
                this.reader = this.read64F_;
356
            }
357
        } else if (this.char) {
358
            this.reader = this.readChar_;
359
        } else if (this.bits < 33) {
360
            this.reader = this.read_;
361
        } else {
362
            this.reader = this.readBits_;
363
        }
364
    }
365
366
    /**
367
     * Set the function to write data of this type.
368
     * @private
369
     */
370
    setWriter_() {
371
        if (this.float) {
372
            if (this.bits == 16) {
373
                this.writer = this.write16F_;
374
            } else if(this.bits == 32) {
375
                this.writer = this.write32F_;
376
            } else if(this.bits == 64) {
377
                this.writer = this.write64F_;
378
            }
379
        } else if (this.char) {
380
            this.writer = this.writeChar_;
381
        } else {
382
            this.writer = this.write_;
383
        }
384
    }
385
386
    /**
387
     * Set the minimum and maximum values for the type.
388
     * @private
389
     */
390
    setMinMax_() {
391
        let max = Math.pow(2, this.bits);
392
        if (this.signed) {
393
            this.max = max / 2 -1;
394
            this.min = -max / 2;
395
        } else {
396
            this.max = max - 1;
397
            this.min = 0;
398
        }
399
    }
400
401
    /**
402
     * Set the real bit depth for data with bit count different from the
403
     * standard types (1, 2, 4, 8, 16, 32, 40, 48, 64): the closest bigger
404
     * standard number of bits. The data is then treated as data of the
405
     * standard type on all aspects except for the min and max values.
406
     * Ex: a 11-bit uInt is treated as 16-bit uInt with a max value of 2048.
407
     * @private
408
     */
409
    setRealBits_() {
410
        if (this.bits > 8) {
411
            if (this.bits <= 16) {
412
                this.realBits = 16;
413
            } else if (this.bits <= 24) {
414
                this.realBits = 24;
415
            } else if (this.bits <= 32) {
416
                this.realBits = 32;
417
            } else if (this.bits <= 40) {
418
                this.realBits = 40;
419
            } else if (this.bits <= 48) {
420
                this.realBits = 48;
421
            } else if (this.bits <= 56) {
422
                this.realBits = 56;
423
            } else {
424
                this.realBits = 64;
425
            }
426
        } else {
427
            this.realBits = this.bits;
428
        }
429
    }
430
431
    /**
432
     * Set the mask that should be used when writing the last byte of
433
     * data of the type.
434
     * @private
435
     */
436
    setLastByteMask_() {
437
        let r = 8 - (this.realBits - this.bits);
438
        this.lastByteMask = Math.pow(2, r > 0 ? r : 8) -1;
439
    }
440
441
    /**
442
     * Write the first byte of a integer number.
443
     * @param {!Array<number>} bytes An array of bytes.
444
     * @param {number} number The number.
445
     * @param {number} j The index being written in the byte buffer.
446
     * @param {Object} type The type.
447
     * @return {number} The next index to write on the byte buffer.
448
     * @private
449
     */
450
    writeFirstByte_(bytes, number, j, type=this) {
451
        if (type.offset == 1 && type.bits < 8) {
452
            bytes[j++] = number < 0 ? number + Math.pow(2, type.bits) : number;
453
        } else {
454
            bytes[j++] = number & 255;
455
        }
456
        return j;
457
    }
458
}
459
460
module.exports = Type;
461