Passed
Push — master ( 13b350...038bd4 )
by Rafael S.
03:46
created

index.js ➔ unpack   A

Complexity

Conditions 1

Size

Total Lines 6
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 5
dl 0
loc 6
rs 10
c 0
b 0
f 0
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 binary parser.
27
 * @see https://github.com/rochars/byte-data
28
 * @see https://github.com/rochars/wavefile
29
 */
30
31
import { endianness } from './lib/endianness';
32
import { pack as packUTF8, unpack as unpackUTF8 } from './lib/utf8-parser';
33
import { IntParser } from './lib/int-parser';
34
import { FloatParser } from './lib/float-parser';
35
36
/**
37
 * Read a string of UTF-8 characters from a byte buffer.
38
 * @param {!(Uint8Array|Array<number>)} buffer A byte buffer.
39
 * @param {number} [index=0] The buffer index to start reading.
40
 * @param {number} [end=buffer.length] The index to stop reading, non inclusive.
41
 * @return {string}
42
 */
43
export function unpackString(buffer, index=0, end=buffer.length) {
44
  return unpackUTF8(buffer, index, end);
45
}
46
47
/**
48
 * Write a string of UTF-8 characters as a byte buffer.
49
 * @param {string} str The string to pack.
50
 * @return {!Array<number>} The UTF-8 string bytes.
51
 */
52
export function packString(str) {
53
  /** @type {!Array<number>} */
54
  let buffer = [];
55
  packUTF8(str, buffer);
56
  return buffer;
57
}
58
59
/**
60
 * Write a string of UTF-8 characters to a byte buffer.
61
 * @param {string} str The string to pack.
62
 * @param {!(Uint8Array|Array<number>)} buffer The output buffer.
63
 * @param {number} [index=0] The buffer index to start writing.
64
 * @return {number} The next index to write in the buffer.
65
 */
66
export function packStringTo(str, buffer, index=0) {
67
  return packUTF8(str, buffer, index);
68
}
69
70
// Numbers
71
/**
72
 * Pack a array of numbers to a byte buffer.
73
 * All other packing functions are interfaces to this function.
74
 * @param {!(Array<number>|TypedArray)} values The values to pack.
75
 * @param {!{bits:number,
76
 *   fp: (boolean|undefined),
77
 *   signed: (boolean|undefined),
78
 *   be: (boolean|undefined)}} theType The type definition.
79
 * @param {!(Uint8Array|Array<number>)} buffer The buffer to write on.
80
 * @param {number} [index=0] The buffer index to start writing.
81
 * @return {number} The next index to write.
82
 * @throws {Error} If the type definition is not valid.
83
 */
84
export function packArrayTo(values, theType, buffer, index=0) {
85
  theType = theType || {};
86
  /** @type {!Object} */
87
  let packer = getParser_(theType.bits, theType.fp, theType.signed);
88
  /** @type {number} */
89
  let offset = Math.ceil(theType.bits / 8);
90
  /** @type {number} */
91
  let i = 0;
92
  /** @type {number} */
93
  let start = index;
94
  for (let valuesLen = values.length; i < valuesLen; i++) {
95
    index = packer.pack(buffer, values[i], index);
96
  }
97
  if (theType.be) {
98
    endianness(buffer, offset, start, index);
99
  }
100
  return index;
101
}
102
103
/**
104
 * Unpack a array of numbers from a byte buffer to a array or a typed array.
105
 * All other unpacking functions are interfaces to this function.
106
 * @param {!(Uint8Array|Array<number>)} buffer The byte buffer.
107
 * @param {!{bits:number,
108
 *   fp: (boolean|undefined),
109
 *   signed: (boolean|undefined),
110
 *   be: (boolean|undefined)}} theType The type definition.
111
 * @param {!(TypedArray|Array<number>)} output The output array or typed array.
112
 * @param {number} [start=0] The buffer index to start reading.
113
 * @param {number} [end=buffer.length] The buffer index to stop reading.
114
 * @throws {Error} If the type definition is not valid.
115
 */
