Passed
Branch v13.x (3f0d34)
by Rafael S.
02:14
created

T_IMPORT   A

Complexity

Total Complexity 28
Complexity/F 2.33

Size

Lines of Code 1
Function Count 12

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 0
wmc 28
mnd 4
bc 27
fnc 12
dl 0
loc 1
rs 10
bpm 2.25
cpm 2.3333
noi 1
c 0
b 0
f 0
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 Function to serialize binary data.
27
 * @see https://github.com/rochars/byte-data
28
 */
29
30
import Integer from './integer.js';
31
import endianness from './endianness.js';
32
import {validateType} from './validation.js';
33
34
/**
35
 * Use a Typed Array to check if the host is BE or LE. This will impact
36
 * on how 64-bit floating point numbers are handled.
37
 * @type {boolean}
38
 * @private
39
 */
40
let HOST_BE_ = new Uint8Array(new Uint32Array([0x12345678]).buffer)[0] === 0x12;
41
42
/**
43
 * @type {!Int8Array}
44
 * @private
45
 */
46
const int8_ = new Int8Array(8);
47
/**
48
 * @type {!Uint32Array}
49
 * @private
50
 */
51
const ui32_ = new Uint32Array(int8_.buffer);
52
/**
53
 * @type {!Float32Array}
54
 * @private
55
 */
56
const f32_ = new Float32Array(int8_.buffer);
57
/**
58
 * @type {!Float64Array}
59
 * @private
60
 */
61
const f64_ = new Float64Array(int8_.buffer);
62
/**
63
 * @type {Function}
64
 * @private
65
 */
66
let reader_;
67
/**
68
 * @type {Function}
69
 * @private
70
 */
71
let writer_;
72
/**
73
 * @type {Object}
74
 * @private
75
 */
76
let gInt_ = {};
77
78
/**
79
 * Validate the type and set up the packing/unpacking functions.
80
 * @param {!Object} theType The type definition.
81
 * @throws {Error} If the type definition is not valid.
82
 * @private
83
 */
84
export function setUp_(theType) {
85
  validateType(theType);
86
  theType.offset = theType.bits < 8 ? 1 : Math.ceil(theType.bits / 8);
87
  theType.be = theType.be || false;
88
  setReader(theType);
89
  setWriter(theType);
90
  gInt_ = new Integer(
91
    theType.bits == 64 ? 32 : theType.bits,
92
    theType.float ? false : theType.signed);
93
}
94
95
/**
96
 * Turn numbers to bytes.
97
 * @param {number} value The value to be packed.
98
 * @param {!Object} theType The type definition.
99
 * @param {!Uint8Array|!Array<number>} buffer The buffer to write the bytes to.
100
 * @param {number} index The index to start writing.
101
 * @param {number} len The end index.
102
 * @param {!Function} validate The function used to validate input.
103
 * @param {boolean} be True if big-endian.
104
 * @return {number} the new index to be written.
105
 * @private
106
 */
107
export function writeBytes_(value, theType, buffer, index, len, validate, be) {
108
  while (index < len) {
109
    validate(value, theType);
110
    index = writer_(buffer, value, index);
111
  }
112
  if (be) {
113
    endianness(
114
      buffer, theType.offset, index - theType.offset, index);
115
  }
116
  return index;
117
}
118
119
/**
120
 * Read int values from bytes.
121
 * @param {!Uint8Array} bytes An array of bytes.
122
 * @param {number} i The index to read.
123
 * @return {number}
124
 * @private
125
 */
126
function readInt_(bytes, i) {
127
  return gInt_.read(bytes, i);
128
}
129
130
/**
131
 * Read 1 16-bit float from bytes.
132
 * Thanks https://stackoverflow.com/a/8796597
133
 * @param {!Uint8Array} bytes An array of bytes.
134
 * @param {number} i The index to read.
135
 * @return {number}
136
 * @private
137
 */
138
function read16F_(bytes, i) {
139
  let int = gInt_.read(bytes, i);
140
  let exponent = (int & 0x7C00) >> 10;
141
  let fraction = int & 0x03FF;
142
  let floatValue;
143
  if (exponent) {
144
    floatValue =  Math.pow(2, exponent - 15) * (1 + fraction / 0x400);
145
  } else {
146
    floatValue = 6.103515625e-5 * (fraction / 0x400);
147
  }
148
  return floatValue * (int >> 15 ? -1 : 1);
149
}
150
151
/**
152
 * Read 1 32-bit float from bytes.
153
 * @param {!Uint8Array} bytes An array of bytes.
154
 * @param {number} i The index to read.
155
 * @return {number}
156
 * @private
157
 */
