Passed
Push — master ( eba43f...0a65f1 )
by Rafael S.
02:17
created

T_IMPORT   C

Complexity

Total Complexity 54
Complexity/F 2.16

Size

Lines of Code 1
Function Count 25

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 0
nc 1
dl 0
loc 1
rs 6.4799
c 0
b 0
f 0
noi 0
wmc 54
mnd 4
bc 53
fnc 25
bpm 2.12
cpm 2.16

How to fix   Complexity   

Complexity

Complex classes like T_IMPORT often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

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 The byte-data API.
30
 */
31
32
/** @module byteData */
33
34
import Integer from './lib/integer.js';
35
36
import endianness from 'endianness';
37
38
import {validateType, validateNotUndefined} from './lib/validation.js';
39
40
// Strings
41
/**
42
 * Read a string from a byte buffer.
43
 * @param {!Uint8Array} bytes A byte buffer.
44
 * @param {number=} index The index to read.
45
 * @param {?number=} len The number of bytes to read.
46
 * @return {string}
47
 */
48
export function unpackString(bytes, index=0, len=null) {
49
  let chrs = '';
50
  len = len || bytes.length - index;
51
  for(let j = 0; j < len; j++) {
52
    chrs += String.fromCharCode(bytes[index+j]);
53
  }
54
  return chrs;
55
}
56
57
/**
58
 * Write a string as a byte buffer.
59
 * @param {string} str The string to pack.
60
 * @return {!Array<number>} The next index to write on the buffer.
61
 */
62
export function packString(str) {
63
  let bytes = [];
64
  for (let i = 0; i < str.length; i++) {
65
    bytes[i] = str.charCodeAt(i);
66
  }
67
  return bytes;
68
}
69
70
/**
71
 * Write a string to a byte buffer.
72
 * @param {string} str The string to pack.
73
 * @param {!Uint8Array} bytes A byte buffer.
74
 * @param {number=} index The index to write in the buffer.
75
 * @return {number} The next index to write in the buffer.
76
 */
77
export function packStringTo(str, bytes, index=0) {
78
  for (let i = 0; i < str.length; i++) {
79
    bytes[index] = str.charCodeAt(i);
80
    index++;
81
  }
82
  return index;
83
}
84
85
// Numbers
86
/**
87
 * Pack a number as a byte buffer.
88
 * @param {number} value The number.
89
 * @param {!Object} theType The type definition.
90
 * @return {!Array<number>} The packed value.
91
 * @throws {Error} If the type definition is not valid.
92
 * @throws {Error} If the value is not valid.
93
 */
94
export function pack(value, theType) {
95
  setUp_(theType);
96
  return toBytes_([value], theType);
97
}
98
99
/**
100
 * Pack an array of numbers as a byte buffer.
101
 * @param {!Array<number>} values The values.
102
 * @param {!Object} theType The type definition.
103
 * @return {!Array<number>} The packed values.
104
 * @throws {Error} If the type definition is not valid.
105
 * @throws {Error} If any of the values are not valid.
106
 */
107
export function packArray(values, theType) {
108
  setUp_(theType);
109
  return toBytes_(values, theType);
110
}
111
112
/**
113
 * Pack a number to a byte buffer.
114
 * @param {number} value The value.
115
 * @param {!Object} theType The type definition.
116
 * @param {!Uint8Array} buffer The output buffer.
117
 * @param {number=} index The index to write.
118
 * @return {number} The next index to write.
119
 * @throws {Error} If the type definition is not valid.
120
 * @throws {Error} If the value is not valid.
121
 */
122
export function packTo(value, theType, buffer, index=0) {
123
  setUp_(theType);
124
  return writeBytes_(value,
125
    theType,
126
    buffer,
127
    index,
128
    index + theType['offset'],
129
    validateNotUndefined,
130
    theType['be']);
131
}
132
133
/**
134
 * Pack a array of numbers to a byte buffer.
135
 * @param {!Array<number>} values The value.
136
 * @param {!Object} theType The type definition.
137
 * @param {!Uint8Array} buffer The output buffer.
138
 * @param {number=} index The buffer index to write.
139
 * @return {number} The next index to write.
140
 * @throws {Error} If the type definition is not valid.
141
 * @throws {Error} If the value is not valid.
142
 */
