Completed
Push — master ( f33066...08b2cb )
by Rafael S.
05:59
created

index.js   A

Complexity

Total Complexity 29
Complexity/F 1.93

Size

Lines of Code 315
Function Count 15

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 91
dl 0
loc 315
rs 10
c 0
b 0
f 0
wmc 29
mnd 14
bc 14
fnc 15
bpm 0.9333
cpm 1.9333
noi 0

15 Functions

Rating   Name   Duplication   Size   Complexity  
A ➔ packString 0 6 1
A ➔ packStringTo 0 3 1
A ➔ unpackString 0 3 1
A ➔ unpackArrayTo 0 29 5
A ➔ packArrayTo 0 22 4
A ➔ packTo 0 3 1
A ➔ validateIntType 0 5 2
A ➔ throwValueError_ 0 5 1
A ➔ pack 0 6 1
A ➔ packArray 0 6 1
A ➔ getParser_ 0 15 5
A ➔ unpack 0 5 1
A ➔ getUnpackLen_ 0 8 2
A ➔ unpackArray 0 7 1
A ➔ validateFloatType 0 5 2
1
/*
2
 * Copyright (c) 2017-2019 Rafael da Silva Rocha.
3
 *
4
 * Permission is hereby granted, free of charge, to any person obtaining
5
 * a copy of this software and associated documentation files (the
6
 * "Software"), to deal in the Software without restriction, including
7
 * without limitation the rights to use, copy, modify, merge, publish,
8
 * distribute, sublicense, and/or sell copies of the Software, and to
9
 * permit persons to whom the Software is furnished to do so, subject to
10
 * the following conditions:
11
 *
12
 * The above copyright notice and this permission notice shall be
13
 * included in all copies or substantial portions of the Software.
14
 *
15
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
 *
23
 */
24
25
/**
26
 * @fileoverview JavaScript binary parser for any browser or environment.
27
 * @see https://github.com/rochars/byte-data
28
 */
29
30
/** @module byte-data */
31
32
import endianness from 'endianness';
33
import { pack as packUTF8, unpack as unpackUTF8 } from 'utf8-buffer';
34
import { IntParser } from './lib/int-parser';
35
import { IEEE754Buffer } from 'ieee754-buffer';
36
37
/**
38
 * Read a string of UTF-8 characters from a byte buffer.
39
 * @param {!Uint8Array|!Array<number>} buffer A byte buffer.
40
 * @param {number=} index The buffer index to start reading.
41
 * @param {number=} end The buffer index to stop reading, non inclusive.
42
 *   Assumes buffer length if undefined.
43
 * @return {string}
44
 */
45
export function unpackString(buffer, index=0, end=buffer.length) {
46
  return unpackUTF8(buffer, index, end);
47
}
48
49
/**
50
 * Write a string of UTF-8 characters as a byte buffer.
51
 * @param {string} str The string to pack.
52
 * @return {!Array<number>} The UTF-8 string bytes.
53
 */ 
54
export function packString(str) {
55
  /** @type {!Array<number>} */
56
  let buffer = [];
57
  packUTF8(str, buffer, 0);
58
  return buffer;
59
}
60
61
/**
62
 * Write a string of UTF-8 characters to a byte buffer.
63
 * @param {string} str The string to pack.
64
 * @param {!Uint8Array|!Array<number>} buffer The output buffer.
65
 * @param {number=} index The buffer index to start writing.
66
 *   Assumes zero if undefined.
67
 * @return {number} The next index to write in the buffer.
68
 */
69
export function packStringTo(str, buffer, index=0) {
70
  return packUTF8(str, buffer, index);
71
}
72
73
// Numbers
74
/**
75
 * Pack a array of numbers to a byte buffer.
76
 * All other packing functions are interfaces to this function.
77
 * @param {!Array<number>|!TypedArray} values The value.
78
 * @param { {bits:number, fp: boolean, signed: boolean, be: boolean} } theType
79
    The type definition.
80
 * @param {!Uint8Array|!Array<number>} buffer The output buffer.
81
 * @param {number=} index The buffer index to start writing.
82
 *   Assumes zero if undefined.
83
 * @param {boolean=} clamp True to clamp ints on overflow. Default is false.
84
 * @return {number} The next index to write.
85
 * @throws {Error} If the type definition is not valid.
86
 * @throws {RangeError} On overflow.
87
 * @throws {TypeError} If input is not valid.
88
 */
