Passed
Push — master ( 8bb50e...3e923a )
by Rafael S.
02:31
created
Labels
Severity
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
/**
35
 * byte-data standard types.
36
 * @type {!Object}
37
 */
38
export {types} from './lib/types.js';
0 ignored issues
show
The variable types seems to be never declared. If this is a global, consider adding a /** global: types */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
39
40
/**
41
 * @constructor
42
 */
43
import Integer from './lib/integer.js';
44
45
/**
46
 * @type {!Function}
47
 * @private
48
 */
49
import {endianness} from 'endianness';
50
/**
51
 * @type {!Int8Array}
52
 * @private
53
 */
54
const int8_ = new Int8Array(8);
55
/**
56
 * @type {!Uint32Array}
57
 * @private
58
 */
59
const ui32_ = new Uint32Array(int8_.buffer);
60
/**
61
 * @type {!Float32Array}
62
 * @private
63
 */
64
const f32_ = new Float32Array(int8_.buffer);
65
/**
66
 * @type {!Float64Array}
67
 * @private
68
 */
69
const f64_ = new Float64Array(int8_.buffer);
70
/**
71
 * @type {Function}
72
 * @private
73
 */
74
let reader_;
75
/**
76
 * @type {Function}
77
 * @private
78
 */
79
let writer_;
80
/**
81
 * @type {Object}
82
 * @private
83
 */
84
let gInt_ = {};
85
86
/**
87
 * Pack a number or a string as a byte buffer.
88
 * @param {number|string} value The value.
89
 * @param {!Object} theType The type definition.
90
 * @return {!Array<number>}
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 or strings to a byte buffer.
101
 * @param {!Array<number|string>} values The values.
102
 * @param {!Object} theType The type definition.
103
 * @return {!Array<number>}
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 or a string as a byte buffer.
114
 * @param {number|string} value The value.
115
 * @param {!Object} theType The type definition.
116
 * @param {!Uint8Array|!Array<number>} buffer The output buffer.
117
 * @param {number} index The buffer index to write.
118
 * @return {number} The next index to start writing.
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) {
123
  setUp_(theType);
124
  let validate = validateNotNull_;
125
  if (theType['char']) {
126
    validate = validateString_;
127
  }
128
  return writeBytes_(value,
129
    theType,
130
    buffer,
131
    index,
132
    index + theType['offset'],
133
    validate,
134
    theType['be']);
135
}
136
137
/**
138
 * Pack a number or a string as a byte buffer.
139
 * @param {number|string} values The value.
140
 * @param {!Object} theType The type definition.
141
 * @param {!Uint8Array|!Array<number>} buffer The output buffer.
142
 * @param {number} index The buffer index to write.
143
 * @return {number} The next index to start writing.
144
 * @throws {Error} If the type definition is not valid.
145
 * @throws {Error} If the value is not valid.
146
 */
147
export function packArrayTo(values, theType, buffer, index) {
148
  setUp_(theType);
149
  let validate = validateNotNull_;
150
  if (theType['char']) {
151
    validate = validateString_;
152
  }
153
  let be = theType['be'];
154
  let offset = theType['offset'];
155
  for (let i=0; i<values.length; i++) {
156
    index = writeBytes_(
157
      values[i],
158
      theType,
159
      buffer,
160
      index,
161
      index + offset,
162
      validate, be);
163
  }
164
  return index;
165
}
166
167
/**
168
 * Unpack a number or a string from a byte buffer.
169
 * @param {!Array<number>|!Uint8Array} buffer The byte buffer.
170
 * @param {!Object} theType The type definition.
171
 * @return {number|string}
172
 * @throws {Error} If the type definition is not valid
173
 */
174
export function unpack(buffer, theType) {
175
  setUp_(theType);
176
  let values = fromBytes_(
177
    buffer.slice(0, theType['offset']), theType);
178
  return values[0];
179
}
180
181
/**
182
 * Unpack an array of numbers or strings from a byte buffer.
183
 * @param {!Array<number>|!Uint8Array} buffer The byte buffer.
184
 * @param {!Object} theType The type definition.
185
 * @return {!Array<number|string>}
186
 * @throws {Error} If the type definition is not valid.
187
 */
188
export function unpackArray(buffer, theType) {
189
  setUp_(theType);
190
  return fromBytes_(buffer, theType);
191
}
192
193
/**
194
 * Unpack a number or a string from a byte buffer.
195
 * @param {!Array<number>|!Uint8Array} buffer The byte buffer.
196
 * @param {!Object} theType The type definition.
197
 * @param {number=} index The buffer index to read.
198
 * @return {number|string}
199
 * @throws {Error} If the type definition is not valid
200
 */
