Completed
Push — master ( 262243...61d2d3 )
by Rafael S.
01:52
created

main.js (2 issues)

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_, writeBytes_} from './lib/packer.js';
34
35
/**
36
 * Read a string of UTF-8 characters from a byte buffer.
37
 * @see https://encoding.spec.whatwg.org/#the-encoding
38
 * @see https://stackoverflow.com/a/34926911
39
 * @param {!Uint8Array|!Array<!number>} buffer A byte buffer.
40
 * @param {number=} index The index to read.
41
 * @param {?number=} len The number of bytes to read.
42
 * @return {string}
43
 * @throws {Error} If read a value that is not UTF-8.
44
 */
45
export function unpackString(buffer, index=0, len=null) {
46
  len = len !== null ? index + len : buffer.length;
47
  /** @type {string} */
48
  let str = "";
49
  while(index < len) {
50
    /** @type {number} */
51
    let charCode = buffer[index++];
52
    if (charCode >> 7 === 0) {
53
      str += String.fromCharCode(charCode);
54
    } else {
55
      /** @type {number} */
56
      let count = 0;
57
      if (charCode >> 5 === 0x06) {
58
        count = 1;
59
      } else if (charCode >> 4 === 0x0e) {
60
        count = 2;
61
      } else if (charCode >> 3 === 0x1e) {
62
        count = 3;
63
      }
64
      charCode = charCode & (1 << (8 - count - 1)) - 1;
65
      for (let i = 0; i < count; i++) {
66
        charCode = (charCode << 6) | (buffer[index++] & 0x3f);
67
      }
68
      if (charCode <= 0xffff) {
69
        str += String.fromCharCode(charCode);
70
      } else {
71
        charCode -= 0x10000;
72
        str += String.fromCharCode(
73
          ((charCode >> 10) & 0x3ff) + 0xd800,
74
          (charCode & 0x3ff) + 0xdc00);
75
      }
76
    }
77
  }
78
  return str;
79
}
80
81
/**
82
 * Write a string of UTF-8 characters as a byte buffer.
83
 * @see https://encoding.spec.whatwg.org/#utf-8-encoder
84
 * @param {string} str The string to pack.
85
 * @return {!Array<number>} The next index to write on the buffer.
86
 * @throws {Error} If a character in the string is not UTF-8.
87
 */
88
export function packString(str) {
89
  /** @type {!Array<!number>} */
90
  let bytes = [];
91
  for (let i = 0; i < str.length; i++) {
92
    /** @type {number} */
93
    let codePoint = str.codePointAt(i);
94
    if (codePoint < 128) {
95
      bytes.push(codePoint);
96
    } else {
97
      /** @type {number} */
98
      let count = 0;
99
      /** @type {number} */
100
      let offset = 0;
101
      if (codePoint <= 0x07FF) {
102
        count = 1;
103
        offset = 0xC0;
104
      } else if(codePoint <= 0xFFFF) {
105
        count = 2;
106
        offset = 0xE0;
107
      } else if(codePoint <= 0x10FFFF) {
108
        count = 3;
109
        offset = 0xF0;
110
        i++;
0 ignored issues
show
Complexity Coding Style introduced by
You seem to be assigning a new value to the loop variable i here. Please check if this was indeed your intention. Even if it was, consider using another kind of loop instead.
Loading history...
111
      }
112
      bytes.push((codePoint >> (6 * count)) + offset);
113
      while (count > 0) {
114
        bytes.push(0x80 | (codePoint >> (6 * (count - 1)) & 0x3F));
115
        count--;
116
      }
117
    }
118
  }
119
  return bytes;
120
}
121
122
/**
123
 * Write a string of UTF-8 characters to a byte buffer.
124
 * @param {string} str The string to pack.
125
 * @param {!Uint8Array|!Array<number>} buffer The output buffer.
126
 * @param {number=} index The index to write in the buffer.
127
 * @return {number} The next index to write in the buffer.
128
 * @throws {Error} If a character in the string is not valid ASCII.
129
 */
130
export function packStringTo(str, buffer, index=0) {
131
  /** @type {!Array<!number>} */
132
  let bytes = packString(str);
133
  for (let i = 0; i < bytes.length; i++) {
134
    buffer[index++] = bytes[i];
135
  }
136
  return index;
137
}
138
139
// Numbers
140
/**
141
 * Pack a number as a byte buffer.
142
 * @param {number} value The number.
143
 * @param {!Object} theType The type definition.
144
 * @return {!Array<number>} The packed value.
145
 * @throws {Error} If the type definition is not valid.
146
 * @throws {Error} If the value is not valid.
147
 */
