Passed
Push — master ( 4137f4...784724 )
by Rafael S.
01:53
created

T_CONST ➔ validateNotNull   A

Complexity

Conditions 3
Paths 1

Size

Total Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
c 0
b 0
f 0
nc 1
dl 0
loc 1
rs 10
nop 1
1
/**
2
 * Copyright (c) 2017-2018 Rafael da Silva Rocha.
3
 * https://github.com/rochars/byte-data
4
 *
5
 */
6
7
/**
8
 * @type {!Function}
9
 * @private
10
 */
11
const endianness = require("endianness");
12
/**
13
 * @constructor
14
 * @private
15
 */
16
const Integer = require("./integer.js");
17
/**
18
 * @type {!Int8Array}
19
 * @private
20
 */
21
const int8_ = new Int8Array(8);
22
/**
23
 * @type {!Uint32Array}
24
 * @private
25
 */
26
const ui32_ = new Uint32Array(int8_.buffer);
27
/**
28
 * @type {!Float32Array}
29
 * @private
30
 */
31
const f32_ = new Float32Array(int8_.buffer);
32
/**
33
 * @type {!Float64Array}
34
 * @private
35
 */
36
const f64_ = new Float64Array(int8_.buffer);
37
/**
38
 * @type {Function}
39
 * @private
40
 */
41
let reader_;
42
/**
43
 * @type {Function}
44
 * @private
45
 */
46
let writer_;
47
/**
48
 * @type {Object}
49
 * @private
50
 */
51
let gInt_ = {};
52
53
/**
54
 * Turn a byte buffer into what the bytes represent.
55
 * @param {!Array<number|string>|!Uint8Array} buffer An array of bytes.
56
 * @param {!Object} theType The type definition.
57
 * @return {!Array<number>}
58
 * @private
59
 */
60
function fromBytes(buffer, theType) {
61
    // turn to BE if BE
62
    if (theType["be"]) {
63
        endianness(buffer, theType["offset"]);
64
    }
65
    let len = buffer.length;
66
    // unpack the values
67
    let values = [];
68
    len = len - (theType["offset"] - 1);
69
    for (let i=0; i<len; i+=theType["offset"]) {
70
        values.push(reader_(buffer, i));
71
    }
72
    return values;
73
}
74
75
/**
76
 * Turn numbers and strings to bytes.
77
 * @param {!Array<number|string>} values The data.
78
 * @param {!Object} theType The type definition.
79
 * @return {!Array<number|string>} the data as a byte buffer.
80
 * @private
81
 */
82
function toBytes(values, theType) {
83
    let j = 0;
84
    let bytes = [];
85
    let len = values.length;
86
    let validate = validateNotNull;
87
    if (theType["char"]) {
88
        validate = validateString;
89
    }
90
    for(let i=0; i < len; i++) {
91
        validate(values[i], theType);
92
        j = writer_(bytes, values[i], j);
93
    }
94
    if (theType["be"]) {
95
        endianness(bytes, theType["offset"]);
96
    }
97
    return bytes;
98
}
99
100
/**
101
 * Read int values from bytes.
102
 * @param {!Array<number>|!Uint8Array} bytes An array of bytes.
103
 * @param {number} i The index to read.
104
 * @return {number}
105
 * @private
106
 */
107
function readInt_(bytes, i) {
108
    return gInt_.read(bytes, i);
109
}
110
111
/**
112
 * Read 1 16-bit float from bytes.
113
 * Thanks https://stackoverflow.com/a/8796597
114
 * @param {!Array<number>|!Uint8Array} bytes An array of bytes.
115
 * @param {number} i The index to read.
116
 * @return {number}
117
 * @private
118
 */
119
function read16F_(bytes, i) {
120
    let int = gInt_.read(bytes, i);
121
    let exponent = (int & 0x7C00) >> 10;
122
    let fraction = int & 0x03FF;
123
    let floatValue;
124
    if (exponent) {
125
        floatValue =  Math.pow(2, exponent - 15) * (1 + fraction / 0x400);
126
    } else {
127
        floatValue = 6.103515625e-5 * (fraction / 0x400);
128
    }
129
    return floatValue * (int >> 15 ? -1 : 1);
130
}
131
132
/**
133
 * Read 1 32-bit float from bytes.
134
 * @param {!Array<number>|!Uint8Array} bytes An array of bytes.
135
 * @param {number} i The index to read.
136
 * @return {number}
137
 * @private
138
 */
