New phpjs function: pack()

Kingsquare tributed a new pack-function to the phpjs library by Kevin van Zonneveld.

Comments, reviews, bug reports and improvements are appriciated


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
    // %        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 dependant byte order and size' aren't
    // %        note 5: applicable for JavaScript pack works as on a 32bit,
    // %        note 6: 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[formatPointer];
        quantifier = '';
        formatPointer++;
        while ((formatPointer < format.length) &&
              (format[formatPointer].match(/[\d\*]/) !== null)) {
            quantifier += format[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;
}
2 reacties: RSS feed voor commentaar op dit item
macrilfella
macrilfella
Op 31-01-2012 03:10
Definition Of The Side Effects Of Sildenafil Drug Lessons For Lasix Singulair Dog Breeds Soma Online Cheap Anti-androgens Finasteride Viagra Soft No Prescription Store Viagra Adverse Effects Cialis Levitra Ibutilide And Coreg Arimidex And Osteoporosis Atac Generic Coreg Cost Amoxicillin Taken With Beer Missed Dose Amoxicillin Antibiotic Buy Celexa Without A Prescription Girl Licking Viagra Viagra War On Terror Amoxicillin Online Side Effects Cheap Amoxicillin Prescribed For What Schedule Drug Is Ambien Levitra Duration Professional Cialis Secure Singulair Fda Mood Changes Brand No Prescription Levitra Online Buy Claritin D Sneezing Runny Nose Amoxicillin Sensitivity Organisms How Good Is Does Generic Viagra Work Furosemide Dose Pediatric Propecia Risk Prostate Cancer Prevention Buy Cymbalta Without Drug Ativan Drug Name Cheap Order Prescription Soma Vardenafil No Prescription Generic Cialis Tramadol For Dog Usage Information Flagyl Xopenex Liposomal Delivery Testosterone
Kevin van Zonneveld
Kevin van Zonneveld
Op 14-12-2009 15:14
Hell of a job Tim!
Let's continue this over at phpjs.org!

Plaats een reactie

Terug naar het weblog overzicht