143
export function packArrayTo(values, theType, buffer, index=0) {
144
  setUp_(theType);
145
  let be = theType['be'];
146
  let offset = theType['offset'];
147
  for (let i=0; i<values.length; i++) {
148
    index = writeBytes_(
149
      values[i],
150
      theType,
151
      buffer,
152
      index,
153
      index + offset,
154
      validateNotUndefined,
155
      be);
156
  }
157
  return index;
158
}
159
160
/**
161
 * Unpack a number from a byte buffer.
162
 * @param {!Uint8Array} buffer The byte buffer.
163
 * @param {!Object} theType The type definition.
164
 * @return {number}
165
 * @throws {Error} If the type definition is not valid
166
 */
167
export function unpack(buffer, theType) {
168
  setUp_(theType);
169
  let values = fromBytes_(
170
    buffer.slice(0, theType['offset']), theType);
171
  return values[0];
172
}
173
174
/**
175
 * Unpack an array of numbers from a byte buffer.
176
 * @param {!Uint8Array} buffer The byte buffer.
177
 * @param {!Object} theType The type definition.
178
 * @return {!Array<number>}
179
 * @throws {Error} If the type definition is not valid.
180
 */
181
export function unpackArray(buffer, theType) {
182
  setUp_(theType);
183
  return fromBytes_(buffer, theType);
184
}
185
186
/**
187
 * Unpack a number from a byte buffer by index.
188
 * @param {!Uint8Array} buffer The byte buffer.
189
 * @param {!Object} theType The type definition.
190
 * @param {number=} index The buffer index to read.
191
 * @return {number}
192
 * @throws {Error} If the type definition is not valid
193
 */
194
export function unpackFrom(buffer, theType, index=0) {
195
  setUp_(theType);
196
  if (theType['be']) {
197
    endianness(buffer, theType['offset'], index, index + theType['offset']);
198
  }
199
  let value = reader_(buffer, index);
200
  if (theType['be']) {
201
    endianness(buffer, theType['offset'], index, index + theType['offset']);
202
  }
203
  return value;
204
}
205
206
/**
207
 * Unpack a array of numbers from a byte buffer by index.
208
 * @param {!Uint8Array} buffer The byte buffer.
209
 * @param {!Object} theType The type definition.
210
 * @param {number=} start The start index. Assumes 0.
211
 * @param {?number=} end The end index. Assumes the buffer length.
212
 * @return {!Array<number>}
213
 * @throws {Error} If the type definition is not valid
214
 */
215
export function unpackArrayFrom(buffer, theType, start=0, end=null) {
216
  setUp_(theType);
217
  if (theType['be']) {
218
    endianness(buffer, theType['offset']);
219
  }
220
  let len = end || buffer.length;
221
  let values = [];
222
  for (let i=start; i<len; i+=theType['offset']) {
223
    values.push(reader_(buffer, i));
224
  }
225
  if (theType['be']) {
226
    endianness(buffer, theType['offset']);
227
  }
228
  return values;
229
}
230
231
/**
232
 * @type {!Int8Array}
233
 * @private
234
 */
235
const int8_ = new Int8Array(8);
236
/**
237
 * @type {!Uint32Array}
238
 * @private
239
 */
240
const ui32_ = new Uint32Array(int8_.buffer);
241
/**
242
 * @type {!Float32Array}
243
 * @private
244
 */
245
const f32_ = new Float32Array(int8_.buffer);
246
/**
247
 * @type {!Float64Array}
248
 * @private
249
 */
250
const f64_ = new Float64Array(int8_.buffer);
251
/**
252
 * @type {Function}
253
 * @private
254
 */
255
let reader_;
256
/**
257
 * @type {Function}
258
 * @private
259
 */
260
let writer_;
261
/**
262
 * @type {Object}
263
 * @private
264
 */