89
export function packArrayTo(values, theType, buffer, index=0, clamp=false) {
90
  theType = theType || {};
91
  /** @type {Object} */
92
  let packer = getParser_(theType.bits, theType.fp, theType.signed, clamp);
93
  /** @type {number} */
94
  let offset = Math.ceil(theType.bits / 8);
95
  /** @type {number} */
96
  let i = 0;
97
  /** @type {number} */
98
  let start = index;
99
  try {
100
    for (let valuesLen = values.length; i < valuesLen; i++) {
101
      index = packer.pack(buffer, values[i], index);
102
    }
103
    if (theType.be) {
104
      endianness(buffer, offset, start, index);
105
    }
106
  } catch (e) {
107
    throwValueError_(e, values[i], i);
108
  }
109
  return index;
110
}
111
112
/**
113
 * Unpack a array of numbers to a typed array.
114
 * All other unpacking functions are interfaces to this function.
115
 * @param {!Uint8Array|!Array<number>} buffer The byte buffer.
116
 * @param { {bits:number, fp: boolean, signed: boolean, be: boolean} } theType
117
    The type definition.
118
 * @param {!TypedArray|!Array<number>} output The output array.
119
 * @param {number=} start The buffer index to start reading.
120
 *   Assumes zero if undefined.
121
 * @param {number=} end The buffer index to stop reading.
122
 *   Assumes the buffer length if undefined.
123
 * @param {boolean=} safe If set to false, extra bytes in the end of
124
 *   the array are ignored and input buffers with insufficient bytes will
125
 *   write nothing to the output array. If safe is set to true the function
126
 *   will throw a 'Bad buffer length' error. Defaults to false.
127
 * @param {boolean=} clamp True to clamp ints on overflow. Default is false.
128
 * @throws {Error} If the type definition is not valid
129
 * @throws {RangeError} On overflow
130
 */
131
export function unpackArrayTo(
132
    buffer, theType, output, start=0, end=buffer.length,
133
    safe=false, clamp=false) {
134
  theType = theType || {};
135
  /** @type {Object} */
136
  let packer = getParser_(theType.bits, theType.fp, theType.signed, clamp);
137
  /** @type {number} */
138
  let offset = Math.ceil(theType.bits / 8);
139
  // getUnpackLen_ will either fix the length of the input buffer
140
  // according to the byte offset of the type (on unsafe mode) or
141
  // throw a Error if the input buffer has a bad length (on safe mode)
142
  end = getUnpackLen_(buffer, start, end, offset, safe);
143
  /** @type {number} */
144
  let index = 0;
145
  let j = start;
146
  try {
147
    if (theType.be) {
148
      endianness(buffer, offset, start, end);
149
    }
150
    for (; j < end; j += offset, index++) {
151
      output[index] = packer.unpack(buffer, j);
152
    }
153
    if (theType.be) {
154
      endianness(buffer, offset, start, end);
155
    }
156
  } catch (e) {
157
    throwValueError_(e, buffer.slice(j, j + offset), j);
158
  }
159
}
160
161
/**
162
 * Pack a number to a byte buffer.
163
 * @param {number} value The value.
164
 * @param { {bits:number, fp: boolean, signed: boolean, be: boolean} } theType
165
    The type definition.
166
 * @param {!Uint8Array|!Array<number>} buffer The output buffer.
167
 * @param {number=} index The buffer index to write. Assumes 0 if undefined.
168
 * @param {boolean=} clamp True to clamp ints on overflow. Default is false.
169
 * @return {number} The next index to write.
170
 * @throws {Error} If the type definition is not valid.
171
 * @throws {RangeError} On overflow.
172
 * @throws {TypeError} If input is not valid.
173
 */
174
export function packTo(value, theType, buffer, index=0, clamp=false) {
175
  return packArrayTo([value], theType, buffer, index, clamp);
176
}
177
178
/**
179
 * Pack a number as a byte buffer.
180
 * @param {number} value The number.
181
 * @param {!{bits:number, fp: boolean, signed: boolean, be: boolean}} theType
182
    The type definition.
183
 * @param {boolean=} clamp True to clamp ints on overflow. Default is false.
184
 * @return {!Array<number>} The packed value.
185
 * @throws {Error} If the type definition is not valid.
186
 * @throws {RangeError} On overflow.
187
 * @throws {TypeError} If input is not valid.
188
 */
189
export function pack(value, theType, clamp=false) {
190
  /** @type {!Array<number>} */
191
  let output = [];
192
  packTo(value, theType, output, 0, clamp);
193
  return output;
194
}
195
196
/**
197
 * Pack an array of numbers as a byte buffer.
198
 * @param {!Array<number>|!TypedArray} values The values.
199
 * @param {!{bits:number, fp: boolean, signed: boolean, be: boolean}} theType
200
    The type definition.
201
 * @param {boolean=} clamp True to clamp ints on overflow. Default is false.
202
 * @return {!Array<number>} The packed values.
203
 * @throws {Error} If the type definition is not valid.
204
 * @throws {RangeError} On overflow.
205
 * @throws {TypeError} If input is not valid.
206
 */