201
export function unpackFrom(buffer, theType, index=0) {
202
  setUp_(theType);
203
  return readBytes_(buffer, theType, index);
204
}
205
206
/**
207
 * Unpack a number or a string from a byte buffer.
208
 * @param {!Array<number>|!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 array 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
 * Turn a byte buffer into what the bytes represent.
233
 * @param {!Array<number|string>|!Uint8Array} buffer An array of bytes.
234
 * @param {!Object} theType The type definition.
235
 * @return {number}
236
 * @private
237
 */
238
function readBytes_(buffer, theType, start) {
239
  if (theType['be']) {
240
    endianness(buffer, theType['offset'], start, start + theType['offset']);
241
  }
242
  let value = reader_(buffer, start);
243
  if (theType['be']) {
244
    endianness(buffer, theType['offset'], start, start + theType['offset']);
245
  }
246
  return value;
247
}
248
249
/**
250
 * Turn a byte buffer into what the bytes represent.
251
 * @param {!Array<number|string>|!Uint8Array} buffer An array of bytes.
252
 * @param {!Object} theType The type definition.
253
 * @return {!Array<number>}
254
 * @private
255
 */
256
function fromBytes_(buffer, theType) {
257
  if (theType['be']) {
258
    endianness(buffer, theType['offset']);
259
  }
260
  let len = buffer.length;
261
  let values = [];
262
  len = len - (theType['offset'] - 1);
263
  for (let i=0; i<len; i+=theType['offset']) {
264
    values.push(reader_(buffer, i));
265
  }
266
  return values;
267
}
268
269
/**
270
 * Turn numbers and strings to bytes.
271
 * @param {!Array<number|string>} values The data.
272
 * @param {!Object} theType The type definition.
273
 * @return {!Array<number|string>} the data as a byte buffer.
274
 * @private
275
 */
276
function toBytes_(values, theType) {
277
  let j = 0;
278
  let bytes = [];
279
  let len = values.length;
280
  let validate = validateNotNull_;
281
  if (theType['char']) {
282
    validate = validateString_;
283
  }
284
  for(let i=0; i < len; i++) {
285
    validate(values[i], theType);
286
    j = writer_(bytes, values[i], j);
287
  }
288
  if (theType['be']) {
289
    endianness(bytes, theType['offset']);
290
  }
291
  return bytes;
292
}
293
294
/**
295
 * Turn numbers and strings to bytes.
296
 * @param {number|string} value The value to be packed.
297
 * @param {!Object} theType The type definition.
298
 * @param {!Object} buffer The buffer to write the bytes to.
299
 * @param {number} index The index to start writing.
300
 * @param {number} len The end index.
301
 * @param {!Function} validate The function used to validate input.
302
 * @param {boolean} be True if big-endian.
303
 * @return {number} the new index to be written.
304
 * @private
305
 */
306
function writeBytes_(value, theType, buffer, index, len, validate, be) {
307
  while (index < len) {
308
    validate(value, theType);
309
    index = writer_(buffer, value, index);
310
  }
311
  if (be) {
312
    endianness(
313
      buffer, theType['offset'], index - theType['offset'], index);
314
  }
315
  return index;
316
}
317
318
/**
319
 * Read int values from bytes.
320
 * @param {!Array<number>|!Uint8Array} bytes An array of bytes.
321
 * @param {number} i The index to read.
322
 * @return {number}
323
 * @private
324
 */
325
function readInt_(bytes, i) {
326
  return gInt_.read(bytes, i);
327
}
328
329
/**
330
 * Read 1 16-bit float from bytes.
331
 * Thanks https://stackoverflow.com/a/8796597
332
 * @param {!Array<number>|!Uint8Array} bytes An array of bytes.
333
 * @param {number} i The index to read.
334
 * @return {number}
335
 * @private
336
 */
337
function read16F_(bytes, i) {
338
  let int = gInt_.read(bytes, i);
339
  let exponent = (int & 0x7C00) >> 10;
340
  let fraction = int & 0x03FF;
341
  let floatValue;
342
  if (exponent) {
343
    floatValue =  Math.pow(2, exponent - 15) * (1 + fraction / 0x400);
344
  } else {
345
    floatValue = 6.103515625e-5 * (fraction / 0x400);
346
  }
347
  return floatValue * (int >> 15 ? -1 : 1);
348
}
349
350
/**
351
 * Read 1 32-bit float from bytes.
352
 * @param {!Array<number>|!Uint8Array} bytes An array of bytes.
353
 * @param {number} i The index to read.
354
 * @return {number}
355
 * @private
356
 */
