Completed
Push — master ( a1a5d5...5f5aeb )
by Rafael S.
03:30
created

index.js ➔ unpackArrayTo   A

Complexity

Conditions 4
Paths 24

Size

Total Lines 27
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 4
eloc 16
c 2
b 0
f 0
nc 24
nop 6
dl 0
loc 27
rs 9.6
1
/*
2
 * Copyright (c) 2017-2018 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 Functions to read and write numbers and strings as bytes.
27
 * @see https://github.com/rochars/byte-data
28
 */
29
30
/** @module byteData */
31
32
import endianness from 'endianness';
33
import {pack as packUTF8, unpack as unpackUTF8} from 'utf8-buffer';
34
import NumberBuffer from './lib/number-buffer.js';
35
import {validateIsNumber} from './lib/validation.js';
36
37
/**
38
 * Throw a value error.
39
 * @throws {Error} A Error with a message based on the input params.
40
 */
41
function throwValueError_(e, value, i, fp) {
42
  if (!fp && (
43
      value === Infinity || value === -Infinity || value !== value)) {
44
    throw new Error('Argument is not a integer at input index ' + i);
45
  } else {
46
    throw new Error(e.message + ' at input index ' + i);
47
  }
48
}
49
50
/**
51
 * Unpack a array of numbers to a typed array.
52
 * All other unpacking functions are interfaces to this function.
53
 * @param {!Uint8Array|!Array<number>} buffer The byte buffer.
54
 * @param {number=} start The buffer index to start reading.
55
 * @param {number=} end The buffer index to stop reading.
56
 * @param {number=} offset The number of bytes used by the type.
57
 * @param {boolean=} safe True for size-safe buffer reading.
58
 * @throws {Error} On bad buffer length, if safe.
59
 */
60
function getUnpackLen_(buffer, start, end, offset, safe) {
61
  /** @type {number} */
62
  let extra = (end - start) % offset;
63
  if (safe && (extra || buffer.length < offset)) {
64
    throw new Error('Bad buffer length');
65
  }
66
  return end - extra;
67
}
68
69
/**
70
 * Read a string of UTF-8 characters from a byte buffer.
71
 * @param {!Uint8Array|!Array<number>} buffer A byte buffer.
72
 * @param {number=} index The buffer index to start reading.
73
 * @param {number=} end The buffer index to stop reading, non inclusive.
74
 *   Assumes buffer length if undefined.
75
 * @return {string}
76
 */
77
export function unpackString(buffer, index=0, end=buffer.length) {
78
  return unpackUTF8(buffer, index, end);
79
}
80
81
/**
82
 * Write a string of UTF-8 characters as a byte buffer.
83
 * @param {string} str The string to pack.
84
 * @return {!Array<number>} The UTF-8 string bytes.
85
 */ 
86
export function packString(str) {
87
  /** @type {!Array<number>} */
88
  let buffer = [];
89
  packUTF8(str, buffer, 0);
90
  return buffer;
91
}
92
93
/**
94
 * Write a string of UTF-8 characters to a byte buffer.
95
 * @param {string} str The string to pack.
96
 * @param {!Uint8Array|!Array<number>} buffer The output buffer.
97
 * @param {number=} index The buffer index to start writing.
98
 *   Assumes zero if undefined.
99
 * @return {number} The next index to write in the buffer.
100
 */
101
export function packStringTo(str, buffer, index=0) {
102
  return packUTF8(str, buffer, index);
103
}
104
105
// Numbers
106
/**
107
 * Pack a array of numbers to a byte buffer.
108
 * All other packing functions are interfaces to this function.
109
 * @param {!Array<number>|!TypedArray} values The value.
110
 * @param {!Object} theType The type definition.
111
 * @param {!Uint8Array|!Array<number>} buffer The output buffer.
112
 * @param {number=} index The buffer index to start writing.
113
 *   Assumes zero if undefined.
114
 * @return {number} The next index to write.
115
 * @throws {Error} If the type definition is not valid.
116
 * @throws {Error} If the value is not valid.
117
 */
118
export function packArrayTo(values, theType, buffer, index=0) {
119
  theType = theType || {};
120
  /** @type {NumberBuffer} */
121
  let packer = new NumberBuffer(theType.bits, theType.fp, theType.signed);
122
  /** @type {number} */
123
  let i = 0;
124
  /** @type {number} */
125
  let start = index;
126
  try {
127
    for (let valuesLen = values.length; i < valuesLen; i++) {
128
      validateIsNumber(values[i]);
129
      index = packer.pack(buffer, values[i], index);
130
    }
131
    if (theType.be) {
132
      endianness(buffer, packer.offset, start, index);
133
    }
134
  } catch (e) {
135
    throwValueError_(e, values[i], i, theType.fp);
136
  }
137
  return index;
138
}
139
140
/**
141
 * Unpack a array of numbers to a typed array.
142
 * All other unpacking functions are interfaces to this function.
143
 * @param {!Uint8Array|!Array<number>} buffer The byte buffer.
144
 * @param {!Object} theType The type definition.
145
 * @param {!TypedArray|!Array<number>} output The output array.
146
 * @param {number=} start The buffer index to start reading.
147
 *   Assumes zero if undefined.
148
 * @param {number=} end The buffer index to stop reading.
149
 *   Assumes the buffer length if undefined.
150
 * @param {boolean=} safe If set to false, extra bytes in the end of
151
 *   the array are ignored and input buffers with insufficient bytes will
152
 *   write nothing to the output array. If safe is set to true the function
153
 *   will throw a 'Bad buffer length' error. Defaults to false.
154
 * @throws {Error} If the type definition is not valid
155
 * @throws {Error} On overflow
156
 */
