Passed
Push — master ( 39dea5...dace4e )
by Rafael S.
01:52
created

main.js (1 issue)

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 The byte-data API.
27
 * @see https://github.com/rochars/byte-data
28
 */
29
30
/** @module byteData */
31
32
import endianness from './lib/endianness.js';
33
import {reader_, setUp_, writer_} from './lib/packer.js';
34
import {validateNotUndefined, validateValueType} from './lib/validation.js';
35
36
const UTF8_ERROR = 'Invalid UTF-8 character.';
0 ignored issues
show
The constant UTF8_ERROR seems to be never used. Consider removing it.
Loading history...
37
38
/**
39
 * Read a string of UTF-8 characters from a byte buffer.
40
 * @see https://encoding.spec.whatwg.org/#the-encoding
41
 * @see https://stackoverflow.com/a/34926911
42
 * @param {!Uint8Array|!Array<!number>} buffer A byte buffer.
43
 * @param {number=} index The index to read.
44
 * @param {?number=} len The number of bytes to read.
45
 * @return {string}
46
 */
47
export function unpackString(buffer, index=0, len=null) {
48
  len = len !== null ? index + len : buffer.length;
49
  /** @type {string} */
50
  let str = "";
51
  while(index < len) {
52
    /** @type {number} */
53
    let lowerBoundary = 0x80;
54
    /** @type {number} */
55
    let upperBoundary = 0xBF;
56
    /** @type {boolean} */
57
    let replace = false;
58
    /** @type {number} */
59
    let charCode = buffer[index++];
60
    if (charCode >= 0x00 && charCode <= 0x7F) {
61
      str += String.fromCharCode(charCode);
62
    } else {
63
      /** @type {number} */
64
      let count = 0;
65
      if (charCode >= 0xC2 && charCode <= 0xDF) {
66
        count = 1;
67
      } else if (charCode >= 0xE0 && charCode <= 0xEF ) {
68
        count = 2;
69
        if (buffer[index] === 0xE0) {
70
          lowerBoundary = 0xA0;
71
        }
72
        if (buffer[index] === 0xED) {
73
          upperBoundary = 0x9F;
74
        }
75
      } else if (charCode >= 0xF0 && charCode <= 0xF4 ) {
76
        count = 3;
77
        if (buffer[index] === 0xF0) {
78
          lowerBoundary = 0x90;
79
        }
80
        if (buffer[index] === 0xF4) {
81
          upperBoundary = 0x8F;
82
        }
83
      } else {
84
        replace = true;
85
      }
86
      charCode = charCode & (1 << (8 - count - 1)) - 1;
87
      for (let i = 0; i < count; i++) {
88
        if (buffer[index] < lowerBoundary || buffer[index] > upperBoundary) {
89
          replace = true;
90
        }
91
        charCode = (charCode << 6) | (buffer[index] & 0x3f);
92
        index++;
93
      }
94
      if (replace) {
95
        str += String.fromCharCode(0xFFFD);
96
      } 
97
      else if (charCode <= 0xffff) {
98
        str += String.fromCharCode(charCode);
99
      } else {
100
        charCode -= 0x10000;
101
        str += String.fromCharCode(
102
          ((charCode >> 10) & 0x3ff) + 0xd800,
103
          (charCode & 0x3ff) + 0xdc00);
104
      }
105
    }
106
  }
107
  return str;
108
}
109
110
/**
111
 * Write a string of UTF-8 characters as a byte buffer.
112
 * @see https://encoding.spec.whatwg.org/#utf-8-encoder
113
 * @param {string} str The string to pack.
114
 * @return {!Array<number>} The packed string.
115
 */
116
export function packString(str) {
117
  /** @type {!Array<!number>} */
118
  let bytes = [];
119
  for (let i = 0; i < str.length; i++) {
120
    /** @type {number} */
121
    let codePoint = str.codePointAt(i);
122
    if (codePoint < 128) {
123
      bytes.push(codePoint);
124
    } else {
125
      /** @type {number} */
126
      let count = 0;
127
      /** @type {number} */
128
      let offset = 0;
129
      if (codePoint <= 0x07FF) {
130
        count = 1;
131
        offset = 0xC0;
132
      } else if(codePoint <= 0xFFFF) {
133
        count = 2;
134
        offset = 0xE0;
135
      } else if(codePoint <= 0x10FFFF) {
136
        count = 3;
137
        offset = 0xF0;
138
        i++;
139
      }
140
      bytes.push((codePoint >> (6 * count)) + offset);
141
      while (count > 0) {
142
        bytes.push(0x80 | (codePoint >> (6 * (count - 1)) & 0x3F));
143
        count--;
144
      }
145
    }
146
  }
147
  return bytes;
148
}
149
150
/**
151
 * Write a string of UTF-8 characters to a byte buffer.
152
 * @param {string} str The string to pack.
153
 * @param {!Uint8Array|!Array<number>} buffer The output buffer.
154
 * @param {number=} index The index to write in the buffer.
155
 * @return {number} The next index to write in the buffer.
156
 */
