You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

481 lines
11 KiB

// Copyright (c) 2008, Fair Oaks Labs, Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice, this list
// of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice, this
// list of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
// * Neither the name of Fair Oaks Labs, Inc. nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Modified from original JSPack <cbond@positrace.com>
exports.jspack = function (bigEndian) {
this.bigEndian = bigEndian;
}
exports.jspack.prototype._DeArray = function (a, p, l) {
return [a.slice(p, p + l)];
};
exports.jspack.prototype._EnArray = function (a, p, l, v) {
for (var i = 0; i < l; ++i) {
a[p + i] = v[i] ? v[i] : 0;
}
};
exports.jspack.prototype._DeChar = function (a, p) {
return String.fromCharCode(a[p]);
};
exports.jspack.prototype._EnChar = function (a, p, v) {
a[p] = v.charCodeAt(0);
};
exports.jspack.prototype._DeInt = function (a, p) {
var lsb = bigEndian ? format.len - 1 : 0;
var nsb = bigEndian ? -1 : 1;
var stp = lsb + nsb * format.len,
rv;
var ret = 0;
var i = lsb;
var f = 1;
while (i != stp) {
rv += a[p + i] * f;
i += nsb;
f *= 256;
}
if (format.signed) {
if ((rv & Math.pow(2, format.len * 8 - 1)) != 0) {
rv -= Math.pow(2, format.len * 8);
}
}
return rv;
};
exports.jspack.prototype._EnInt = function (a, p, v) {
var lsb = bigEndian ? format.len - 1 : 0;
var nsb = bigEndian ? -1 : 1;
var stp = lsb + nsb * format.len;
v = v < format.min ? format.min : ((v > format.max) ? format.max : v);
var i = lsb;
while (i != stp) {
a[p + i] = v & 0xff;
i += nsb;
v >>= 8;
}
};
exports.jspack.prototype._DeString = function (a, p, l) {
var rv = new Array(1);
for (i = 0; i < l; i++) {
rv[i] = String.fromCharCode(a[p + i]);
}
return rv.join('');
};
exports.jspack.prototype._EnString = function (a, p, l, v) {
for (var t, i = 0; i < l; ++i) {
t = v.charCodeAt(i);
if (!t) t = 0;
a[p + i] = t;
}
};
exports.jspack.prototype._De754 = function (a, p) {
var s, e, m, i, d, bits, bit, len, bias, max;
bit = format.bit;
len = format.len * 8 - format.bit - 1;
max = (1 << len) - 1;
bias = max >> 1;
i = bigEndian ? 0 : format.len - 1;
d = bigEndian ? 1 : -1;;
s = a[p + i];
i = i + d;
bits = -7;
e = s & ((1 << -bits) - 1);
s >>= -bits;
for (bits += len; bits > 0; bits -= 8) {
e = e * 256 + a[p + i];
i += d;
}
m = e & ((1 << -bits) - 1);
e >>= -bits;
for (bits += bit; bits > 0; bits -= 8) {
m = m * 256 + a[p + i];
i += d;
}
switch (e) {
case 0:
// Zero, or denormalized number
e = 1 - bias;
break;
case max:
// NaN, or +/-Infinity
return m ? NaN : ((s ? -1 : 1) * Infinity);
default:
// Normalized number
m = m + Math.pow(2, bit);
e = e - bias;
break;
}
return (s ? -1 : 1) * m * Math.pow(2, e - bit);
};
exports.jspack.prototype._En754 = function (a, p, v) {
var s, e, m, i, d, c, bit, len, bias, max;
bit = format.bit;
len = format.len * 8 - format.bit - 1;
max = (1 << len) - 1;
bias = max >> 1;
s = v < 0 ? 1 : 0;
v = Math.abs(v);
if (isNaN(v) || (v == Infinity)) {
m = isNaN(v) ? 1 : 0;
e = max;
} else {
e = Math.floor(Math.log(v) / Math.LN2); // Calculate log2 of the value
c = Math.pow(2, -e);
if (v * c < 1) {
e--;
c = c * 2;
}
// Round by adding 1/2 the significand's LSD
if (e + bias >= 1) {
v += format.rt / c; // Normalized: bit significand digits
} else {
v += format.rt * Math.pow(2, 1 - bias); // Denormalized: <= bit significand digits
}
if (v * c >= 2) {
e++;
c = c / 2; // Rounding can increment the exponent
}
if (e + bias >= max) { // overflow
m = 0;
e = max;
} else if (e + bias >= 1) { // normalized
m = (v * c - 1) * Math.pow(2, bit); // do not reorder this expression
e = e + bias;
} else {
// Denormalized - also catches the '0' case, somewhat by chance
m = v * Math.pow(2, bias - 1) * Math.pow(2, bit);
e = 0;
}
}
i = bigEndian ? format.len - 1 : 0;
d = bigEndian ? -1 : 1;;
while (bit >= 8) {
a[p + i] = m & 0xff;
i += d;
m /= 256;
bit -= 8;
}
e = (e << bit) | m;
for (len += bit; len > 0; len -= 8) {
a[p + i] = e & 0xff;
i += d;
e /= 256;
}
a[p + i - d] |= s * 128;
};
// Unpack a series of n formatements of size s from array a at offset p with fxn
exports.jspack.prototype._UnpackSeries = function (n, s, a, p) {
var fxn = format.de;
var ret = [];
for (var i = 0; i < n; i++) {
ret.push(fxn(a, p + i * s));
}
return ret;
};
// Pack a series of n formatements of size s from array v at offset i to array a at offset p with fxn
exports.jspack.prototype._PackSeries = function (n, s, a, p, v, i) {
var fxn = format.en;
for (o = 0; o < n; o++) {
fxn(a, p + o * s, v[i + o]);
}
};
// Unpack the octet array a, beginning at offset p, according to the fmt string
exports.jspack.prototype.Unpack = function (fmt, a, p) {
bigEndian = fmt.charAt(0) != '<';
if (p == undefined || p == null) p = 0;
var re = new RegExp(this._sPattern, 'g');
var ret = [];
for (var m; m = re.exec(fmt); /* */ ) {
var n;
if (m[1] == undefined || m[1] == '') n = 1;
else n = parseInt(m[1]);
var s = this._lenLut[m[2]];
if ((p + n * s) > a.length) return undefined;
switch (m[2]) {
case 'A':
case 's':
rv.push(this._formatLut[m[2]].de(a, p, n));
break;
case 'c':
case 'b':
case 'B':
case 'h':
case 'H':
case 'i':
case 'I':
case 'l':
case 'L':
case 'f':
case 'd':
format = this._formatLut[m[2]];
ret.push(this._UnpackSeries(n, s, a, p));
break;
}
p += n * s;
}
return Array.prototype.concat.apply([], ret);
};
// Pack the supplied values into the octet array a, beginning at offset p, according to the fmt string
exports.jspack.prototype.PackTo = function (fmt, a, p, values) {
bigEndian = (fmt.charAt(0) != '<');
var re = new RegExp(this._sPattern, 'g');
for (var m, i = 0; m = re.exec(fmt); /* */ ) {
var n;
if (m[1] == undefined || m[1] == '') n = 1;
else n = parseInt(m[1]);
var s = this._lenLut[m[2]];
if ((p + n * s) > a.length) return false;
switch (m[2]) {
case 'A':
case 's':
if ((i + 1) > values.length) return false;
this._formatLut[m[2]].en(a, p, n, values[i]);
i += 1;
break;
case 'c':
case 'b':
case 'B':
case 'h':
case 'H':
case 'i':
case 'I':
case 'l':
case 'L':
case 'f':
case 'd':
format = this._formatLut[m[2]];
if (i + n > values.length) return false;
this._PackSeries(n, s, a, p, values, i);
i += n;
break;
case 'x':
for (var j = 0; j < n; j++) {
a[p + j] = 0;
}
break;
}
p += n * s;
}
return a;
};
// Pack the supplied values into a new octet array, according to the fmt string
exports.jspack.prototype.Pack = function (fmt, values) {
return this.PackTo(fmt, new Array(this.CalcLength(fmt)), 0, values);
};
// Determine the number of bytes represented by the format string
exports.jspack.prototype.CalcLength = function (fmt) {
var re = new RegExp(this._sPattern, 'g');
var sz = 0;
while (match = re.exec(fmt)) {
var n;
if (match[1] == undefined || match[1] == '') n = 1;
else n = parseInt(match[1]);
sz += n * this._lenLut[match[2]];
}
return sz;
};
// Regular expression for counting digits
exports.jspack.prototype._sPattern = '(\\d+)?([AxcbBhHsfdiIlL])';
// Byte widths for associated formats
exports.jspack.prototype._lenLut = {
'A': 1,
'x': 1,
'c': 1,
'b': 1,
'B': 1,
'h': 2,
'H': 2,
's': 1,
'f': 4,
'd': 8,
'i': 4,
'I': 4,
'l': 4,
'L': 4
};
exports.jspack.prototype._formatLut = {
'A': {
en: exports.jspack.prototype._EnArray,
de: exports.jspack.prototype._DeArray
},
's': {
en: exports.jspack.prototype._EnString,
de: exports.jspack.prototype._DeString
},
'c': {
en: exports.jspack.prototype._EnChar,
de: exports.jspack.prototype._DeChar
},
'b': {
en: exports.jspack.prototype._EnInt,
de: exports.jspack.prototype._DeInt,
len: 1,
signed: true,
min: -Math.pow(2, 7),
max: Math.pow(2, 7) - 1
},
'B': {
en: exports.jspack.prototype._EnInt,
de: exports.jspack.prototype._DeInt,
len: 1,
signed: false,
min: 0,
max: Math.pow(2, 8) - 1
},
'h': {
en: exports.jspack.prototype._EnInt,
de: exports.jspack.prototype._DeInt,
len: 2,
signed: true,
min: -Math.pow(2, 15),
max: Math.pow(2, 15) - 1
},
'H': {
en: exports.jspack.prototype._EnInt,
de: exports.jspack.prototype._DeInt,
len: 2,
signed: false,
min: 0,
max: Math.pow(2, 16) - 1
},
'i': {
en: exports.jspack.prototype._EnInt,
de: exports.jspack.prototype._DeInt,
len: 4,
signed: true,
min: -Math.pow(2, 31),
max: Math.pow(2, 31) - 1
},
'I': {
en: exports.jspack.prototype._EnInt,
de: exports.jspack.prototype._DeInt,
len: 4,
signed: false,
min: 0,
max: Math.pow(2, 32) - 1
},
'l': {
en: exports.jspack.prototype._EnInt,
de: exports.jspack.prototype._DeInt,
len: 4,
signed: true,
min: -Math.pow(2, 31),
max: Math.pow(2, 31) - 1
},
'L': {
en: exports.jspack.prototype._EnInt,
de: exports.jspack.prototype._DeInt,
len: 4,
signed: false,
min: 0,
max: Math.pow(2, 32) - 1
},
'f': {
en: exports.jspack.prototype._En754,
de: exports.jspack.prototype._De754,
len: 4,
bit: 23,
rt: Math.pow(2, -24) - Math.pow(2, -77)
},
'd': {
en: exports.jspack.prototype._En754,
de: exports.jspack.prototype._De754,
len: 8,
bit: 52,
rt: 0
}
};