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
Unused Code
introduced
by
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 |