Passed
Push — master ( 567ae9...db344a )
by Rafael S.
01:53
created

packer.js ➔ write16F_   B

Complexity

Conditions 5
Paths 23

Size

Total Lines 29
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 1 Features 0
Metric Value
cc 5
eloc 22
nc 23
nop 3
dl 0
loc 29
rs 8.8853
c 3
b 1
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 {validateType} from './validation.js';
32
import {unpackIEEE754} from './IEEE754.js';
33
34
/**
35
 * A class to pack and unpack integers and floating-point numbers.
36
 * @extends {Integer}
37
 */
38
export default class Packer extends Integer {
39
  
40
  constructor() {
41
    super();
42
    /**
43
     * Use a Typed Array to check if the host is BE or LE. This will impact
44
     * on how 64-bit floating point numbers are handled.
45
     * @type {boolean}
46
     * @private
47
     */
48
    const BE_ENV = new Uint8Array(new Uint32Array([1]).buffer)[0] === 0;
49
    /**
50
     * @type {number}
51
     * @private
52
     */
53
    this.HIGH = BE_ENV ? 1 : 0;
54
    /**
55
     * @type {number}
56
     * @private
57
     */
58
    this.LOW = BE_ENV ? 0 : 1;
59
    /**
60
     * @type {!Int8Array}
61
     * @private
62
     */
63
    let int8_ = new Int8Array(8);
64
    /**
65
     * @type {!Uint32Array}
66
     * @private
67
     */
68
    this.ui32_ = new Uint32Array(int8_.buffer);
69
    /**
70
     * @type {!Float32Array}
71
     * @private
72
     */
73
    this.f32_ = new Float32Array(int8_.buffer);
74
    /**
75
     * @type {!Float64Array}
76
     * @private
77
     */
78
    this.f64_ = new Float64Array(int8_.buffer);
79
    /**
80
     * @type {Object}
81
     * @private
82
     */
83
    this.gInt_ = {};
84
  }
85
86
  /**
87
   * Set up the object to start serializing/deserializing a data type..
88
   * @param {!Object} theType The type definition.
89
   * @throws {Error} If the type definition is not valid.
90
   */
91
  setUp(theType) {
92
    validateType(theType);
93
    super.setUp({
94
      bits: theType.bits,
95
      signed: theType.float ? false : theType.signed});
96
    this.setReaderAndWriter_(theType);
97
  }
98
99
  /**
100
   * Read 1 16-bit float from bytes.
101
   * @see https://stackoverflow.com/a/8796597
102
   * @param {!Uint8Array} bytes An array of bytes.
103
   * @param {number=} i The index to read.
104
   * @return {number}
105
   * @private
106
   */
107
  read16F_(bytes, i=0) {
108
    return unpackIEEE754(bytes, i, 2, 5, 15);
109
  }
110
111
  /**
112
   * Read 1 32-bit float from bytes.
113
   * @param {!Uint8Array} bytes An array of bytes.
114
   * @param {number=} i The index to read.
115
   * @return {number}
116
   * @private
117
   */
118
  read32F_(bytes, i=0) {
119
    return unpackIEEE754(bytes, i, 4, 8, 127);
120
  }
121
122
  /**
123
   * Read 1 64-bit float from bytes.
124
   * Thanks https://gist.github.com/kg/2192799
125
   * @param {!Uint8Array} bytes An array of bytes.
126
   * @param {number=} i The index to read.
127
   * @return {number}
128
   * @private
129
   */
130
  read64F_(bytes, i=0) {
131
    return unpackIEEE754(bytes, i, 8, 11, 1023);
132
  }
133
134
  /**
135
   * Write one 16-bit float as a binary value.
136
   * @param {!Uint8Array} bytes An array of bytes.
137
   * @param {number} number The number to write as bytes.
138
   * @param {number=} j The index being written in the byte buffer.
139
   * @return {number} The next index to write on the byte buffer.
140
   * @private
141
   */
142
  write16F_(bytes, number, j=0) {
143
    this.f32_[0] = number;
144
    if (isNaN(number)) {
145
      bytes[j++] = 0;
146
      bytes[j++] = 126;
147
    } else if (number === Infinity) {
148
      bytes[j++] = 0;
149
      bytes[j++] = 124;
150
    } else if (number === -Infinity) {
151
      bytes[j++] = 0;
152
      bytes[j++] = 252;
153
    } else {
154
      /** @type {number} */
155
      let x = this.ui32_[0];
156
      /** @type {number} */
157
      let bits = (x >> 16) & 0x8000;
158
      /** @type {number} */
159
      let m = (x >> 12) & 0x07ff;
160
      /** @type {number} */
161
      let e = (x >> 23) & 0xff;
162
      if (e >= 103) {
163
        bits |= ((e - 112) << 10) | (m >> 1);
164
        bits += m & 1;
165
      }
166
      bytes[j++] = bits & 0xFF;
167
      bytes[j++] = bits >>> 8 & 0xFF;
168
    }
169
    return j;
170
  }
171
172
  /**
173
   * Write one 32-bit float as a binary value.
174
   * @param {!Uint8Array} bytes An array of bytes.
175
   * @param {number} number The number to write as bytes.
176
   * @param {number=} j The index being written in the byte buffer.
177
   * @return {number} The next index to write on the byte buffer.
178
   * @private
179
   */
180
  write32F_(bytes, number, j=0) {
181
    this.f32_[0] = number;
182
    return super.write(bytes, this.ui32_[0], j);
183
  }
184
185
  /**
186
   * Write one 64-bit float as a binary value.
187
   * @param {!Uint8Array} bytes An array of bytes.
188
   * @param {number} number The number to write as bytes.
189
   * @param {number=} j The index being written in the byte buffer.
190
   * @return {number} The next index to write on the byte buffer.
191
   * @private
192
   */
193
  write64F_(bytes, number, j=0) {
194
    this.f64_[0] = number;
195
    j = super.write(bytes, this.ui32_[this.HIGH], j);
196
    return super.write(bytes, this.ui32_[this.LOW], j);
197
  }
198
199
  /**
200
   * Set the functions to pack and unpack numbers.
201
   * @param {!Object} theType The type definition.
202
   * @private
203
   */
204
  setReaderAndWriter_(theType) {
205
    if (theType.float) {
206
      if (theType.bits == 16) {
207
        this.read = this.read16F_;
208
        this.write = this.write16F_;
209
      } else if(theType.bits == 32) {
210
        this.read = this.read32F_;
211
        this.write = this.write32F_;
212
      } else {
213
        this.read = this.read64F_;
214
        this.write = this.write64F_;
215
      }
216
    } else {
217
      this.read = super.read;
218
      this.write = super.write;
219
    }
220
  }
221
}
222