207
export function packArray(values, theType, clamp=false) {
208
  /** @type {!Array<number>} */
209
  let output = [];
210
  packArrayTo(values, theType, output, 0, clamp);
211
  return output;
212
}
213
214
/**
215
 * Unpack an array of numbers from a byte buffer.
216
 * @param {!Uint8Array|!Array<number>} buffer The byte buffer.
217
 * @param {!{bits:number, fp: boolean, signed: boolean, be: boolean}} theType
218
    The type definition.
219
 * @param {number=} start The buffer index to start reading.
220
 *   Assumes zero if undefined.
221
 * @param {number=} end The buffer index to stop reading.
222
 *   Assumes the buffer length if undefined.
223
 * @param {boolean=} safe If set to false, extra bytes in the end of
224
 *   the array are ignored and input buffers with insufficient bytes will
225
 *   output a empty array. If safe is set to true the function
226
 *   will throw a 'Bad buffer length' error. Defaults to false.
227
 * @param {boolean=} clamp True to clamp ints on overflow. Default is false.
228
 * @return {!Array<number>}
229
 * @throws {Error} If the type definition is not valid
230
 * @throws {RangeError} On overflow
231
 */
232
export function unpackArray(
233
    buffer, theType, start=0, end=buffer.length, safe=false, clamp=false) {
234
  /** @type {!Array<number>} */
235
  let output = [];
236
  unpackArrayTo(buffer, theType, output, start, end, safe, clamp);
237
  return output;
238
}
239
240
/**
241
 * Unpack a number from a byte buffer.
242
 * @param {!Uint8Array|!Array<number>} buffer The byte buffer.
243
 * @param {!{bits:number, fp: boolean, signed: boolean, be: boolean}} theType
244
    The type definition.
245
 * @param {number=} index The buffer index to read. Assumes zero if undefined.
246
 * @param {boolean=} clamp True to clamp ints on overflow. Default is false.
247
 * @return {number}
248
 * @throws {Error} If the type definition is not valid
249
 * @throws {Error} On bad buffer length.
250
 * @throws {RangeError} On overflow
251
 */
252
export function unpack(buffer, theType, index=0, clamp=false) {
253
  return unpackArray(
254
    buffer, theType, index, index + Math.ceil(theType.bits / 8),
255
    true, clamp)[0];
256
}
257
258
/**
259
 * Throw a error with information about the problem.
260
 * @param {!Object} err The Error object that is being raised.
261
 * @param {*} value The value that caused the error.
262
 * @param {number} index The index of the value that caused the error.
263
 * @throws {RangeError|TypeError|Error} A Error with a message.
264
 * @private
265
 */
266
function throwValueError_(err, value, index) {
267
  err.message = err.constructor.name +
268
    ' at index ' + index + ': ' + value;
269
  throw err;
270
}
271
272
/**
273
 * Unpack a array of numbers to a typed array.
274
 * All other unpacking functions are interfaces to this function.
275
 * @param {!Uint8Array|!Array<number>} buffer The byte buffer.
276
 * @param {number=} start The buffer index to start reading.
277
 * @param {number=} end The buffer index to stop reading.
278
 * @param {number=} offset The number of bytes used by the type.
279
 * @param {boolean=} safe True for size-safe buffer reading.
280
 * @throws {Error} On bad buffer length, if safe.
281
 * @private
282
 */
283
function getUnpackLen_(buffer, start, end, offset, safe) {
284
  /** @type {number} */
285
  let extra = (end - start) % offset;
286
  if (safe && (extra || buffer.length < offset)) {
287
    throw new Error('Bad buffer length');
288
  }
289
  return end - extra;
290
}
291
292
/**
293
 * Return a parser for int, uint or fp numbers.
294
 * @param {number} bits The number of bits.
295
 * @param {boolean} fp True for fp numbers, false otherwise.
296
 * @param {boolean} signed True for signed ints, false otherwise.
297
 * @param {boolean} clamp True to clamp ints on overflow, false otherwise.
298
 * @return {!Object}
299
 * @private
300
 */
301
function getParser_(bits, fp, signed, clamp) {
302
  if (fp) {
303
    validateFloatType(bits);
304
  } else {
305
    validateIntType(bits);
306
  }
307
  if (fp && bits === 16) {
308
    return new IEEE754Buffer(5, 11);
309
  } else if (fp && bits == 32) {
310
    return new IEEE754Buffer(8, 23);
311
  } else if(fp && bits == 64) {
312
    return new IEEE754Buffer(11, 52);
313
  }
314
  return new IntParser(bits, signed, clamp);
315
}
316
317
/**
318
 * The type definition error message.
319
 * @type {string}
320
 * @private
321
 */
322
const TYPE_ERR = 'Unsupported type';
323
324
/**
325
 * Validate the type definition of floating-point numbers.
326
 * @param {number} bits The number of bits.
327
 * @throws {Error} If the type definition is not valid.
328
 * @private
329
 */
330
function validateFloatType(bits) {
331
  if (!bits || bits !== 16 && bits !== 32 && bits !== 64) {
332
    throw new Error(TYPE_ERR + ': float, bits: ' + bits);
333
  }
334
}
335
336
/**
337
 * Validate the type definition of integers.
338
 * @param {number} bits The number of bits.
339
 * @throws {Error} If the type definition is not valid.
340
 * @private
341
 */
342
function validateIntType(bits) {
343
  if (!bits || bits < 1 || bits > 53) {
344
    throw new Error(TYPE_ERR + ': int, bits: ' + bits);
345
  }
346
}
347