/_octopress/source/functions/pack/index.markdown
Markdown | 385 lines | 347 code | 38 blank | 0 comment | 0 complexity | 2b533e3c81bb35b578ab759a8001be52 MD5 | raw file
- ---
- layout: page
- title: "JavaScript pack function"
- comments: true
- sharing: true
- footer: true
- alias:
- - /functions/view/pack:880
- - /functions/view/pack
- - /functions/view/880
- - /functions/pack:880
- - /functions/880
- ---
- <!-- Generated by Rakefile:build -->
- A JavaScript equivalent of PHP's pack
- {% codeblock misc/pack.js lang:js https://raw.github.com/kvz/phpjs/master/functions/misc/pack.js raw on github %}
- function pack(format) {
- // discuss at: http://phpjs.org/functions/pack/
- // 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: Float encoding by: Jonas Raoni Soares Silva
- // note: Home: http://www.kingsquare.nl/blog/12-12-2009/13507444
- // note: Feedback: phpjs-pack@kingsquare.nl
- // note: 'machine dependent byte order and size' aren't
- // note: applicable for JavaScript; pack works as on a 32bit,
- // note: 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 original by 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;
- }
- {% endcodeblock %}
- - [Raw function on GitHub](https://github.com/kvz/phpjs/blob/master/functions/misc/pack.js)
- Please note that php.js uses JavaScript objects as substitutes for PHP arrays, they are
- the closest match to this hashtable-like data structure.
- Please also note that php.js offers community built functions and goes by the
- [McDonald's Theory](https://medium.com/what-i-learned-building/9216e1c9da7d). We'll put online
- functions that are far from perfect, in the hopes to spark better contributions.
- Do you have one? Then please just:
- - [Edit on GitHub](https://github.com/kvz/phpjs/edit/master/functions/misc/pack.js)
- ### Other PHP functions in the misc extension
- {% render_partial _includes/custom/misc.html %}