139
function read32F_(bytes, i) {
140
    ui32_[0] = gInt_.read(bytes, i);
141
    return f32_[0];
142
}
143
144
/**
145
 * Read 1 64-bit float from bytes.
146
 * Thanks https://gist.github.com/kg/2192799
147
 * @param {!Array<number>|!Uint8Array} bytes An array of bytes.
148
 * @param {number} i The index to read.
149
 * @return {number}
150
 * @private
151
 */
152
function read64F_(bytes, i) {
153
    ui32_[0] = gInt_.read(bytes, i);
154
    ui32_[1] = gInt_.read(bytes, i + 4);
155
    return f64_[0];
156
}
157
158
/**
159
 * Read 1 char from bytes.
160
 * @param {!Array<number>|!Uint8Array} bytes An array of bytes.
161
 * @param {number} i The index to read.
162
 * @return {string}
163
 * @private
164
 */
165
function readChar_(bytes, i) {
166
    let chrs = "";
167
    for(let j=0; j < gInt_.offset; j++) {
168
        chrs += String.fromCharCode(bytes[i+j]);
169
    }
170
    return chrs;
171
}
172
173
/**
174
 * Write a integer value to a byte buffer.
175
 * @param {!Array<number>|!Uint8Array} bytes An array of bytes.
176
 * @param {number} number The number to write as bytes.
177
 * @param {number} j The index being written in the byte buffer.
178
 * @return {!number} The next index to write on the byte buffer.
179
 * @private
180
 */
181
function writeInt_(bytes, number, j) {
182
    return gInt_.write(bytes, number, j);
183
}
184
185
/**
186
 * Write one 16-bit float as a binary value.
187
 * @param {!Array<number>|!Uint8Array} bytes An array of bytes.
188
 * @param {number} number The number to write as bytes.
189
 * @param {number} j The index being written in the byte buffer.
190
 * @return {number} The next index to write on the byte buffer.
191
 * @private
192
 */
193
function write16F_(bytes, number, j) {
194
    f32_[0] = number;
195
    let x = ui32_[0];
196
    let bits = (x >> 16) & 0x8000;
197
    let m = (x >> 12) & 0x07ff;
198
    let e = (x >> 23) & 0xff;
199
    if (e >= 103) {
200
        bits |= ((e - 112) << 10) | (m >> 1);
201
        bits += m & 1;
202
    }
203
    bytes[j++] = bits & 0xFF;
204
    bytes[j++] = bits >>> 8 & 0xFF;
205
    return j;
206
}
207
208
/**
209
 * Write one 32-bit float as a binary value.
210
 * @param {!Array<number>|!Uint8Array} bytes An array of bytes.
211
 * @param {number} number The number to write as bytes.
212
 * @param {number} j The index being written in the byte buffer.
213
 * @return {number} The next index to write on the byte buffer.
214
 * @private
215
 */
216
function write32F_(bytes, number, j) {
217
    f32_[0] = number;
218
    return gInt_.write(bytes, ui32_[0], j);
219
}
220
221
/**
222
 * Write one 64-bit float as a binary value.
223
 * @param {!Array<number>|!Uint8Array} bytes An array of bytes.
224
 * @param {number} number The number to write as bytes.
225
 * @param {number} j The index being written in the byte buffer.
226
 * @return {number} The next index to write on the byte buffer.
227
 * @private
228
 */
229
function write64F_(bytes, number, j) {
230
    f64_[0] = number;
231
    j = gInt_.write(bytes, ui32_[0], j);
232
    return gInt_.write(bytes, ui32_[1], j);
233
}
234
235
/**
236
 * Write one char as a byte.
237
 * @param {!Array<number>|!Uint8Array} bytes An array of bytes.
238
 * @param {string} str The string to write as bytes.
239
 * @param {number} j The index being written in the byte buffer.
240
 * @return {number} The next index to write on the byte buffer.
241
 * @private
242
 */