265
let gInt_ = {};
266
267
/**
268
 * Turn numbers to bytes.
269
 * @param {number} value The value to be packed.
270
 * @param {!Object} theType The type definition.
271
 * @param {!Uint8Array} buffer The buffer to write the bytes to.
272
 * @param {number} index The index to start writing.
273
 * @param {number} len The end index.
274
 * @param {!Function} validate The function used to validate input.
275
 * @param {boolean} be True if big-endian.
276
 * @return {number} the new index to be written.
277
 * @private
278
 */
279
function writeBytes_(value, theType, buffer, index, len, validate, be) {
280
  while (index < len) {
281
    validate(value, theType);
282
    index = writer_(buffer, value, index);
283
  }
284
  if (be) {
285
    endianness(
286
      buffer, theType['offset'], index - theType['offset'], index);
287
  }
288
  return index;
289
}
290
291
/**
292
 * Turn a byte buffer into what the bytes represent.
293
 * @param {!Uint8Array} buffer An array of bytes.
294
 * @param {!Object} theType The type definition.
295
 * @return {!Array<number>}
296
 * @private
297
 */
298
function fromBytes_(buffer, theType) {
299
  if (theType['be']) {
300
    endianness(buffer, theType['offset']);
301
  }
302
  let len = buffer.length;
303
  let values = [];
304
  len = len - (theType['offset'] - 1);
305
  for (let i=0; i<len; i+=theType['offset']) {
306
    values.push(reader_(buffer, i));
307
  }
308
  return values;
309
}
310
311
/**
312
 * Turn numbers to bytes.
313
 * @param {!Array<number>} values The data.
314
 * @param {!Object} theType The type definition.
315
 * @return {!Array<number>} the data as a byte buffer.
316
 * @private
317
 */
318
function toBytes_(values, theType) {
319
  let j = 0;
320
  let bytes = [];
321
  let len = values.length;
322
  for(let i=0; i < len; i++) {
323
    validateNotUndefined(values[i]);
324
    j = writer_(bytes, values[i], j);
325
  }
326
  if (theType['be']) {
327
    endianness(bytes, theType['offset']);
328
  }
329
  return bytes;
330
}
331
332
/**
333
 * Read int values from bytes.
334
 * @param {!Uint8Array} bytes An array of bytes.
335
 * @param {number} i The index to read.
336
 * @return {number}
337
 * @private
338
 */
339
function readInt_(bytes, i) {
340
  return gInt_.read(bytes, i);
341
}
342
343
/**
344
 * Read 1 16-bit float from bytes.
345
 * Thanks https://stackoverflow.com/a/8796597
346
 * @param {!Uint8Array} bytes An array of bytes.
347
 * @param {number} i The index to read.
348
 * @return {number}
349
 * @private
350
 */
351
function read16F_(bytes, i) {
352
  let int = gInt_.read(bytes, i);
353
  let exponent = (int & 0x7C00) >> 10;
354
  let fraction = int & 0x03FF;
355
  let floatValue;
356
  if (exponent) {
357
    floatValue =  Math.pow(2, exponent - 15) * (1 + fraction / 0x400);
358
  } else {
359
    floatValue = 6.103515625e-5 * (fraction / 0x400);
360
  }
361
  return floatValue * (int >> 15 ? -1 : 1);
362
}
363
364
/**
365
 * Read 1 32-bit float from bytes.
366
 * @param {!Uint8Array} bytes An array of bytes.
367
 * @param {number} i The index to read.
368
 * @return {number}
369
 * @private
370
 */
371
function read32F_(bytes, i) {
372
  ui32_[0] = gInt_.read(bytes, i);
373
  return f32_[0];
374
}
375
376
/**
377
 * Read 1 64-bit float from bytes.
378
 * Thanks https://gist.github.com/kg/2192799
379
 * @param {!Uint8Array} bytes An array of bytes.
380
 * @param {number} i The index to read.
381
 * @return {number}
382
 * @private
383
 */
