/files/phpjs/0.1/misc/pack.js
https://gitlab.com/Mirros/jsdelivr · JavaScript · 346 lines · 277 code · 30 blank · 39 comment · 85 complexity · 3bfadd30a62999b8fdfbb6ac80c59535 MD5 · raw file
- function pack (format) {
- // http://kevin.vanzonneveld.net
- // + original by: Tim de Koning (http://www.kingsquare.nl)
- // + parts by: Jonas Raoni Soares Silva (http://www.jsfromhell.com)
- // + bugfixed by: Tim de Koning (http://www.kingsquare.nl)
- // % note 1: Float encoding by: Jonas Raoni Soares Silva
- // % note 2: Home: http://www.kingsquare.nl/blog/12-12-2009/13507444
- // % note 3: Feedback: phpjs-pack@kingsquare.nl
- // % note 4: 'machine dependent byte order and size' aren't
- // % note 4: applicable for JavaScript; pack works as on a 32bit,
- // % note 4: little endian machine
- // * example 1: pack('nvc*', 0x1234, 0x5678, 65, 66);
- // * returns 1: '4xVAB'
- var formatPointer = 0,
- argumentPointer = 1,
- result = '',
- argument = '',
- i = 0,
- r = [],
- instruction, quantifier, word, precisionBits, exponentBits, extraNullCount;
- // vars used by float encoding
- var bias, minExp, maxExp, minUnnormExp, status, exp, len, bin, signal, n, intPart, floatPart, lastBit, rounded, j, k, tmpResult;
- while (formatPointer < format.length) {
- instruction = format.charAt(formatPointer);
- quantifier = '';
- formatPointer++;
- while ((formatPointer < format.length) && (format.charAt(formatPointer).match(/[\d\*]/) !== null)) {
- quantifier += format.charAt(formatPointer);
- formatPointer++;
- }
- if (quantifier === '') {
- quantifier = '1';
- }
- // Now pack variables: 'quantifier' times 'instruction'
- switch (instruction) {
- case 'a':
- // NUL-padded string
- case 'A':
- // SPACE-padded string
- if (typeof arguments[argumentPointer] === 'undefined') {
- throw new Error('Warning: pack() Type ' + instruction + ': not enough arguments');
- } else {
- argument = String(arguments[argumentPointer]);
- }
- if (quantifier === '*') {
- quantifier = argument.length;
- }
- for (i = 0; i < quantifier; i++) {
- if (typeof argument[i] === 'undefined') {
- if (instruction === 'a') {
- result += String.fromCharCode(0);
- } else {
- result += ' ';
- }
- } else {
- result += argument[i];
- }
- }
- argumentPointer++;
- break;
- case 'h':
- // Hex string, low nibble first
- case 'H':
- // Hex string, high nibble first
- if (typeof arguments[argumentPointer] === 'undefined') {
- throw new Error('Warning: pack() Type ' + instruction + ': not enough arguments');
- } else {
- argument = arguments[argumentPointer];
- }
- if (quantifier === '*') {
- quantifier = argument.length;
- }
- if (quantifier > argument.length) {
- throw new Error('Warning: pack() Type ' + instruction + ': not enough characters in string');
- }
- for (i = 0; i < quantifier; i += 2) {
- // Always get per 2 bytes...
- word = argument[i];
- if (((i + 1) >= quantifier) || typeof argument[i + 1] === 'undefined') {
- word += '0';
- } else {
- word += argument[i + 1];
- }
- // The fastest way to reverse?
- if (instruction === 'h') {
- word = word[1] + word[0];
- }
- result += String.fromCharCode(parseInt(word, 16));
- }
- argumentPointer++;
- break;
- case 'c':
- // signed char
- case 'C':
- // unsigned char
- // c and C is the same in pack
- if (quantifier === '*') {
- quantifier = arguments.length - argumentPointer;
- }
- if (quantifier > (arguments.length - argumentPointer)) {
- throw new Error('Warning: pack() Type ' + instruction + ': too few arguments');
- }
- for (i = 0; i < quantifier; i++) {
- result += String.fromCharCode(arguments[argumentPointer]);
- argumentPointer++;
- }
- break;
- case 's':
- // signed short (always 16 bit, machine byte order)
- case 'S':
- // unsigned short (always 16 bit, machine byte order)
- case 'v':
- // s and S is the same in pack
- if (quantifier === '*') {
- quantifier = arguments.length - argumentPointer;
- }
- if (quantifier > (arguments.length - argumentPointer)) {
- throw new Error('Warning: pack() Type ' + instruction + ': too few arguments');
- }
- for (i = 0; i < quantifier; i++) {
- result += String.fromCharCode(arguments[argumentPointer] & 0xFF);
- result += String.fromCharCode(arguments[argumentPointer] >> 8 & 0xFF);
- argumentPointer++;
- }
- break;
- case 'n':
- // unsigned short (always 16 bit, big endian byte order)
- if (quantifier === '*') {
- quantifier = arguments.length - argumentPointer;
- }
- if (quantifier > (arguments.length - argumentPointer)) {
- throw new Error('Warning: pack() Type ' + instruction + ': too few arguments');
- }
- for (i = 0; i < quantifier; i++) {
- result += String.fromCharCode(arguments[argumentPointer] >> 8 & 0xFF);
- result += String.fromCharCode(arguments[argumentPointer] & 0xFF);
- argumentPointer++;
- }
- break;
- case 'i':
- // signed integer (machine dependent size and byte order)
- case 'I':
- // unsigned integer (machine dependent size and byte order)
- case 'l':
- // signed long (always 32 bit, machine byte order)
- case 'L':
- // unsigned long (always 32 bit, machine byte order)
- case 'V':
- // unsigned long (always 32 bit, little endian byte order)
- if (quantifier === '*') {
- quantifier = arguments.length - argumentPointer;
- }
- if (quantifier > (arguments.length - argumentPointer)) {
- throw new Error('Warning: pack() Type ' + instruction + ': too few arguments');
- }
- for (i = 0; i < quantifier; i++) {
- result += String.fromCharCode(arguments[argumentPointer] & 0xFF);
- result += String.fromCharCode(arguments[argumentPointer] >> 8 & 0xFF);
- result += String.fromCharCode(arguments[argumentPointer] >> 16 & 0xFF);
- result += String.fromCharCode(arguments[argumentPointer] >> 24 & 0xFF);
- argumentPointer++;
- }
- break;
- case 'N':
- // unsigned long (always 32 bit, big endian byte order)
- if (quantifier === '*') {
- quantifier = arguments.length - argumentPointer;
- }
- if (quantifier > (arguments.length - argumentPointer)) {
- throw new Error('Warning: pack() Type ' + instruction + ': too few arguments');
- }
- for (i = 0; i < quantifier; i++) {
- result += String.fromCharCode(arguments[argumentPointer] >> 24 & 0xFF);
- result += String.fromCharCode(arguments[argumentPointer] >> 16 & 0xFF);
- result += String.fromCharCode(arguments[argumentPointer] >> 8 & 0xFF);
- result += String.fromCharCode(arguments[argumentPointer] & 0xFF);
- argumentPointer++;
- }
- break;
- case 'f':
- // float (machine dependent size and representation)
- case 'd':
- // double (machine dependent size and representation)
- // version based on IEEE754
- precisionBits = 23;
- exponentBits = 8;
- if (instruction === 'd') {
- precisionBits = 52;
- exponentBits = 11;
- }
- if (quantifier === '*') {
- quantifier = arguments.length - argumentPointer;
- }
- if (quantifier > (arguments.length - argumentPointer)) {
- throw new Error('Warning: pack() Type ' + instruction + ': too few arguments');
- }
- for (i = 0; i < quantifier; i++) {
- argument = arguments[argumentPointer];
- bias = Math.pow(2, exponentBits - 1) - 1;
- minExp = -bias + 1;
- maxExp = bias;
- minUnnormExp = minExp - precisionBits;
- status = isNaN(n = parseFloat(argument)) || n === -Infinity || n === +Infinity ? n : 0;
- exp = 0;
- len = 2 * bias + 1 + precisionBits + 3;
- bin = new Array(len);
- signal = (n = status !== 0 ? 0 : n) < 0;
- n = Math.abs(n);
- intPart = Math.floor(n);
- floatPart = n - intPart;
- for (k = len; k;) {
- bin[--k] = 0;
- }
- for (k = bias + 2; intPart && k;) {
- bin[--k] = intPart % 2;
- intPart = Math.floor(intPart / 2);
- }
- for (k = bias + 1; floatPart > 0 && k; --floatPart) {
- (bin[++k] = ((floatPart *= 2) >= 1) - 0);
- }
- for (k = -1; ++k < len && !bin[k];) {}
- if (bin[(lastBit = precisionBits - 1 + (k = (exp = bias + 1 - k) >= minExp && exp <= maxExp ? k + 1 : bias + 1 - (exp = minExp - 1))) + 1]) {
- if (!(rounded = bin[lastBit])) {
- for (j = lastBit + 2; !rounded && j < len; rounded = bin[j++]) {}
- }
- for (j = lastBit + 1; rounded && --j >= 0;
- (bin[j] = !bin[j] - 0) && (rounded = 0)) {}
- }
- for (k = k - 2 < 0 ? -1 : k - 3; ++k < len && !bin[k];) {}
- if ((exp = bias + 1 - k) >= minExp && exp <= maxExp) {
- ++k;
- } else {
- if (exp < minExp) {
- if (exp !== bias + 1 - len && exp < minUnnormExp) { /*"encodeFloat::float underflow" */
- }
- k = bias + 1 - (exp = minExp - 1);
- }
- }
- if (intPart || status !== 0) {
- exp = maxExp + 1;
- k = bias + 2;
- if (status === -Infinity) {
- signal = 1;
- } else if (isNaN(status)) {
- bin[k] = 1;
- }
- }
- n = Math.abs(exp + bias);
- tmpResult = '';
- for (j = exponentBits + 1; --j;) {
- tmpResult = (n % 2) + tmpResult;
- n = n >>= 1;
- }
- n = 0;
- j = 0;
- k = (tmpResult = (signal ? '1' : '0') + tmpResult + bin.slice(k, k + precisionBits).join('')).length;
- r = [];
- for (; k;) {
- n += (1 << j) * tmpResult.charAt(--k);
- if (j === 7) {
- r[r.length] = String.fromCharCode(n);
- n = 0;
- }
- j = (j + 1) % 8;
- }
- r[r.length] = n ? String.fromCharCode(n) : '';
- result += r.join('');
- argumentPointer++;
- }
- break;
- case 'x':
- // NUL byte
- if (quantifier === '*') {
- throw new Error('Warning: pack(): Type x: \'*\' ignored');
- }
- for (i = 0; i < quantifier; i++) {
- result += String.fromCharCode(0);
- }
- break;
- case 'X':
- // Back up one byte
- if (quantifier === '*') {
- throw new Error('Warning: pack(): Type X: \'*\' ignored');
- }
- for (i = 0; i < quantifier; i++) {
- if (result.length === 0) {
- throw new Error('Warning: pack(): Type X:' + ' outside of string');
- } else {
- result = result.substring(0, result.length - 1);
- }
- }
- break;
- case '@':
- // NUL-fill to absolute position
- if (quantifier === '*') {
- throw new Error('Warning: pack(): Type X: \'*\' ignored');
- }
- if (quantifier > result.length) {
- extraNullCount = quantifier - result.length;
- for (i = 0; i < extraNullCount; i++) {
- result += String.fromCharCode(0);
- }
- }
- if (quantifier < result.length) {
- result = result.substring(0, quantifier);
- }
- break;
- default:
- throw new Error('Warning: pack() Type ' + instruction + ': unknown format code');
- }
- }
- if (argumentPointer < arguments.length) {
- throw new Error('Warning: pack(): ' + (arguments.length - argumentPointer) + ' arguments unused');
- }
- return result;
- }