157
export function unpackArrayTo(
158
    buffer, theType, output, start=0, end=buffer.length, safe=false) {
159
  theType = theType || {};
160
  /** @type {NumberBuffer} */
161
  let packer = new NumberBuffer(theType.bits, theType.fp, theType.signed);
162
  /** @type {number} */
163
  let offset = packer.offset;
164
  // getUnpackLen_ will either fix the length of the input buffer
165
  // according to the byte offset of the type (on unsafe mode) or
166
  // throw a Error if the input buffer has a bad length (on safe mode)
167
  end = getUnpackLen_(buffer, start, end, offset, safe);
168
  /** @type {number} */
169
  let index = 0;
170
  try {
171
    if (theType.be) {
172
      endianness(buffer, offset, start, end);
173
    }
174
    for (let j = start; j < end; j += offset, index++) {
175
      output[index] = packer.unpack(buffer, j);
176
    }
177
    if (theType.be) {
178
      endianness(buffer, offset, start, end);
179
    }
180
  } catch (e) {
181
    throw new Error(e.message + ' at output index ' + index);
182
  }
183
}
184
185
/**
186
 * Pack a number to a byte buffer.
187
 * @param {number} value The value.
188
 * @param {!Object} theType The type definition.
189
 * @param {!Uint8Array|!Array<number>} buffer The output buffer.
190
 * @param {number=} index The buffer index to write. Assumes 0 if undefined.
191
 * @return {number} The next index to write.
192
 * @throws {Error} If the type definition is not valid.
193
 * @throws {Error} If the value is not valid.
194
 */
195
export function packTo(value, theType, buffer, index=0) {
196
  return packArrayTo([value], theType, buffer, index);
197
}
198
199
/**
200
 * Pack a number as a byte buffer.
201
 * @param {number} value The number.
202
 * @param {!Object} theType The type definition.
203
 * @return {!Array<number>} The packed value.
204
 * @throws {Error} If the type definition is not valid.
205
 * @throws {Error} If the value is not valid.
206
 */
207
export function pack(value, theType) {
208
  /** @type {!Array<number>} */
209
  let output = [];
210
  packTo(value, theType, output);
211
  return output;
212
}
213
214
/**
215
 * Pack an array of numbers as a byte buffer.
216
 * @param {!Array<number>|!TypedArray} values The values.
217
 * @param {!Object} theType The type definition.
218
 * @return {!Array<number>} The packed values.
219
 * @throws {Error} If the type definition is not valid.
220
 * @throws {Error} If any of the values are not valid.
221
 */
222
export function packArray(values, theType) {
223
  /** @type {!Array<number>} */
224
  let output = [];
225
  packArrayTo(values, theType, output);
226
  return output;
227
}
228
229
/**
230
 * Unpack an array of numbers from a byte buffer.
231
 * @param {!Uint8Array|!Array<number>} buffer The byte buffer.
232
 * @param {!Object} theType The type definition.
233
 * @param {number=} start The buffer index to start reading.
234
 *   Assumes zero if undefined.
235
 * @param {number=} end The buffer index to stop reading.
236
 *   Assumes the buffer length if undefined.
237
 * @param {boolean=} safe If set to false, extra bytes in the end of
238
 *   the array are ignored and input buffers with insufficient bytes will
239
 *   output a empty array. If safe is set to true the function
240
 *   will throw a 'Bad buffer length' error. Defaults to false.
241
 * @return {!Array<number>}
242
 * @throws {Error} If the type definition is not valid
243
 * @throws {Error} On overflow
244
 */
245
export function unpackArray(
246
    buffer, theType, start=0, end=buffer.length, safe=false) {
247
  /** @type {!Array<number>} */
248
  let output = [];
249
  unpackArrayTo(buffer, theType, output, start, end, safe);
250
  return output;
251
}
252
253
/**
254
 * Unpack a number from a byte buffer.
255
 * @param {!Uint8Array|!Array<number>} buffer The byte buffer.
256
 * @param {!Object} theType The type definition.
257
 * @param {number=} index The buffer index to read. Assumes zero if undefined.
258
 * @return {number}
259
 * @throws {Error} If the type definition is not valid
260
 * @throws {Error} On bad buffer length.
261
 * @throws {Error} On overflow
262
 */
263
export function unpack(buffer, theType, index=0) {
264
  return unpackArray(
265
    buffer, theType, index, index + Math.ceil(theType.bits / 8), true)[0];
266
}
267