Completed
Push — master ( 3333dc...570e0f )
by Rafael S.
02:02
created

lib/packer.js (2 issues)

Severity
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, validateNotUndefined, validateValueType} from './validation.js';
0 ignored issues
show
The variable validateNotUndefined seems to be never used. Consider removing it.
Loading history...
The variable validateValueType seems to be never used. Consider removing it.
Loading history...
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
const BE_ENV = new Uint8Array(new Uint32Array([1]).buffer)[0] === 0;
41
/**
42
 * @type {number}
43
 * @private
44
 */
45
const HIGH = BE_ENV ? 1 : 0;
46
/**
47
 * @type {number}
48
 * @private
49
 */
50
const LOW = BE_ENV ? 0 : 1;
51
/**
52
 * @type {!Int8Array}
53
 * @private
54
 */
55
let int8_ = new Int8Array(8);
56
/**
57
 * @type {!Uint32Array}
58
 * @private
59
 */
60
let ui32_ = new Uint32Array(int8_.buffer);
61
/**
62
 * @type {!Float32Array}
63
 * @private
64
 */
65
let f32_ = new Float32Array(int8_.buffer);
66
/**
67
 * @type {!Float64Array}
68
 * @private
69
 */
70
let f64_ = new Float64Array(int8_.buffer);
71
/**
72
 * @type {Function}
73
 * @private
74
 */
75
let reader_;
76
/**
77
 * @type {Function}
78
 * @private
79
 */
80
let writer_;
81
/**
82
 * @type {Object}
83
 * @private
84
 */
85
let gInt_ = {};
86
87
/**
88
 * Validate the type and set up the packing/unpacking functions.
89
 * @param {!Object} theType The type definition.
90
 * @throws {Error} If the type definition is not valid.
91
 * @private
92
 */
93
function setUp_(theType) {
94
  validateType(theType);
95
  theType.offset = theType.bits < 8 ? 1 : Math.ceil(theType.bits / 8);
96
  setReader(theType);
97
  setWriter(theType);
98
  gInt_ = new Integer(
99
    theType.bits == 64 ? 32 : theType.bits,
100
    theType.float ? false : theType.signed);
101
}
102
103
/**
104
 * Read int values from bytes.
105
 * @param {!Uint8Array} bytes An array of bytes.
106
 * @param {number} i The index to read.
107
 * @return {number}
108
 * @private
109
 */
110
function readInt_(bytes, i) {
111
  return gInt_.read(bytes, i);
112
}
113
114
/**
115
 * Read 1 16-bit float from bytes.
116
 * Thanks https://stackoverflow.com/a/8796597
117
 * @param {!Uint8Array} bytes An array of bytes.
118
 * @param {number} i The index to read.
119
 * @return {number}
120
 * @private
121
 */
122
function read16F_(bytes, i) {
123
  /** @type {number} */
124
  let int = gInt_.read(bytes, i);
125
  /** @type {number} */
126
  let exponent = (int & 0x7C00) >> 10;
127
  /** @type {number} */
128
  let fraction = int & 0x03FF;
129
  /** @type {number} */
130
  let floatValue;
131
  if (exponent) {
132
    floatValue =  Math.pow(2, exponent - 15) * (1 + fraction / 0x400);
133
  } else {
134
    floatValue = 6.103515625e-5 * (fraction / 0x400);
135
  }
136
  return floatValue * (int >> 15 ? -1 : 1);
137
}
138
139
/**
140
 * Read 1 32-bit float from bytes.
141
 * @param {!Uint8Array} bytes An array of bytes.
142
 * @param {number} i The index to read.
143
 * @return {number}
144
 * @private
145
 */
146
function read32F_(bytes, i) {
147
  ui32_[0] = gInt_.read(bytes, i);
148
  return f32_[0];
149
}
150
151
/**
152
 * Read 1 64-bit float from bytes.
153
 * Thanks https://gist.github.com/kg/2192799
154
 * @param {!Uint8Array} bytes An array of bytes.
155
 * @param {number} i The index to read.
156
 * @return {number}
157
 * @private
158
 */
159
function read64F_(bytes, i) {
160
  ui32_[HIGH] = gInt_.read(bytes, i);
161
  ui32_[LOW] = gInt_.read(bytes, i + 4);
162
  return f64_[0];
163
}
164
165
/**
166
 * Write a integer value to a byte buffer.
167
 * @param {!Uint8Array} bytes An array of bytes.
168
 * @param {number} number The number to write as bytes.
169
 * @param {number} j The index being written in the byte buffer.
170
 * @return {!number} The next index to write on the byte buffer.
171
 * @private
172
 */
173
function writeInt_(bytes, number, j) {
174
  return gInt_.write(bytes, number, j);
175
}
176
177
/**
178
 * Write one 16-bit float as a binary value.
179
 * @param {!Uint8Array} bytes An array of bytes.
180
 * @param {number} number The number to write as bytes.
181
 * @param {number} j The index being written in the byte buffer.
182
 * @return {number} The next index to write on the byte buffer.
183
 * @private
184
 */
185
function write16F_(bytes, number, j) {
186
  f32_[0] = number;
187
  /** @type {number} */
188
  let x = ui32_[0];
189
  /** @type {number} */
190
  let bits = (x >> 16) & 0x8000;
191
  /** @type {number} */
192
  let m = (x >> 12) & 0x07ff;
193
  /** @type {number} */
194
  let e = (x >> 23) & 0xff;
195
  if (e >= 103) {
196
    bits |= ((e - 112) << 10) | (m >> 1);
197
    bits += m & 1;
198
  }
199
  bytes[j++] = bits & 0xFF;
200
  bytes[j++] = bits >>> 8 & 0xFF;
201
  return j;
202
}
203
204
/**
205
 * Write one 32-bit float as a binary value.
206
 * @param {!Uint8Array} bytes An array of bytes.
207
 * @param {number} number The number to write as bytes.
208
 * @param {number} j The index being written in the byte buffer.
209
 * @return {number} The next index to write on the byte buffer.
210
 * @private
211
 */
212
function write32F_(bytes, number, j) {
213
  f32_[0] = number;
214
  return gInt_.write(bytes, ui32_[0], j);
215
}
216
217
/**
218
 * Write one 64-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 write64F_(bytes, number, j) {
226
  f64_[0] = number;
227
  j = gInt_.write(bytes, ui32_[HIGH], j);
228
  return gInt_.write(bytes, ui32_[LOW], j);
229
}
230
231
/**
232
 * Set the function to unpack the data.
233
 * @param {!Object} theType The type definition.
234
 * @private
235
 */
236
function setReader(theType) {
237
  if (theType.float) {
238
    if (theType.bits == 16) {
239
      reader_ = read16F_;
240
    } else if(theType.bits == 32) {
241
      reader_ = read32F_;
242
    } else {
243
      reader_ = read64F_;
244
    }
245
  } else {
246
    reader_ = readInt_;
247
  }
248
}
249
250
/**
251
 * Set the function to pack the data.
252
 * @param {!Object} theType The type definition.
253
 * @private
254
 */
255
function setWriter(theType) {
256
  if (theType.float) {
257
    if (theType.bits == 16) {
258
      writer_ = write16F_;
259
    } else if(theType.bits == 32) {
260
      writer_ = write32F_;
261
    } else {
262
      writer_ = write64F_;
263
    }
264
  } else {
265
    writer_ = writeInt_;
266
  }   
267
}
268
269
export {reader_, writer_, setUp_};
270