Binary Parser //JavaScript Repository

Description

Serializes and unserializes binary data.
Created: 2005.08.08

Code (Download)

//+ Jonas Raoni Soares Silva
//@ http://jsfromhell.com/classes/binary-parser [rev. #1]

BinaryParser = function(bigEndian, allowExceptions){
    this.bigEndian = bigEndian, this.allowExceptions = allowExceptions;
};
with({p: BinaryParser.prototype}){
    p.encodeFloat = function(number, precisionBits, exponentBits){
        var bias = Math.pow(2, exponentBits - 1) - 1, minExp = -bias + 1, maxExp = bias, minUnnormExp = minExp - precisionBits,
        status = isNaN(n = parseFloat(number)) || 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,
        i, lastBit, rounded, j, result;
        for(i = len; i; bin[--i] = 0);
        for(i = bias + 2; intPart && i; bin[--i] = intPart % 2, intPart = Math.floor(intPart / 2));
        for(i = bias + 1; floatPart > 0 && i; (bin[++i] = ((floatPart *= 2) >= 1) - 0) && --floatPart);
        for(i = -1; ++i < len && !bin[i];);
        if(bin[(lastBit = precisionBits - 1 + (i = (exp = bias + 1 - i) >= minExp && exp <= maxExp ? i + 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(i = i - 2 < 0 ? -1 : i - 3; ++i < len && !bin[i];);

        (exp = bias + 1 - i) >= minExp && exp <= maxExp ? ++i : exp < minExp &&
            (exp != bias + 1 - len && exp < minUnnormExp && this.warn("encodeFloat::float underflow"), i = bias + 1 - (exp = minExp - 1));
        (intPart || status !== 0) && (this.warn(intPart ? "encodeFloat::float overflow" : "encodeFloat::" + status),
            exp = maxExp + 1, i = bias + 2, status == -Infinity ? signal = 1 : isNaN(status) && (bin[i] = 1));
        for(n = Math.abs(exp + bias), j = exponentBits + 1, result = ""; --j; result = (n % 2) + result, n = n >>= 1);
        for(n = 0, j = 0, i = (result = (signal ? "1" : "0") + result + bin.slice(i, i + precisionBits).join("")).length, r = [];
            i; n += (1 << j) * result.charAt(--i), j == 7 && (r[r.length] = String.fromCharCode(n), n = 0), j = (j + 1) % 8);
        r[r.length] = n ? String.fromCharCode(n) : "";
        return (this.bigEndian ? r.reverse() : r).join("");
    };
    p.encodeInt = function(number, bits, signed){
        var max = Math.pow(2, bits), r = [];
        (number >= max || number < -(max >> 1)) && this.warn("encodeInt::overflow") && (number = 0);
        number < 0 && (number += max);
        for(; number; r[r.length] = String.fromCharCode(number % 256), number = Math.floor(number / 256));
        for(bits = -(-bits >> 3) - r.length; bits--; r[r.length] = "\0");
        return (this.bigEndian ? r.reverse() : r).join("");
    };
    p.decodeFloat = function(data, precisionBits, exponentBits){
        var b = ((b = new this.Buffer(this.bigEndian, data)).checkBuffer(precisionBits + exponentBits + 1), b),
            bias = Math.pow(2, exponentBits - 1) - 1, signal = b.readBits(precisionBits + exponentBits, 1),
            exponent = b.readBits(precisionBits, exponentBits), significand = 0,
            divisor = 2, curByte = b.buffer.length + (-precisionBits >> 3) - 1,
            byteValue, startBit, mask;
        do
            for(byteValue = b.buffer[ ++curByte ], startBit = precisionBits % 8 || 8, mask = 1 << startBit;
                mask >>= 1; (byteValue & mask) && (significand += 1 / divisor), divisor *= 2);
        while(precisionBits -= startBit);
        return exponent == (bias << 1) + 1 ? significand ? NaN : signal ? -Infinity : +Infinity
            : (1 + signal * -2) * (exponent || significand ? !exponent ? Math.pow(2, -bias + 1) * significand
            : Math.pow(2, exponent - bias) * (1 + significand) : 0);
    };
    p.decodeInt = function(data, bits, signed){
        var b = new this.Buffer(this.bigEndian, data), x = b.readBits(0, bits), max = Math.pow(2, bits);
        return signed && x >= max / 2 ? x - max : x;
    };
    with({p: (p.Buffer = function(bigEndian, buffer){
        this.bigEndian = bigEndian || 0, this.buffer = [], this.setBuffer(buffer);
    }).prototype}){
        p.readBits = function(start, length){
            //shl fix: Henri Torgemane ~1996 (compressed by Jonas Raoni)
            function shl(a, b){
                for(++b; --b; a = ((a %= 0x7fffffff + 1) & 0x40000000) == 0x40000000 ? a * 2 : (a - 0x40000000) * 2 + 0x7fffffff + 1);
                return a;
            }
            if(start < 0 || length <= 0)
                return 0;
            this.checkBuffer(start + length);
            for(var offsetLeft, offsetRight = start % 8, curByte = this.buffer.length - (start >> 3) - 1,
                lastByte = this.buffer.length + (-(start + length) >> 3), diff = curByte - lastByte,
                sum = ((this.buffer[ curByte ] >> offsetRight) & ((1 << (diff ? 8 - offsetRight : length)) - 1))
                + (diff && (offsetLeft = (start + length) % 8) ? (this.buffer[ lastByte++ ] & ((1 << offsetLeft) - 1))
                << (diff-- << 3) - offsetRight : 0); diff; sum += shl(this.buffer[ lastByte++ ], (diff-- << 3) - offsetRight)
            );
            return sum;
        };
        p.setBuffer = function(data){
            if(data){
                for(var l, i = l = data.length, b = this.buffer = new Array(l); i; b[l - i] = data.charCodeAt(--i));
                this.bigEndian && b.reverse();
            }
        };
        p.hasNeededBits = function(neededBits){
            return this.buffer.length >= -(-neededBits >> 3);
        };
        p.checkBuffer = function(neededBits){
            if(!this.hasNeededBits(neededBits))
                throw new Error("checkBuffer::missing bytes");
        };
    }
    p.warn = function(msg){
        if(this.allowExceptions)
            throw new Error(msg);
        return 1;
    };
    p.toSmall = function(data){return this.decodeInt(data, 8, true);};
    p.fromSmall = function(number){return this.encodeInt(number, 8, true);};
    p.toByte = function(data){return this.decodeInt(data, 8, false);};
    p.fromByte = function(number){return this.encodeInt(number, 8, false);};
    p.toShort = function(data){return this.decodeInt(data, 16, true);};
    p.fromShort = function(number){return this.encodeInt(number, 16, true);};
    p.toWord = function(data){return this.decodeInt(data, 16, false);};
    p.fromWord = function(number){return this.encodeInt(number, 16, false);};
    p.toInt = function(data){return this.decodeInt(data, 32, true);};
    p.fromInt = function(number){return this.encodeInt(number, 32, true);};
    p.toDWord = function(data){return this.decodeInt(data, 32, false);};
    p.fromDWord = function(number){return this.encodeInt(number, 32, false);};
    p.toFloat = function(data){return this.decodeFloat(data, 23, 8);};
    p.fromFloat = function(number){return this.encodeFloat(number, 23, 8);};
    p.toDouble = function(data){return this.decodeFloat(data, 52, 11);};
    p.fromDouble = function(number){return this.encodeFloat(number, 52, 11);};
}

Example (Example)

<form action="">
    <fieldset>
        <br /><input type="radio" name="endian" value="0" checked="checked" />Big Endian
        <input type="radio" name="endian" value="1" />Little Endian
        <br /><br />
    <fieldset>
        <legend>N?mero > Hexadecimal</legend>
        Converter n?mero <input type="text" name="fromNumber" value="123" /> para
        <select name="toHex">
            <option value="Small">Small</option>
            <option value="Byte">Byte</option>
            <option value="Short">Short</option>
            <option value="Word">Word</option>
            <option value="Int">Int</option>
            <option value="DWord">DWord</option>
            <option value="Float">Float</option>
            <option value="Double">Double</option>
        </select>
        <br /><input type="button" name="n2h" value="Converter" />
    </fieldset>
    <br />
    <fieldset>
        <legend>Hexadecimal > N?mero</legend>
        Converter hexadecimal <input type="text" name="fromHex" value="7b" /> para n?mero
        <select name="toNumber">
            <option value="Small">Small</option>
            <option value="Byte">Byte</option>
            <option value="Short">Short</option>
            <option value="Word">Word</option>
            <option value="Int">Int</option>
            <option value="DWord">DWord</option>
            <option value="Float">Float</option>
            <option value="Double">Double</option>
        </select>
        <br /><input type="button" name="h2n" value="Converter" />
    </fieldset>
    </fieldset>
</form>

<script type="text/javascript">
//<![CDATA[

f = document.forms[0];
//http://www.jsfromhell.com/geral/event-listener
addEvent(f.n2h, "click", function(e){
    function char2hex(s){
        for(var k, i = s.length, r = ""; i; r = ((k = s.charCodeAt(--i).toString(16 )).length - 1 ? k : "0" + k) + r);
        return r;
    }
    try{
        alert(char2hex((new BinaryParser(f.endian[0].checked, true))["from" + f.toHex.value](f.fromNumber.value - 0)));
    }
    catch(e){
        alert('Erro\n'+e.message);
    }
});

//http://www.jsfromhell.com/geral/event-listener
addEvent(f.h2n, "click", function(e){
    function hex2bin(s){
        for(var i = 0, l = s.length, r = ""; i < l; r += String.fromCharCode(parseInt(s.substr(i, 2), 16)), i += 2);
        return r;
    }
    try{
        alert((new BinaryParser(f.endian[0].checked, true))["to" + f.toNumber.value](hex2bin(f.fromHex.value)));
    }
    catch(e){
        alert('Erro\n'+e.message);
    }
} );

//]]>
</script>

Help

This class is able to serialize and unserialize binary data, so you can read files generated with C, pascal, etc as well as generate such data. It's also able to handle the byte order (big/little endian) and supports the following types: signed integer (small 8 bits, short 16 bits, int 32 bits), unsigned integer (byte 8 bits, word 16 bits, dword 32 bits) and floating point (IEEE754 float 32 bits and double 64 bits).

Constructor

BinaryParser([bigEndian: Boolean = false], [allowExceptions: Boolean = false])
Generates an instance of BinaryParser.
bigEndian
if true, the class will assume the bigEndian format for the input and output, otherwise, it will assume the little endian format.
allowExceptions
if true, when number=>binary conversion error occur an exception will be raised (which can be caught through a "try..except" block)

Properties

BinaryParser.bigEndian: Boolean
if true, the class will assume the bigEndian format for the input and output, otherwise, it will assume the little endian format.
BinaryParser.allowExceptions: Boolean
if true, when number=>binary conversion error occur an exception will be raised (which can be caught through a "try..except" block)

Gerenic Methods

BinaryParser.decodeFloat(data: String, precisionBits: Integer, exponentBits: Integer): Float
Decodes a string containing the binary representation of a number in the IEEE-754 pattern and returns the number or the following special values: NaN, +Infinity, -Infinity.
data
string containing the binary representation of the number (must contain at least "ceil((exponentBits + precisionBits + 1) / 8)" bytes)
precisionBits
amount of bits that specifies the precision/mantisse
exponentBits
amount of bits that specifies the exponent
BinaryParser.encodeFloat(number: Float, precisionBits: Integer, exponentBits: Integer): String
Encodes a number into the IEEE-754 pattern and returns the binary representation of it in a string containing "ceil((exponentBits + precisionBits + 1) / 8)" bytes.
number
number to be converted
precisionBits
amount of bits that specifies the precision/mantisse
exponentBits
amount of bits that specifies the exponent
BinaryParser.decodeInt(data: String, bits: Integer, signed: Boolean): Integer
Decodes a string containing binary data and returns the number that it represents.
data
string containing the binary representation of the number (must contain at least "ceil(bits / 8)" bytes)
bits
amount of bits that specifies the quantity of numbers that can be represented
signed
indicates if the number must be decoded with signal or without signal
BinaryParser.encodeInt(number: Int, bits: Integer, signed: Boolean): Integer
Encodes an integer number and returns it's binary representation on a string containing "ceil(bits / 8)" bytes.
number
number to be converted
bits
amount of bits that specifies the quantity of numbers that can be represented
signed
indicates if the number must be encoded with signal or without signal

Methods (Float Point)

BinaryParser.toFloat(data: String): Float
Returns a number or a special value (NaN, +Infinity, -Infinity).
data
string containing at least 4 bytes
BinaryParser.fromFloat(number: Float): String
Returns the binary representation of a number in a string, the method can raise exceptions if the property "allowExceptions" is true and the number is an special value (NaN, +Infinity, -Infinity) or if it can't be represented (overflow, underflow).
number
number to be converted to binary
BinaryParser.toDouble(data: String): Float
Returns a number or a special value (NaN, +Infinity, -Infinity).
data
string containing at least 8 bytes
BinaryParser.fromDouble(number: Float): String
Returns the binary representation of a number in a string, the method can raise exceptions if the property "allowExceptions" is true and the number is an special value (NaN, +Infinity, -Infinity) or if it can't be represented (overflow, underflow).
number
number to be converted to binary

Methods (Integer Numbers)

BinaryParser.toSmall(data: String): Integer
Receives a string and returns its integer value with signal.
data
string containing at least 1 bytes
BinaryParser.fromSmall(number: Integer): String
Receives an integer and returns its binary representation in a string with 1 byte, the method can raise an exception if the number is too big to be represented and the property "allowExceptions" is true.
number
number to be converted to binary
BinaryParser.toByte(data: String): Integer
Receives a string and returns its integer value without signal.
data
string containing at least 1 bytes
BinaryParser.fromByte(number: Integer): String
Receives an integer and returns its binary representation in a string with 1 byte, the method can raise an exception if the number is too big to be represented and the property "allowExceptions" is true.
number
number to be converted to binary, the class ignores if the number is negative or not (in the two's complement, "-1" is represented in the same way as "255")
BinaryParser.toShort(data: String): Integer
Receives a string and returns its integer value with signal.
data
string containing at least 2 bytes
BinaryParser.fromShort(number: Integer): String
Receives an integer and returns its binary representation in a string with 2 bytes, the method can raise an exception if the number is too big to be represented and the property "allowExceptions" is true.
number
number to be converted to binary
BinaryParser.toWord(data: String): Integer
Receives a string and returns its integer value without signal.
data
string containing at least 2 bytes
BinaryParser.fromWord(number: Integer): String
Receives an integer and returns its binary representation in a string with 2 bytes, the method can raise an exception if the number is too big to be represented and the property "allowExceptions" is true.
number
number to be converted to binary, the class ignores if the number is negative or not (in the two's complement, "-1" is represented in the same way as "255")
BinaryParser.toInt(data: String): Integer
Receives a string and returns its integer value with signal.
data
string containing at least 4 bytes
BinaryParser.fromInt(number: Integer): String
Receives an integer and returns its binary representation in a string with 4 bytes, the method can raise an exception if the number is too big to be represented and the property "allowExceptions" is true.
number
number to be converted to binary
BinaryParser.toDWord(data: String): Integer
Receives a string and returns its integer value without signal.
data
string containing at least 4 bytes
BinaryParser.fromDWord(number: Integer): String
Receives an integer and returns its binary representation in a string with 4 bytes, the method can raise an exception if the number is too big to be represented and the property "allowExceptions" is true.
number
number to be converted to binary, the class ignores if the number is negative or not (in the two's complement, "-1" is represented in the same way as "255")

Rank (Votes: 141)

2.77