148
export function pack(value, theType) {
149
  /** @type {!Array<!number>} */
150
  let output = [];
151
  packTo(value, theType, output);
152
  return output;
153
}
154
155
/**
156
 * Pack an array of numbers as a byte buffer.
157
 * @param {!Array<number>|!TypedArray} values The values.
158
 * @param {!Object} theType The type definition.
159
 * @return {!Array<number>} The packed values.
160
 * @throws {Error} If the type definition is not valid.
161
 * @throws {Error} If any of the values are not valid.
162
 */
163
export function packArray(values, theType) {
164
  /** @type {!Array<!number>} */
165
  let output = [];
166
  packArrayTo(values, theType, output);
167
  return output;
168
}
169
170
/**
171
 * Pack a number to a byte buffer.
172
 * @param {number} value The value.
173
 * @param {!Object} theType The type definition.
174
 * @param {!Uint8Array|!Array<number>} buffer The output buffer.
175
 * @param {number=} index The index to write.
176
 * @return {number} The next index to write.
177
 * @throws {Error} If the type definition is not valid.
178
 * @throws {Error} If the value is not valid.
179
 */
180
export function packTo(value, theType, buffer, index=0) {
181
  setUp_(theType);
182
  return writeBytes_(value,
183
    theType,
184
    buffer,
185
    index,
186
    index + theType.offset);
187
}
188
189
/**
190
 * Pack a array of numbers to a byte buffer.
191
 * @param {!Array<number>|!TypedArray} values The value.
192
 * @param {!Object} theType The type definition.
193
 * @param {!Uint8Array|!Array<number>} buffer The output buffer.
194
 * @param {number=} index The buffer index to write.
195
 * @return {number} The next index to write.
196
 * @throws {Error} If the type definition is not valid.
197
 * @throws {Error} If the value is not valid.
198
 */
199
export function packArrayTo(values, theType, buffer, index=0) {
200
  setUp_(theType);
201
  for (let i=0; i < values.length; i++) {
202
    index = writeBytes_(
203
      values[i],
204
      theType,
205
      buffer,
206
      index,
207
      index + theType.offset);
208
  }
209
  return index;
210
}
211
212
/**
213
 * Unpack a number from a byte buffer.
214
 * @param {!Uint8Array|!Array<!number>} buffer The byte buffer.
215
 * @param {!Object} theType The type definition.
216
 * @param {number=} index The buffer index to read.
217
 * @return {number|undefined}
218
 * @throws {Error} If the type definition is not valid
219
 */
220
export function unpack(buffer, theType, index=0) {
221
  setUp_(theType);
222
  if ((theType.offset + index) > buffer.length) {
223
    throw Error('Bad buffer length.');
224
  }
225
  if (theType.be) {
226
    endianness(buffer, theType.offset, index, index + theType.offset);
227
  }
228
  /** @type {number} */
229
  let value = reader_(buffer, index);
230
  if (theType.be) {
231
    endianness(buffer, theType.offset, index, index + theType.offset);
232
  }
233
  return value;
234
}
235
236
/**
237
 * Unpack an array of numbers from a byte buffer.
238
 * @param {!Uint8Array|!Array<!number>} buffer The byte buffer.
239
 * @param {!Object} theType The type definition.
240
 * @param {number=} index The start index. Assumes 0.
241
 * @param {?number=} end The end index. Assumes the buffer length.
242
 * @return {!Array<number>}
243
 * @throws {Error} If the type definition is not valid
244
 */
245
export function unpackArray(buffer, theType, index=0, end=buffer.length) {
246
  /** @type {!Array<!number>} */
247
  let output = [];
248
  unpackArrayTo(buffer, theType, output, index, end);
249
  return output;
250
}
251
252
/**
253
 * Unpack a array of numbers to a typed array.
254
 * @param {!Uint8Array|!Array<!number>} buffer The byte buffer.
255
 * @param {!Object} theType The type definition.
256
 * @param {!TypedArray|!Array<!number>} output The output array.
257
 * @param {number=} index The start index. Assumes 0.
258
 * @param {?number=} end The end index. Assumes the buffer length.
259
 * @throws {Error} If the type definition is not valid
260
 */
261
export function unpackArrayTo(buffer, theType, output, index=0, end=buffer.length) {
262
  while ((end - index) % theType.offset) {
263
      end--;
264
  }
265
  for (let i = 0; index < end; index += theType.offset, i++) {
0 ignored issues
show
The loop variable i is initialized by the loop but not used in the test. Consider using another type of loop if this is the intended behavior.
Loading history...
266
    //if ((theType.offset + index - 1) < buffer.length) {
267
    output[i] = unpack(buffer, theType, index);
268
    //}
269
  }
270
}
271