Passed
Push — master ( c79e9a...d409a1 )
by Rafael S.
02:16
created

lib/integer.js   A

Size

Lines of Code 1

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 1
rs 10
noi 0
c 0
b 0
f 0
1
/*
2
 * Copyright (c) 2017-2018 Rafael da Silva Rocha.
3
 * https://github.com/rochars/byte-data
4
 *
5
 */
6
7
/**
8
 * A class to pack and unpack integers.
9
 * 
10
 */
11
class Integer {
12
13
    /**
14
     * @param {number} bits Number of bits used by the data.
15
     * @param {boolean} signed True for signed types.
16
     * @throws {Error} if the number of bits is smaller than 1 or greater than 64.
17
     */
18
    constructor(bits, signed) {
19
        /**
20
         * The max number of bits used by the data.
21
         * @type {number}
22
         */
23
        this.bits = bits;
24
        /**
25
         * If this type it is signed or not.
26
         * @type {boolean}
27
         */
28
        this.signed = signed;
29
        /**
30
         * The number of bytes used by the data.
31
         * @type {number}
32
         */
33
        this.offset = 0;
34
        /**
35
         * Min value for numbers of this type.
36
         * @type {number}
37
         */
38
        this.min = -Infinity;
39
        /**
40
         * Max value for numbers of this type.
41
         * @type {number}
42
         */
43
        this.max = Infinity;
44
        /**
45
         * The practical number of bits used by the data.
46
         * @type {number}
47
         * @private
48
         */
49
        this.realBits_ = this.bits;
50
        /**
51
         * The mask to be used in the last byte.
52
         * @type {number}
53
         * @private
54
         */
55
        this.lastByteMask_ = 255;
56
        this.build_();
57
    }
58
59
    /**
60
     * Read one integer number from a byte buffer.
61
     * @param {!Array<number>|!Uint8Array} bytes An array of bytes.
62
     * @param {number=} i The index to read.
63
     * @return {number}
64
     * @export
65
     */
66
    read(bytes, i=0) {
67
        let num = 0;
68
        let x = this.offset - 1;
69
        while (x > 0) {
70
            num = (bytes[x + i] << x * 8) | num;
71
            x--;
72
        }
73
        num = (bytes[i] | num) >>> 0;
74
        return this.overflow_(this.sign_(num));
75
    }
76
77
    /**
78
     * Write one integer number to a byte buffer.
79
     * @param {!Array<number>} bytes An array of bytes.
80
     * @param {number} number The number.
81
     * @param {number=} j The index being written in the byte buffer.
82
     * @return {number} The next index to write on the byte buffer.
83
     * @export
84
     */
85
    write(bytes, number, j=0) {
86
        number = this.overflow_(number);
87
        bytes[j++] = number & 255;
88
        for (let i = 2; i <= this.offset; i++) {
89
            bytes[j++] = Math.floor(number / Math.pow(2, ((i - 1) * 8))) & 255;
90
        }
91
        return j;
92
    }
93
94
    /**
95
     * Write one integer number to a byte buffer.
96
     * @param {!Array<number>} bytes An array of bytes.
97
     * @param {number} number The number.
98
     * @param {number=} j The index being written in the byte buffer.
99
     * @return {number} The next index to write on the byte buffer.
100
     * @private
101
     */
102
    writeEsoteric_(bytes, number, j=0) {
103
        number = this.overflow_(number);
104
        j = this.writeFirstByte_(bytes, number, j);
105
        for (let i = 2; i < this.offset; i++) {
106
            bytes[j++] = Math.floor(number / Math.pow(2, ((i - 1) * 8))) & 255;
107
        }
108
        if (this.bits > 8) {
109
            bytes[j++] = Math.floor(
110
                    number / Math.pow(2, ((this.offset - 1) * 8))) &
111
                this.lastByteMask_;
112
        }
113
        return j;
114
    }
115
116
    /**
117
     * Read a integer number from a byte buffer by turning int bytes
118
     * to a string of bits. Used for data with more than 32 bits.
119
     * @param {!Array<number>|!Uint8Array} bytes An array of bytes.
120
     * @param {number=} i The index to read.
121
     * @return {number}
122
     * @private
123
     */
124
    readBits_(bytes, i=0) {
125
        let binary = "";
126
        let j = 0;
127
        while(j < this.offset) {
128
            let bits = bytes[i + j].toString(2);
129
            binary = new Array(9 - bits.length).join("0") + bits + binary;
130
            j++;
131
        }
132
        return this.overflow_(this.sign_(parseInt(binary, 2)));
133
    }
134
135
    /**
136
     * Build the type.
137
     * @throws {Error} if the number of bits is smaller than 1 or greater than 64.
138
     * @private
139
     */
140
    build_() {
141
        this.setRealBits_();
142
        this.setLastByteMask_();
143
        this.setMinMax_();
144
        this.offset = this.bits < 8 ? 1 : Math.ceil(this.realBits_ / 8);
145
        if ((this.realBits_ != this.bits) || this.bits < 8 || this.bits > 32) {
146
            this.write = this.writeEsoteric_;
147
            this.read = this.readBits_;
148
        }
149
    }
150
151
    /**
152
     * Sign a number.
153
     * @param {number} num The number.
154
     * @return {number}
155
     * @private
156
     */
157
    sign_(num) {
158
        if (num > this.max) {
159
            num -= (this.max * 2) + 2;
160
        }
161
        return num;
162
    }
163
164
    /**
165
     * Limit the value according to the bit depth in case of
166
     * overflow or underflow.
167
     * @param {number} value The data.
168
     * @return {number}
169
     * @private
170
     */
171
    overflow_(value) {
172
        if (value > this.max) {
173
            throw new Error("Overflow.");
174
        } else if (value < this.min) {
175
            throw new Error("Underflow.");
176
        }
177
        return value;
178
    }
179
180
    /**
181
     * Set the minimum and maximum values for the type.
182
     * @private
183
     */
184
    setMinMax_() {
185
        let max = Math.pow(2, this.bits);
186
        if (this.signed) {
187
            this.max = max / 2 -1;
188
            this.min = -max / 2;
189
        } else {
190
            this.max = max - 1;
191
            this.min = 0;
192
        }
193
    }
194
195
    /**
196
     * Set the practical bit number for data with bit count different
197
     * from the standard types (8, 16, 32, 40, 48, 64) and more than 8 bits.
198
     * @private
199
     */
200
    setRealBits_() {
201
        if (this.bits > 8) {
202
            this.realBits_ = ((this.bits - 1) | 7) + 1;
203
        }
204
    }
205
206
    /**
207
     * Set the mask that should be used when writing the last byte.
208
     * @private
209
     */
210
    setLastByteMask_() {
211
        let r = 8 - (this.realBits_ - this.bits);
212
        this.lastByteMask_ = Math.pow(2, r > 0 ? r : 8) -1;
213
    }
214
215
    /**
216
     * Write the first byte of a integer number.
217
     * @param {!Array<number>} bytes An array of bytes.
218
     * @param {number} number The number.
219
     * @param {number} j The index being written in the byte buffer.
220
     * @return {number} The next index to write on the byte buffer.
221
     * @private
222
     */
223
    writeFirstByte_(bytes, number, j) {
224
        if (this.bits < 8) {
225
            bytes[j++] = number < 0 ? number + Math.pow(2, this.bits) : number;
226
        } else {
227
            bytes[j++] = number & 255;
228
        }
229
        return j;
230
    }
231
}
232
233
module.exports = Integer;
234