158
function read32F_(bytes, i) {
159
  ui32_[0] = gInt_.read(bytes, i);
160
  return f32_[0];
161
}
162
163
/**
164
 * Read 1 64-bit float from bytes.
165
 * Thanks https://gist.github.com/kg/2192799
166
 * @param {!Uint8Array} bytes An array of bytes.
167
 * @param {number} i The index to read.
168
 * @return {number}
169
 * @private
170
 */
171
function read64F_(bytes, i) {
172
  if (HOST_BE_) {
173
    ui32_[1] = gInt_.read(bytes, i);
174
    ui32_[0] = gInt_.read(bytes, i + 4);
175
  } else {
176
    ui32_[0] = gInt_.read(bytes, i);
177
    ui32_[1] = gInt_.read(bytes, i + 4);
178
  }
179
  return f64_[0];
180
}
181
182
/**
183
 * Write a integer value to a byte buffer.
184
 * @param {!Uint8Array} bytes An array of bytes.
185
 * @param {number} number The number to write as bytes.
186
 * @param {number} j The index being written in the byte buffer.
187
 * @return {!number} The next index to write on the byte buffer.
188
 * @private
189
 */
190
function writeInt_(bytes, number, j) {
191
  return gInt_.write(bytes, number, j);
192
}
193
194
/**
195
 * Write one 16-bit float as a binary value.
196
 * @param {!Uint8Array} bytes An array of bytes.
197
 * @param {number} number The number to write as bytes.
198
 * @param {number} j The index being written in the byte buffer.
199
 * @return {number} The next index to write on the byte buffer.
200
 * @private
201
 */
202
function write16F_(bytes, number, j) {
203
  f32_[0] = number;
204
  let x = ui32_[0];
205
  let bits = (x >> 16) & 0x8000;
206
  let m = (x >> 12) & 0x07ff;
207
  let e = (x >> 23) & 0xff;
208
  if (e >= 103) {
209
    bits |= ((e - 112) << 10) | (m >> 1);
210
    bits += m & 1;
211
  }
212
  bytes[j++] = bits & 0xFF;
213
  bytes[j++] = bits >>> 8 & 0xFF;
214
  return j;
215
}
216
217
/**
218
 * Write one 32-bit float as a binary value.
219
 * @param {!Uint8Array} bytes An array of bytes.
220
 * @param {number} number The number to write as bytes.
221
 * @param {number} j The index being written in the byte buffer.
222
 * @return {number} The next index to write on the byte buffer.
223
 * @private
224
 */
225
function write32F_(bytes, number, j) {
226
  f32_[0] = number;
227
  return gInt_.write(bytes, ui32_[0], j);
228
}
229
230
/**
231
 * Write one 64-bit float as a binary value.
232
 * @param {!Uint8Array} bytes An array of bytes.
233
 * @param {number} number The number to write as bytes.
234
 * @param {number} j The index being written in the byte buffer.
235
 * @return {number} The next index to write on the byte buffer.
236
 * @private
237
 */
238
function write64F_(bytes, number, j) {
239
  f64_[0] = number;
240
  if (HOST_BE_) {
241
    j = gInt_.write(bytes, ui32_[1], j);
242
    j = gInt_.write(bytes, ui32_[0], j);
243
  } else {
244
    j = gInt_.write(bytes, ui32_[0], j);
245
    j = gInt_.write(bytes, ui32_[1], j);
246
  }
247
  return j;
248
}
249
250
/**
251
 * Set the function to unpack the data.
252
 * @param {!Object} theType The type definition.
253
 * @private
254
 */
255
function setReader(theType) {
256
  if (theType.float) {
257
    if (theType.bits == 16) {
258
      reader_ = read16F_;
259
    } else if(theType.bits == 32) {
260
      reader_ = read32F_;
261
    } else if(theType.bits == 64) {
262
      reader_ = read64F_;
263
    }
264
  } else {
265
    reader_ = readInt_;
266
  }
267
}
268
269
/**
270
 * Set the function to pack the data.
271
 * @param {!Object} theType The type definition.
272
 * @private
273
 */
274
function setWriter(theType) {
275
  if (theType.float) {
276
    if (theType.bits == 16) {
277
      writer_ = write16F_;
278
    } else if(theType.bits == 32) {
279
      writer_ = write32F_;
280
    } else if(theType.bits == 64) {
281
      writer_ = write64F_;
282
    }
283
  } else {
284
    writer_ = writeInt_;
285
  }   
286
}
287
288
export {reader_};
289