116
export function unpackArrayTo(
117
    buffer, theType, output, start=0, end=buffer.length) {
118
  theType = theType || {};
119
  /** @type {!Object} */
120
  let parser = getParser_(theType.bits, theType.fp, theType.signed);
121
  // getUnpackLen_ will adjust the end index according to the size
122
  // of the input buffer and the byte offset or throw a error on bad
123
  // end index if safe=true
124
  end = getUnpackLen_(buffer, start, end, parser.offset);
125
  if (theType.be) {
126
    /** @type {!(Uint8Array|Array<number>)} */
127
    let readBuffer = copyBuffer_(buffer);
128
    if (theType.be) {
129
      endianness(readBuffer, parser.offset, start, end);
130
    }
131
    unpack_(readBuffer, output, start, end, parser);
132
  } else {
133
    unpack_(buffer, output, start, end, parser);
134
  }
135
}
136
137
/**
138
 * Pack a number to a byte buffer.
139
 * @param {number} value The value.
140
 * @param {!{bits:number,
141
 *   fp: (boolean|undefined),
142
 *   signed: (boolean|undefined),
143
 *   be: (boolean|undefined)}} theType The type definition.
144
 * @param {!(Uint8Array|Array<number>)} buffer The byte buffer to write on.
145
 * @param {number} [index=0] The buffer index to write.
146
 * @return {number} The next index to write.
147
 * @throws {Error} If the type definition is not valid.
148
 */
149
export function packTo(value, theType, buffer, index=0) {
150
  return packArrayTo([value], theType, buffer, index);
151
}
152
153
/**
154
 * Pack a number as a array of bytes.
155
 * @param {number} value The number to pack.
156
 * @param {!{bits:number,
157
 *   fp: (boolean|undefined),
158
 *   signed: (boolean|undefined),
159
 *   be: (boolean|undefined)}} theType The type definition.
160
 * @return {!Array<number>} The packed value.
161
 * @throws {Error} If the type definition is not valid.
162
 */
163
export function pack(value, theType) {
164
  /** @type {!Array<number>} */
165
  let output = [];
166
  packTo(value, theType, output, 0);
167
  return output;
168
}
169
170
/**
171
 * Unpack a number from a byte buffer.
172
 * @param {!(Uint8Array|Array<number>)} buffer The byte buffer.
173
 * @param {!{bits:number,
174
 *   fp: (boolean|undefined),
175
 *   signed: (boolean|undefined),
176
 *   be: (boolean|undefined)}} theType The type definition.
177
 * @param {number} [index=0] The buffer index to read.
178
 * @return {number}
179
 * @throws {Error} If the type definition is not valid.
180
 */
181
export function unpack(buffer, theType, index=0) {
182
  let output = [];
183
  unpackArrayTo(buffer, theType, output,
184
    index, index + Math.ceil(theType.bits / 8));
185
  return output[0];
186
}
187
188
/**
189
 * Unpack a array of numbers from a byte buffer to a array or a typed array.
190
 * @param {!(Uint8Array|Array<number>)} buffer The byte buffer.
191
 * @param {!(TypedArray|Array<number>)} output The output array or typed array.
192
 * @param {number} start The buffer index to start reading.
193
 * @param {number} end The buffer index to stop reading.
194
 * @param {!Object} parser The parser.
195
 * @private
196
 */
197
function unpack_(buffer, output, start, end, parser) {
198
  /** @type {number} */
199
  let offset = parser.offset;
200
  for (let index = 0, j = start; j < end; j += offset, index++) {
201
    output[index] = parser.unpack(buffer, j);
202
  }
203
}
204
205
/**
206
 * Copy a byte buffer as a Array or Uint8Array.
207
 * @param {!(Uint8Array|Array<number>)} buffer The byte buffer.
208
 * @return {!(Uint8Array|Array<number>)}
209
 * @private
210
 */
211
function copyBuffer_(buffer) {
212
  return new Uint8Array(buffer);
213
}
214
215
/**
216
 * Adjust the end index according to the input buffer length and the
217
 * type offset.
218
 * @param {!(Uint8Array|Array<number>)} buffer The byte buffer.
219
 * @param {number} start The buffer index to start reading.
220
 * @param {number} end The buffer index to stop reading.
221
 * @param {number} offset The number of bytes used by the type.
222
 * @private
223
 */
224
function getUnpackLen_(buffer, start, end, offset) {
225
  /** @type {number} */
226
  let extra = (end - start) % offset;
227
  return end - extra;
228
}
229
230
/**
231
 * Return a parser for int, uint or fp numbers.
232
 * @param {number} bits The number of bits.
233
 * @param {boolean|undefined} fp True for fp numbers, false otherwise.
234
 * @param {boolean|undefined} signed True for signed ints, false otherwise.
235
 * @return {!Object}
236
 * @private
237
 */
238
function getParser_(bits, fp, signed) {
239
  if (fp && bits == 32) {
240
    return new FloatParser(8, 23);
241
  } else if(fp && bits == 64) {
242
    return new FloatParser(11, 52);
243
  }
244
  return new IntParser(bits, signed);
245
}
246