357
function read32F_(bytes, i) {
358
  ui32_[0] = gInt_.read(bytes, i);
359
  return f32_[0];
360
}
361
362
/**
363
 * Read 1 64-bit float from bytes.
364
 * Thanks https://gist.github.com/kg/2192799
365
 * @param {!Array<number>|!Uint8Array} bytes An array of bytes.
366
 * @param {number} i The index to read.
367
 * @return {number}
368
 * @private
369
 */
370
function read64F_(bytes, i) {
371
  ui32_[0] = gInt_.read(bytes, i);
372
  ui32_[1] = gInt_.read(bytes, i + 4);
373
  return f64_[0];
374
}
375
376
/**
377
 * Read 1 char from bytes.
378
 * @param {!Array<number>|!Uint8Array} bytes An array of bytes.
379
 * @param {number} i The index to read.
380
 * @return {string}
381
 * @private
382
 */
383
function readChar_(bytes, i) {
384
  let chrs = '';
385
  for(let j=0; j < gInt_.offset; j++) {
386
    chrs += String.fromCharCode(bytes[i+j]);
387
  }
388
  return chrs;
389
}
390
391
/**
392
 * Write a integer value to a byte buffer.
393
 * @param {!Array<number>|!Uint8Array} bytes An array of bytes.
394
 * @param {number} number The number to write as bytes.
395
 * @param {number} j The index being written in the byte buffer.
396
 * @return {!number} The next index to write on the byte buffer.
397
 * @private
398
 */
399
function writeInt_(bytes, number, j) {
400
  return gInt_.write(bytes, number, j);
401
}
402
403
/**
404
 * Write one 16-bit float as a binary value.
405
 * @param {!Array<number>|!Uint8Array} bytes An array of bytes.
406
 * @param {number} number The number to write as bytes.
407
 * @param {number} j The index being written in the byte buffer.
408
 * @return {number} The next index to write on the byte buffer.
409
 * @private
410
 */
411
function write16F_(bytes, number, j) {
412
  f32_[0] = number;
413
  let x = ui32_[0];
414
  let bits = (x >> 16) & 0x8000;
415
  let m = (x >> 12) & 0x07ff;
416
  let e = (x >> 23) & 0xff;
417
  if (e >= 103) {
418
    bits |= ((e - 112) << 10) | (m >> 1);
419
    bits += m & 1;
420
  }
421
  bytes[j++] = bits & 0xFF;
422
  bytes[j++] = bits >>> 8 & 0xFF;
423
  return j;
424
}
425
426
/**
427
 * Write one 32-bit float as a binary value.
428
 * @param {!Array<number>|!Uint8Array} bytes An array of bytes.
429
 * @param {number} number The number to write as bytes.
430
 * @param {number} j The index being written in the byte buffer.
431
 * @return {number} The next index to write on the byte buffer.
432
 * @private
433
 */
434
function write32F_(bytes, number, j) {
435
  f32_[0] = number;
436
  return gInt_.write(bytes, ui32_[0], j);
437
}
438
439
/**
440
 * Write one 64-bit float as a binary value.
441
 * @param {!Array<number>|!Uint8Array} bytes An array of bytes.
442
 * @param {number} number The number to write as bytes.
443
 * @param {number} j The index being written in the byte buffer.
444
 * @return {number} The next index to write on the byte buffer.
445
 * @private
446
 */
447
function write64F_(bytes, number, j) {
448
  f64_[0] = number;
449
  j = gInt_.write(bytes, ui32_[0], j);
450
  return gInt_.write(bytes, ui32_[1], j);
451
}
452
453
/**
454
 * Write one char as a byte.
455
 * @param {!Array<number>|!Uint8Array} bytes An array of bytes.
456
 * @param {string} str The string to write as bytes.
457
 * @param {number} j The index being written in the byte buffer.
458
 * @return {number} The next index to write on the byte buffer.
459
 * @private
460
 */
461
function writeChar_(bytes, str, j) {
462
  for (let i=0; i<str.length; i++) {
463
    bytes[j++] = str.charCodeAt(i);
464
  }
465
  return j;
466
}
467
468
/**
469
 * Set the function to unpack the data.
470
 * @param {!Object} theType The type definition.
471
 * @private
472
 */