157
export function packStringTo(str, buffer, index=0) {
158
  /** @type {!Array<!number>} */
159
  let bytes = packString(str);
160
  for (let i = 0; i < bytes.length; i++) {
161
    buffer[index++] = bytes[i];
162
  }
163
  return index;
164
}
165
166
// Numbers
167
/**
168
 * Pack a number as a byte buffer.
169
 * @param {number} value The number.
170
 * @param {!Object} theType The type definition.
171
 * @return {!Array<number>} The packed value.
172
 * @throws {Error} If the type definition is not valid.
173
 * @throws {Error} If the value is not valid.
174
 */
175
export function pack(value, theType) {
176
  /** @type {!Array<!number>} */
177
  let output = [];
178
  packTo(value, theType, output);
179
  return output;
180
}
181
182
/**
183
 * Pack a number to a byte buffer.
184
 * @param {number} value The value.
185
 * @param {!Object} theType The type definition.
186
 * @param {!Uint8Array|!Array<number>} buffer The output buffer.
187
 * @param {number=} index The index to write.
188
 * @return {number} The next index to write.
189
 * @throws {Error} If the type definition is not valid.
190
 * @throws {Error} If the value is not valid.
191
 */
192
export function packTo(value, theType, buffer, index=0) {
193
  return packArrayTo([value], theType, buffer, index);
194
}
195
196
/**
197
 * Pack an array of numbers as a byte buffer.
198
 * @param {!Array<number>|!TypedArray} values The values.
199
 * @param {!Object} theType The type definition.
200
 * @return {!Array<number>} The packed values.
201
 * @throws {Error} If the type definition is not valid.
202
 * @throws {Error} If any of the values are not valid.
203
 */
204
export function packArray(values, theType) {
205
  /** @type {!Array<!number>} */
206
  let output = [];
207
  packArrayTo(values, theType, output);
208
  return output;
209
}
210
211
/**
212
 * Pack a array of numbers to a byte buffer.
213
 * @param {!Array<number>|!TypedArray} values The value.
214
 * @param {!Object} theType The type definition.
215
 * @param {!Uint8Array|!Array<number>} buffer The output buffer.
216
 * @param {number=} index The buffer index to write.
217
 * @return {number} The next index to write.
218
 * @throws {Error} If the type definition is not valid.
219
 * @throws {Error} If the value is not valid.
220
 */
221
export function packArrayTo(values, theType, buffer, index=0) {
222
  setUp_(theType);
223
  for (let i=0; i < values.length; i++) {
224
    validateNotUndefined(values[i]);
225
    validateValueType(values[i]);
226
    /** @type {number} */
227
    let len = index + theType.offset;
228
    while (index < len) {
229
      index = writer_(buffer, values[i], index);
230
    }
231
    if (theType.be) {
232
      endianness(
233
        buffer, theType.offset, index - theType.offset, index);
234
    }
235
  }
236
  return index;
237
}
238
239
/**
240
 * Unpack a number from a byte buffer.
241
 * @param {!Uint8Array|!Array<!number>} buffer The byte buffer.
242
 * @param {!Object} theType The type definition.
243
 * @param {number=} index The buffer index to read.
244
 * @return {number}
245
 * @throws {Error} If the type definition is not valid
246
 */
247
export function unpack(buffer, theType, index=0) {
248
  setUp_(theType);
249
  if ((theType.offset + index) > buffer.length) {
250
    throw Error('Bad buffer length.');
251
  }
252
  if (theType.be) {
253
    endianness(buffer, theType.offset, index, index + theType.offset);
254
  }
255
  /** @type {number} */
256
  let value = reader_(buffer, index);
257
  if (theType.be) {
258
    endianness(buffer, theType.offset, index, index + theType.offset);
259
  }
260
  return value;
261
}
262
263
/**
264
 * Unpack an array of numbers from a byte buffer.
265
 * @param {!Uint8Array|!Array<!number>} buffer The byte buffer.
266
 * @param {!Object} theType The type definition.
267
 * @param {number=} index The start index. Assumes 0.
268
 * @param {?number=} end The end index. Assumes the buffer length.
269
 * @return {!Array<number>}
270
 * @throws {Error} If the type definition is not valid
271
 */
272
export function unpackArray(buffer, theType, index=0, end=buffer.length) {
273
  /** @type {!Array<!number>} */
274
  let output = [];
275
  unpackArrayTo(buffer, theType, output, index, end);
276
  return output;
277
}
278
279
/**
280
 * Unpack a array of numbers to a typed array.
281
 * @param {!Uint8Array|!Array<!number>} buffer The byte buffer.
282
 * @param {!Object} theType The type definition.
283
 * @param {!TypedArray|!Array<!number>} output The output array.
284
 * @param {number=} index The start index. Assumes 0.
285
 * @param {?number=} end The end index. Assumes the buffer length.
286
 * @throws {Error} If the type definition is not valid
287
 */
288
export function unpackArrayTo(buffer, theType, output, index=0, end=buffer.length) {
289
  setUp_(theType);
290
  while ((end - index) % theType.offset) {
291
      end--;
292
  }
293
  for (let i = 0; index < end; index += theType.offset, i++) {
294
    output[i] = unpack(buffer, theType, index);
295
  }
296
}
297