384
function read64F_(bytes, i) {
385
  ui32_[0] = gInt_.read(bytes, i);
386
  ui32_[1] = gInt_.read(bytes, i + 4);
387
  return f64_[0];
388
}
389
390
/**
391
 * Write a integer value to a byte buffer.
392
 * @param {!Uint8Array} bytes An array of bytes.
393
 * @param {number} number The number to write as bytes.
394
 * @param {number} j The index being written in the byte buffer.
395
 * @return {!number} The next index to write on the byte buffer.
396
 * @private
397
 */
398
function writeInt_(bytes, number, j) {
399
  return gInt_.write(bytes, number, j);
400
}
401
402
/**
403
 * Write one 16-bit float as a binary value.
404
 * @param {!Uint8Array} bytes An array of bytes.
405
 * @param {number} number The number to write as bytes.
406
 * @param {number} j The index being written in the byte buffer.
407
 * @return {number} The next index to write on the byte buffer.
408
 * @private
409
 */
410
function write16F_(bytes, number, j) {
411
  f32_[0] = number;
412
  let x = ui32_[0];
413
  let bits = (x >> 16) & 0x8000;
414
  let m = (x >> 12) & 0x07ff;
415
  let e = (x >> 23) & 0xff;
416
  if (e >= 103) {
417
    bits |= ((e - 112) << 10) | (m >> 1);
418
    bits += m & 1;
419
  }
420
  bytes[j++] = bits & 0xFF;
421
  bytes[j++] = bits >>> 8 & 0xFF;
422
  return j;
423
}
424
425
/**
426
 * Write one 32-bit float as a binary value.
427
 * @param {!Uint8Array} bytes An array of bytes.
428
 * @param {number} number The number to write as bytes.
429
 * @param {number} j The index being written in the byte buffer.
430
 * @return {number} The next index to write on the byte buffer.
431
 * @private
432
 */
433
function write32F_(bytes, number, j) {
434
  f32_[0] = number;
435
  return gInt_.write(bytes, ui32_[0], j);
436
}
437
438
/**
439
 * Write one 64-bit float as a binary value.
440
 * @param {!Uint8Array} bytes An array of bytes.
441
 * @param {number} number The number to write as bytes.
442
 * @param {number} j The index being written in the byte buffer.
443
 * @return {number} The next index to write on the byte buffer.
444
 * @private
445
 */
446
function write64F_(bytes, number, j) {
447
  f64_[0] = number;
448
  j = gInt_.write(bytes, ui32_[0], j);
449
  return gInt_.write(bytes, ui32_[1], j);
450
}
451
452
/**
453
 * Set the function to unpack the data.
454
 * @param {!Object} theType The type definition.
455
 * @private
456
 */
457
function setReader(theType) {
458
  if (theType['float']) {
459
    if (theType['bits'] == 16) {
460
      reader_ = read16F_;
461
    } else if(theType['bits'] == 32) {
462
      reader_ = read32F_;
463
    } else if(theType['bits'] == 64) {
464
      reader_ = read64F_;
465
    }
466
  } else {
467
    reader_ = readInt_;
468
  }
469
}
470
471
/**
472
 * Set the function to pack the data.
473
 * @param {!Object} theType The type definition.
474
 * @private
475
 */
476
function setWriter(theType) {
477
  if (theType['float']) {
478
    if (theType['bits'] == 16) {
479
      writer_ = write16F_;
480
    } else if(theType['bits'] == 32) {
481
      writer_ = write32F_;
482
    } else if(theType['bits'] == 64) {
483
      writer_ = write64F_;
484
    }
485
  } else {
486
    writer_ = writeInt_;
487
  }   
488
}
489
490
/**
491
 * Validate the type and set up the packing/unpacking functions.
492
 * @param {!Object} theType The type definition.
493
 * @throws {Error} If the type definition is not valid.
494
 * @private
495
 */
496
function setUp_(theType) {
497
  validateType(theType);
498
  theType['offset'] = theType['bits'] < 8 ? 1 : Math.ceil(theType['bits'] / 8);
499
  setReader(theType);
500
  setWriter(theType);
501
  gInt_ = new Integer(
502
    theType['bits'] == 64 ? 32 : theType['bits'],
503
    theType['float'] ? false : theType['signed']);
504
}
505