473
function setReader(theType) {
474
  if (theType['float']) {
475
    if (theType['bits'] == 16) {
476
      reader_ = read16F_;
477
    } else if(theType['bits'] == 32) {
478
      reader_ = read32F_;
479
    } else if(theType['bits'] == 64) {
480
      reader_ = read64F_;
481
    }
482
  } else if (theType['char']) {
483
    reader_ = readChar_;
484
  } else {
485
    reader_ = readInt_;
486
  }
487
}
488
489
/**
490
 * Set the function to pack the data.
491
 * @param {!Object} theType The type definition.
492
 * @private
493
 */
494
function setWriter(theType) {
495
  if (theType['float']) {
496
    if (theType['bits'] == 16) {
497
      writer_ = write16F_;
498
    } else if(theType['bits'] == 32) {
499
      writer_ = write32F_;
500
    } else if(theType['bits'] == 64) {
501
      writer_ = write64F_;
502
    }
503
  } else if (theType['char']) {
504
    writer_ = writeChar_;
505
  } else {
506
    writer_ = writeInt_;
507
  }   
508
}
509
510
/**
511
 * Validate the type and set up the packing/unpacking functions.
512
 * @param {!Object} theType The type definition.
513
 * @throws {Error} If the type definition is not valid.
514
 * @private
515
 */
516
function setUp_(theType) {
517
  validateType_(theType);
518
  theType['offset'] = theType['bits'] < 8 ? 1 : Math.ceil(theType['bits'] / 8);
519
  setReader(theType);
520
  setWriter(theType);
521
  if (!theType['char']) {
522
    gInt_ = new Integer(
523
      theType['bits'] == 64 ? 32 : theType['bits'],
524
      theType['float'] ? false : theType['signed']);
525
  } else {
526
    // Workaround; should not use Integer when type['char']
527
    gInt_.offset = theType['bits'] < 8 ? 1 : Math.ceil(theType['bits'] / 8);
528
  }
529
}
530
531
/**
532
 * Validate the type definition.
533
 * @param {!Object} theType The type definition.
534
 * @throws {Error} If the type definition is not valid.
535
 * @private
536
 */
537
function validateType_(theType) {
538
  if (!theType) {
539
    throw new Error('Undefined type.');
540
  }
541
  if (theType['float']) {
542
    validateFloatType_(theType);
543
  } else {
544
    if (theType['char']) {
545
      validateCharType_(theType);
546
    } else {
547
      validateIntType_(theType);
548
    }
549
  }
550
}
551
552
/**
553
 * Validate the type definition of floating point numbers.
554
 * @param {!Object} theType The type definition.
555
 * @throws {Error} If the type definition is not valid.
556
 * @private
557
 */
558
function validateFloatType_(theType) {
559
  if ([16,32,64].indexOf(theType['bits']) == -1) {
560
    throw new Error('Not a supported float type.');
561
  }
562
}
563
564
/**
565
 * Validate the type definition of char and strings.
566
 * @param {!Object} theType The type definition.
567
 * @throws {Error} If the type definition is not valid.
568
 * @private
569
 */
570
function validateCharType_(theType) {
571
  if (theType['bits'] < 8 || theType['bits'] % 2) {
572
    throw new Error('Wrong offset for type char.');
573
  }
574
}
575
576
/**
577
 * Validate the type definition of integers.
578
 * @param {!Object} theType The type definition.
579
 * @throws {Error} If the type definition is not valid.
580
 * @private
581
 */
582
function validateIntType_(theType) {
583
  if (theType['bits'] < 1 || theType['bits'] > 53) {
584
    throw new Error('Not a supported type.');
585
  }
586
}
587
588
/**
589
 * Validate strings with bad length.
590
 * @param {string|number} value The string to validate.
591
 * @param {!Object} theType The type definition.
592
 * @private
593
 */
594
function validateString_(value, theType) {
595
  validateNotNull_(value);
596
  if (value.length > theType['offset']) {
597
    throw new Error('String is bigger than its type definition.');
598
  } else if (value.length < theType['offset']) {
599
    throw new Error('String is smaller than its type definition.');
600
  }
601
}
602
/**
603
 * Validate that the value is not null.
604
 * @param {string|number} value The value.
605
 * @private
606
 */
607
function validateNotNull_(value) {
608
  if (value === null || value === undefined) {
609
    throw new Error('Cannot pack null or undefined values.');
610
  }
611
}
612