243
function writeChar_(bytes, str, j) {
244
    for (let i=0; i<str.length; i++) {
245
        bytes[j++] = str.charCodeAt(i);
246
    }
247
    return j;
248
}
249
250
/**
251
 * Set the function to unpack the data.
252
 * @param {!Object} theType The type definition.
253
 * @private
254
 */
255
function setReader_(theType) {
256
    if (theType["float"]) {
257
        if (theType["bits"] == 16) {
258
            reader_ = read16F_;
259
        } else if(theType["bits"] == 32) {
260
            reader_ = read32F_;
261
        } else if(theType["bits"] == 64) {
262
            reader_ = read64F_;
263
        }
264
    } else if (theType["char"]) {
265
        reader_ = readChar_;
266
    } else {
267
        reader_ = readInt_;
268
    }
269
}
270
271
/**
272
 * Set the function to pack the data.
273
 * @param {!Object} theType The type definition.
274
 * @private
275
 */
276
function setWriter_(theType) {
277
    if (theType["float"]) {
278
        if (theType["bits"] == 16) {
279
            writer_ = write16F_;
280
        } else if(theType["bits"] == 32) {
281
            writer_ = write32F_;
282
        } else if(theType["bits"] == 64) {
283
            writer_ = write64F_;
284
        }
285
    } else if (theType["char"]) {
286
        writer_ = writeChar_;
287
    } else {
288
        writer_ = writeInt_;
289
    }   
290
}
291
292
/**
293
 * Validate the type and set up the packing/unpacking functions.
294
 * @param {!Object} theType The type definition.
295
 * @throws {Error} If the type definition is not valid.
296
 * @private
297
 */
298
function setUp(theType) {
299
    validateType_(theType);
300
    theType["offset"] = theType["bits"] < 8 ? 1 : Math.ceil(theType["bits"] / 8);
301
    setReader_(theType);
302
    setWriter_(theType);
303
    if (!theType["char"]) {
304
        gInt_ = new Integer(
305
            theType["bits"] == 64 ? 32 : theType["bits"],
306
            theType["float"] ? false : theType["signed"]);
307
    } else {
308
        // Workaround; should not use Integer when type["char"]
309
        gInt_.offset = theType["bits"] < 8 ? 1 : Math.ceil(theType["bits"] / 8);
310
    }
311
}
312
313
/**
314
 * Validate the type definition.
315
 * @param {!Object} theType The type definition.
316
 * @throws {Error} If the type definition is not valid.
317
 * @private
318
 */
319
function validateType_(theType) {
320
    if (!theType) {
321
        throw new Error("Undefined type.");
322
    }
323
    if (theType["float"]) {
324
        if ([16,32,64].indexOf(theType["bits"]) == -1) {
325
            throw new Error("Not a supported float type.");
326
        }
327
    } else {
328
        if (theType["char"]) {
329
            if (theType["bits"] < 8 || theType["bits"] % 2) {
330
                throw new Error("Wrong offset for type char.");  
331
            }
332
        } else {
333
            if (theType["bits"] < 1 || theType["bits"] > 53) {
334
                throw new Error("Not a supported type.");  
335
            }
336
        }
337
    }
338
}
339
340
/**
341
 * Validate strings with bad length.
342
 * @param {string|number} value The string to validate.
343
 * @param {!Object} theType The type definition.
344
 * @private
345
 */
346
function validateString(value, theType) {
347
    validateNotNull(value);
348
    if (value.length > theType["offset"]) {
349
        throw new Error("String is bigger than its type definition.");
350
    } else if (value.length < theType["offset"]) {
351
        throw new Error("String is smaller than its type definition.");
352
    }
353
}
354
/**
355
 * Validate that the value is not null.
356
 * @param {string|number} value The value.
357
 * @private
358
 */
359
function validateNotNull(value) {
360
    if (value === null || value === undefined) {
361
        throw new Error("Cannot pack null or undefined values.");
362
    }
363
}
364
365
module.exports.setUp = setUp;
366
module.exports.toBytes = toBytes;
367
module.exports.fromBytes = fromBytes;
368