UNPKG

24.1 kBJavaScriptView Raw
1/*
2Copyright (c) 2013 Kevin van Zonneveld (http://kvz.io)
3and Contributors (http://phpjs.org/authors)
4
5Permission is hereby granted, free of charge, to any person obtaining a copy of
6this software and associated documentation files (the "Software"), to deal in
7the Software without restriction, including without limitation the rights to
8use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9of the Software, and to permit persons to whom the Software is furnished to do
10so, subject to the following conditions:
11
12The above copyright notice and this permission notice shall be included in all
13copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21SOFTWARE.
22*/
23function pack(format) {
24 // discuss at: http://phpjs.org/functions/pack/
25 // original by: Tim de Koning (http://www.kingsquare.nl)
26 // parts by: Jonas Raoni Soares Silva (http://www.jsfromhell.com)
27 // bugfixed by: Tim de Koning (http://www.kingsquare.nl)
28 // note: Float encoding by: Jonas Raoni Soares Silva
29 // note: Home: http://www.kingsquare.nl/blog/12-12-2009/13507444
30 // note: Feedback: phpjs-pack@kingsquare.nl
31 // note: 'machine dependent byte order and size' aren't
32 // note: applicable for JavaScript; pack works as on a 32bit,
33 // note: little endian machine
34 // example 1: pack('nvc*', 0x1234, 0x5678, 65, 66);
35 // returns 1: '4xVAB'
36
37 var formatPointer = 0,
38 argumentPointer = 1,
39 result = '',
40 argument = '',
41 i = 0,
42 r = [],
43 instruction, quantifier, word, precisionBits, exponentBits, extraNullCount;
44
45 // vars used by float encoding
46 var bias, minExp, maxExp, minUnnormExp, status, exp, len, bin, signal, n, intPart, floatPart, lastBit, rounded, j,
47 k, tmpResult;
48
49 while (formatPointer < format.length) {
50 instruction = format.charAt(formatPointer);
51 quantifier = '';
52 formatPointer++;
53 while ((formatPointer < format.length) && (format.charAt(formatPointer)
54 .match(/[\d\*]/) !== null)) {
55 quantifier += format.charAt(formatPointer);
56 formatPointer++;
57 }
58 if (quantifier === '') {
59 quantifier = '1';
60 }
61
62 // Now pack variables: 'quantifier' times 'instruction'
63 switch (instruction) {
64 case 'a':
65 // NUL-padded string
66 case 'A':
67 // SPACE-padded string
68 if (typeof arguments[argumentPointer] === 'undefined') {
69 throw new Error('Warning: pack() Type ' + instruction + ': not enough arguments');
70 } else {
71 argument = String(arguments[argumentPointer]);
72 }
73 if (quantifier === '*') {
74 quantifier = argument.length;
75 }
76 for (i = 0; i < quantifier; i++) {
77 if (typeof argument[i] === 'undefined') {
78 if (instruction === 'a') {
79 result += String.fromCharCode(0);
80 } else {
81 result += ' ';
82 }
83 } else {
84 result += argument[i];
85 }
86 }
87 argumentPointer++;
88 break;
89 case 'h':
90 // Hex string, low nibble first
91 case 'H':
92 // Hex string, high nibble first
93 if (typeof arguments[argumentPointer] === 'undefined') {
94 throw new Error('Warning: pack() Type ' + instruction + ': not enough arguments');
95 } else {
96 argument = arguments[argumentPointer];
97 }
98 if (quantifier === '*') {
99 quantifier = argument.length;
100 }
101 if (quantifier > argument.length) {
102 throw new Error('Warning: pack() Type ' + instruction + ': not enough characters in string');
103 }
104 for (i = 0; i < quantifier; i += 2) {
105 // Always get per 2 bytes...
106 word = argument[i];
107 if (((i + 1) >= quantifier) || typeof argument[i + 1] === 'undefined') {
108 word += '0';
109 } else {
110 word += argument[i + 1];
111 }
112 // The fastest way to reverse?
113 if (instruction === 'h') {
114 word = word[1] + word[0];
115 }
116 result += String.fromCharCode(parseInt(word, 16));
117 }
118 argumentPointer++;
119 break;
120
121 case 'c':
122 // signed char
123 case 'C':
124 // unsigned char
125 // c and C is the same in pack
126 if (quantifier === '*') {
127 quantifier = arguments.length - argumentPointer;
128 }
129 if (quantifier > (arguments.length - argumentPointer)) {
130 throw new Error('Warning: pack() Type ' + instruction + ': too few arguments');
131 }
132
133 for (i = 0; i < quantifier; i++) {
134 result += String.fromCharCode(arguments[argumentPointer]);
135 argumentPointer++;
136 }
137 break;
138
139 case 's':
140 // signed short (always 16 bit, machine byte order)
141 case 'S':
142 // unsigned short (always 16 bit, machine byte order)
143 case 'v':
144 // s and S is the same in pack
145 if (quantifier === '*') {
146 quantifier = arguments.length - argumentPointer;
147 }
148 if (quantifier > (arguments.length - argumentPointer)) {
149 throw new Error('Warning: pack() Type ' + instruction + ': too few arguments');
150 }
151
152 for (i = 0; i < quantifier; i++) {
153 result += String.fromCharCode(arguments[argumentPointer] & 0xFF);
154 result += String.fromCharCode(arguments[argumentPointer] >> 8 & 0xFF);
155 argumentPointer++;
156 }
157 break;
158
159 case 'n':
160 // unsigned short (always 16 bit, big endian byte order)
161 if (quantifier === '*') {
162 quantifier = arguments.length - argumentPointer;
163 }
164 if (quantifier > (arguments.length - argumentPointer)) {
165 throw new Error('Warning: pack() Type ' + instruction + ': too few arguments');
166 }
167
168 for (i = 0; i < quantifier; i++) {
169 result += String.fromCharCode(arguments[argumentPointer] >> 8 & 0xFF);
170 result += String.fromCharCode(arguments[argumentPointer] & 0xFF);
171 argumentPointer++;
172 }
173 break;
174
175 case 'i':
176 // signed integer (machine dependent size and byte order)
177 case 'I':
178 // unsigned integer (machine dependent size and byte order)
179 case 'l':
180 // signed long (always 32 bit, machine byte order)
181 case 'L':
182 // unsigned long (always 32 bit, machine byte order)
183 case 'V':
184 // unsigned long (always 32 bit, little endian byte order)
185 if (quantifier === '*') {
186 quantifier = arguments.length - argumentPointer;
187 }
188 if (quantifier > (arguments.length - argumentPointer)) {
189 throw new Error('Warning: pack() Type ' + instruction + ': too few arguments');
190 }
191
192 for (i = 0; i < quantifier; i++) {
193 result += String.fromCharCode(arguments[argumentPointer] & 0xFF);
194 result += String.fromCharCode(arguments[argumentPointer] >> 8 & 0xFF);
195 result += String.fromCharCode(arguments[argumentPointer] >> 16 & 0xFF);
196 result += String.fromCharCode(arguments[argumentPointer] >> 24 & 0xFF);
197 argumentPointer++;
198 }
199
200 break;
201 case 'N':
202 // unsigned long (always 32 bit, big endian byte order)
203 if (quantifier === '*') {
204 quantifier = arguments.length - argumentPointer;
205 }
206 if (quantifier > (arguments.length - argumentPointer)) {
207 throw new Error('Warning: pack() Type ' + instruction + ': too few arguments');
208 }
209
210 for (i = 0; i < quantifier; i++) {
211 result += String.fromCharCode(arguments[argumentPointer] >> 24 & 0xFF);
212 result += String.fromCharCode(arguments[argumentPointer] >> 16 & 0xFF);
213 result += String.fromCharCode(arguments[argumentPointer] >> 8 & 0xFF);
214 result += String.fromCharCode(arguments[argumentPointer] & 0xFF);
215 argumentPointer++;
216 }
217 break;
218
219 case 'f':
220 // float (machine dependent size and representation)
221 case 'd':
222 // double (machine dependent size and representation)
223 // version original by IEEE754
224 precisionBits = 23;
225 exponentBits = 8;
226 if (instruction === 'd') {
227 precisionBits = 52;
228 exponentBits = 11;
229 }
230
231 if (quantifier === '*') {
232 quantifier = arguments.length - argumentPointer;
233 }
234 if (quantifier > (arguments.length - argumentPointer)) {
235 throw new Error('Warning: pack() Type ' + instruction + ': too few arguments');
236 }
237 for (i = 0; i < quantifier; i++) {
238 argument = arguments[argumentPointer];
239 bias = Math.pow(2, exponentBits - 1) - 1;
240 minExp = -bias + 1;
241 maxExp = bias;
242 minUnnormExp = minExp - precisionBits;
243 status = isNaN(n = parseFloat(argument)) || n === -Infinity || n === +Infinity ? n : 0;
244 exp = 0;
245 len = 2 * bias + 1 + precisionBits + 3;
246 bin = new Array(len);
247 signal = (n = status !== 0 ? 0 : n) < 0;
248 n = Math.abs(n);
249 intPart = Math.floor(n);
250 floatPart = n - intPart;
251
252 for (k = len; k;) {
253 bin[--k] = 0;
254 }
255 for (k = bias + 2; intPart && k;) {
256 bin[--k] = intPart % 2;
257 intPart = Math.floor(intPart / 2);
258 }
259 for (k = bias + 1; floatPart > 0 && k; --floatPart) {
260 (bin[++k] = ((floatPart *= 2) >= 1) - 0);
261 }
262 for (k = -1; ++k < len && !bin[k];) {}
263
264 if (bin[(lastBit = precisionBits - 1 + (k = (exp = bias + 1 - k) >= minExp && exp <= maxExp ? k + 1 :
265 bias + 1 - (exp = minExp - 1))) + 1]) {
266 if (!(rounded = bin[lastBit])) {
267 for (j = lastBit + 2; !rounded && j < len; rounded = bin[j++]) {}
268 }
269 for (j = lastBit + 1; rounded && --j >= 0;
270 (bin[j] = !bin[j] - 0) && (rounded = 0)) {}
271 }
272
273 for (k = k - 2 < 0 ? -1 : k - 3; ++k < len && !bin[k];) {}
274
275 if ((exp = bias + 1 - k) >= minExp && exp <= maxExp) {
276 ++k;
277 } else {
278 if (exp < minExp) {
279 if (exp !== bias + 1 - len && exp < minUnnormExp) { /*"encodeFloat::float underflow" */ }
280 k = bias + 1 - (exp = minExp - 1);
281 }
282 }
283
284 if (intPart || status !== 0) {
285 exp = maxExp + 1;
286 k = bias + 2;
287 if (status === -Infinity) {
288 signal = 1;
289 } else if (isNaN(status)) {
290 bin[k] = 1;
291 }
292 }
293
294 n = Math.abs(exp + bias);
295 tmpResult = '';
296
297 for (j = exponentBits + 1; --j;) {
298 tmpResult = (n % 2) + tmpResult;
299 n = n >>= 1;
300 }
301
302 n = 0;
303 j = 0;
304 k = (tmpResult = (signal ? '1' : '0') + tmpResult + bin.slice(k, k + precisionBits)
305 .join(''))
306 .length;
307 r = [];
308
309 for (; k;) {
310 n += (1 << j) * tmpResult.charAt(--k);
311 if (j === 7) {
312 r[r.length] = String.fromCharCode(n);
313 n = 0;
314 }
315 j = (j + 1) % 8;
316 }
317
318 r[r.length] = n ? String.fromCharCode(n) : '';
319 result += r.join('');
320 argumentPointer++;
321 }
322 break;
323
324 case 'x':
325 // NUL byte
326 if (quantifier === '*') {
327 throw new Error('Warning: pack(): Type x: \'*\' ignored');
328 }
329 for (i = 0; i < quantifier; i++) {
330 result += String.fromCharCode(0);
331 }
332 break;
333
334 case 'X':
335 // Back up one byte
336 if (quantifier === '*') {
337 throw new Error('Warning: pack(): Type X: \'*\' ignored');
338 }
339 for (i = 0; i < quantifier; i++) {
340 if (result.length === 0) {
341 throw new Error('Warning: pack(): Type X:' + ' outside of string');
342 } else {
343 result = result.substring(0, result.length - 1);
344 }
345 }
346 break;
347
348 case '@':
349 // NUL-fill to absolute position
350 if (quantifier === '*') {
351 throw new Error('Warning: pack(): Type X: \'*\' ignored');
352 }
353 if (quantifier > result.length) {
354 extraNullCount = quantifier - result.length;
355 for (i = 0; i < extraNullCount; i++) {
356 result += String.fromCharCode(0);
357 }
358 }
359 if (quantifier < result.length) {
360 result = result.substring(0, quantifier);
361 }
362 break;
363
364 default:
365 throw new Error('Warning: pack() Type ' + instruction + ': unknown format code');
366 }
367 }
368 if (argumentPointer < arguments.length) {
369 throw new Error('Warning: pack(): ' + (arguments.length - argumentPointer) + ' arguments unused');
370 }
371
372 return result;
373}
374
375function unpack(format, data) {
376 // http://kevin.vanzonneveld.net
377 // + original by: Tim de Koning (http://www.kingsquare.nl)
378 // + parts by: Jonas Raoni Soares Silva - http://www.jsfromhell.com
379 // + parts by: Joshua Bell - http://cautionsingularityahead.blogspot.nl/
380 // +
381 // + bugfixed by: marcuswestin
382 // % note 1: Float decoding by: Jonas Raoni Soares Silva
383 // % note 2: Home: http://www.kingsquare.nl/blog/22-12-2009/13650536
384 // % note 3: Feedback: phpjs-unpack@kingsquare.nl
385 // % note 4: 'machine dependant byte order and size' aren't
386 // % note 5: applicable for JavaScript unpack works as on a 32bit,
387 // % note 6: little endian machine
388 // * example 1: unpack('d', "\u0000\u0000\u0000\u0000\u00008YÀ");
389 // * returns 1: { "": -100.875 }
390
391 var formatPointer = 0, dataPointer = 0, result = {}, instruction = '',
392 quantifier = '', label = '', currentData = '', i = 0, j = 0,
393 word = '', fbits = 0, ebits = 0, dataByteLength = 0;
394
395 // Used by float decoding - by Joshua Bell
396 //http://cautionsingularityahead.blogspot.nl/2010/04/javascript-and-ieee754-redux.html
397 var fromIEEE754 = function(bytes, ebits, fbits) {
398 // Bytes to bits
399 var bits = [];
400 for (var i = bytes.length; i; i -= 1) {
401 var byte = bytes[i - 1];
402 for (var j = 8; j; j -= 1) {
403 bits.push(byte % 2 ? 1 : 0); byte = byte >> 1;
404 }
405 }
406 bits.reverse();
407 var str = bits.join('');
408
409 // Unpack sign, exponent, fraction
410 var bias = (1 << (ebits - 1)) - 1;
411 var s = parseInt(str.substring(0, 1), 2) ? -1 : 1;
412 var e = parseInt(str.substring(1, 1 + ebits), 2);
413 var f = parseInt(str.substring(1 + ebits), 2);
414
415 // Produce number
416 if (e === (1 << ebits) - 1) {
417 return f !== 0 ? NaN : s * Infinity;
418 }
419 else if (e > 0) {
420 return s * Math.pow(2, e - bias) * (1 + f / Math.pow(2, fbits));
421 }
422 else if (f !== 0) {
423 return s * Math.pow(2, -(bias-1)) * (f / Math.pow(2, fbits));
424 }
425 else {
426 return s * 0;
427 }
428 }
429
430 while (formatPointer < format.length) {
431 instruction = format.charAt(formatPointer);
432
433 // Start reading 'quantifier'
434 quantifier = '';
435 formatPointer++;
436 while ((formatPointer < format.length) &&
437 (format.charAt(formatPointer).match(/[\d\*]/) !== null)) {
438 quantifier += format.charAt(formatPointer);
439 formatPointer++;
440 }
441 if (quantifier === '') {
442 quantifier = '1';
443 }
444
445
446 // Start reading label
447 label = '';
448 while ((formatPointer < format.length) &&
449 (format.charAt(formatPointer) !== '/')) {
450 label += format.charAt(formatPointer);
451 formatPointer++;
452 }
453 if (format.charAt(formatPointer) === '/') {
454 formatPointer++;
455 }
456
457 // Process given instruction
458 switch (instruction) {
459 case 'a': // NUL-padded string
460 case 'A': // SPACE-padded string
461 if (quantifier === '*') {
462 quantifier = data.length - dataPointer;
463 } else {
464 quantifier = parseInt(quantifier, 10);
465 }
466 currentData = data.substr(dataPointer, quantifier);
467 dataPointer += quantifier;
468
469 if (instruction === 'a') {
470 currentResult = currentData.replace(/\0+$/, '');
471 } else {
472 currentResult = currentData.replace(/ +$/, '');
473 }
474 result[label] = currentResult;
475 break;
476
477 case 'h': // Hex string, low nibble first
478 case 'H': // Hex string, high nibble first
479 if (quantifier === '*') {
480 quantifier = data.length - dataPointer;
481 } else {
482 quantifier = parseInt(quantifier, 10);
483 }
484 currentData = data.substr(dataPointer, quantifier);
485 dataPointer += quantifier;
486
487 if (quantifier > currentData.length) {
488 throw new Error('Warning: unpack(): Type ' + instruction +
489 ': not enough input, need ' + quantifier);
490 }
491
492 currentResult = '';
493 for (i = 0; i < currentData.length; i++) {
494 word = currentData.charCodeAt(i).toString(16);
495 if (instruction === 'h') {
496 word = word[1] + word[0];
497 }
498 currentResult += word;
499 }
500 result[label] = currentResult;
501 break;
502
503 case 'c': // signed char
504 case 'C': // unsigned c
505 if (quantifier === '*') {
506 quantifier = data.length - dataPointer;
507 } else {
508 quantifier = parseInt(quantifier, 10);
509 }
510
511 currentData = data.substr(dataPointer, quantifier);
512 dataPointer += quantifier;
513
514 for (i = 0; i < currentData.length; i++) {
515 currentResult = currentData.charCodeAt(i);
516 if ((instruction === 'c') && (currentResult >= 128)) {
517 currentResult -= 256;
518 }
519 result[label + (quantifier > 1 ?
520 (i + 1) :
521 '')] = currentResult;
522 }
523 break;
524
525 case 'S': // unsigned short (always 16 bit, machine byte order)
526 case 's': // signed short (always 16 bit, machine byte order)
527 case 'v': // unsigned short (always 16 bit, little endian byte order)
528 if (quantifier === '*') {
529 quantifier = (data.length - dataPointer) / 2;
530 } else {
531 quantifier = parseInt(quantifier, 10);
532 }
533
534 currentData = data.substr(dataPointer, quantifier * 2);
535 dataPointer += quantifier * 2;
536
537 for (i = 0; i < currentData.length; i += 2) {
538 // sum per word;
539 currentResult = ((currentData.charCodeAt(i + 1) & 0xFF) << 8) +
540 (currentData.charCodeAt(i) & 0xFF);
541 if ((instruction === 's') && (currentResult >= 32768)) {
542 currentResult -= 65536;
543 }
544 result[label + (quantifier > 1 ?
545 ((i / 2) + 1) :
546 '')] = currentResult;
547 }
548 break;
549
550 case 'n': // unsigned short (always 16 bit, big endian byte order)
551 if (quantifier === '*') {
552 quantifier = (data.length - dataPointer) / 2;
553 } else {
554 quantifier = parseInt(quantifier, 10);
555 }
556
557 currentData = data.substr(dataPointer, quantifier * 2);
558 dataPointer += quantifier * 2;
559
560 for (i = 0; i < currentData.length; i += 2) {
561 // sum per word;
562 currentResult = ((currentData.charCodeAt(i) & 0xFF) << 8) +
563 (currentData.charCodeAt(i + 1) & 0xFF);
564 result[label + (quantifier > 1 ?
565 ((i / 2) + 1) :
566 '')] = currentResult;
567 }
568 break;
569
570 case 'i': // signed integer (machine dependent size and byte order)
571 case 'I': // unsigned integer (machine dependent size & byte order)
572 case 'l': // signed long (always 32 bit, machine byte order)
573 case 'L': // unsigned long (always 32 bit, machine byte order)
574 case 'V': // unsigned long (always 32 bit, little endian byte order)
575 if (quantifier === '*') {
576 quantifier = (data.length - dataPointer) / 4;
577 } else {
578 quantifier = parseInt(quantifier, 10);
579 }
580
581 currentData = data.substr(dataPointer, quantifier * 4);
582 dataPointer += quantifier * 4;
583
584 for (i = 0; i < currentData.length; i += 4) {
585 currentResult =
586 ((currentData.charCodeAt(i + 3) & 0xFF) << 24) +
587 ((currentData.charCodeAt(i + 2) & 0xFF) << 16) +
588 ((currentData.charCodeAt(i + 1) & 0xFF) << 8) +
589 ((currentData.charCodeAt(i) & 0xFF));
590 result[label + (quantifier > 1 ?
591 ((i / 4) + 1) :
592 '')] = currentResult;
593 }
594
595 break;
596
597 case 'N': // unsigned long (always 32 bit, little endian byte order)
598 if (quantifier === '*') {
599 quantifier = (data.length - dataPointer) / 4;
600 } else {
601 quantifier = parseInt(quantifier, 10);
602 }
603
604 currentData = data.substr(dataPointer, quantifier * 4);
605 dataPointer += quantifier * 4;
606
607 for (i = 0; i < currentData.length; i += 4) {
608 currentResult =
609 ((currentData.charCodeAt(i) & 0xFF) << 24) +
610 ((currentData.charCodeAt(i + 1) & 0xFF) << 16) +
611 ((currentData.charCodeAt(i + 2) & 0xFF) << 8) +
612 ((currentData.charCodeAt(i + 3) & 0xFF));
613 result[label + (quantifier > 1 ?
614 ((i / 4) + 1) :
615 '')] = currentResult;
616 }
617
618 break;
619
620 case 'f': //float
621 case 'd': //double
622 ebits = 8;
623 fbits = (instruction === 'f') ? 23 : 52;
624 dataByteLength = 4;
625 if (instruction === 'd') {
626 ebits = 11;
627 dataByteLength = 8;
628 }
629
630 if (quantifier === '*') {
631 quantifier = (data.length - dataPointer) / dataByteLength;
632 } else {
633 quantifier = parseInt(quantifier, 10);
634 }
635
636 currentData = data.substr(dataPointer, quantifier * dataByteLength);
637 dataPointer += quantifier * dataByteLength;
638
639 for (i = 0; i < currentData.length; i += dataByteLength) {
640 data = currentData.substr(i, dataByteLength);
641
642 bytes = [];
643 for (j = data.length - 1; j >= 0; --j) {
644 bytes.push(data.charCodeAt(j));
645 }
646 result[label + (quantifier > 1 ?
647 ((i / 4) + 1) :
648 '')] = fromIEEE754(bytes, ebits, fbits);
649 }
650
651 break;
652
653 case 'x': // NUL byte
654 case 'X': // Back up one byte
655 case '@': // NUL byte
656 if (quantifier === '*') {
657 quantifier = data.length - dataPointer;
658 } else {
659 quantifier = parseInt(quantifier, 10);
660 }
661
662 if (quantifier > 0) {
663 if (instruction === 'X') {
664 dataPointer -= quantifier;
665 } else {
666 if (instruction === 'x') {
667 dataPointer += quantifier;
668 } else {
669 dataPointer = quantifier;
670 }
671 }
672 }
673 break;
674
675 default:
676 throw new Error('Warning: unpack() Type ' + instruction +
677 ': unknown format code');
678 }
679 }
680 return result;
681}
682
683exports.pack = pack;
684exports.unpack = unpack;
\No newline at end of file