Browse files

Import of unmodified jspack-1.0.0.

  • Loading branch information...
0 parents commit 159143940a098fb4ed02fe695f8bf5de71088397 @pgriess committed May 22, 2010
Showing with 402 additions and 0 deletions.
  1. +250 −0 code/struct.js
  2. +26 −0 docs/LICENSE
  3. +126 −0 docs/README
250 code/struct.js
@@ -0,0 +1,250 @@
+/*!
+ * Copyright © 2008 Fair Oaks Labs, Inc.
+ * All rights reserved.
+ */
+
+// Utility object: Encode/Decode C-style binary primitives to/from octet arrays
+var Struct = new function ()
+{
+ // Module-level (private) variables
+ var el, bBE = false, m = this;
+
+
+ // Raw byte arrays
+ m._DeArray = function (a, p, l)
+ {
+ return [a.slice(p,p+l)];
+ };
+ m._EnArray = function (a, p, l, v)
+ {
+ for (var i = 0; i < l; a[p+i] = v[i]?v[i]:0, i++);
+ };
+
+ // ASCII characters
+ m._DeChar = function (a, p)
+ {
+ return String.fromCharCode(a[p]);
+ };
+ m._EnChar = function (a, p, v)
+ {
+ a[p] = v.charCodeAt(0);
+ };
+
+ // Little-endian (un)signed N-byte integers
+ m._DeInt = function (a, p)
+ {
+ var lsb = bBE?(el.len-1):0, nsb = bBE?-1:1, stop = lsb+nsb*el.len, rv, i, f;
+ for (rv = 0, i = lsb, f = 1; i != stop; rv+=(a[p+i]*f), i+=nsb, f*=256);
+ if (el.bSigned && (rv & Math.pow(2, el.len*8-1))) { rv -= Math.pow(2, el.len*8); }
+ return rv;
+ };
+ m._EnInt = function (a, p, v)
+ {
+ var lsb = bBE?(el.len-1):0, nsb = bBE?-1:1, stop = lsb+nsb*el.len, i;
+ v = (v<el.min)?el.min:(v>el.max)?el.max:v;
+ for (i = lsb; i != stop; a[p+i]=v&0xff, i+=nsb, v>>=8);
+ };
+
+ // ASCII character strings
+ m._DeString = function (a, p, l)
+ {
+ for (var rv = new Array(l), i = 0; i < l; rv[i] = String.fromCharCode(a[p+i]), i++);
+ return rv.join('');
+ };
+ m._EnString = function (a, p, l, v)
+ {
+ for (var t, i = 0; i < l; a[p+i] = (t=v.charCodeAt(i))?t:0, i++);
+ };
+
+ // Little-endian N-bit IEEE 754 floating point
+ m._De754 = function (a, p)
+ {
+ var s, e, m, i, d, nBits, mLen, eLen, eBias, eMax;
+ mLen = el.mLen, eLen = el.len*8-el.mLen-1, eMax = (1<<eLen)-1, eBias = eMax>>1;
+
+ i = bBE?0:(el.len-1); d = bBE?1:-1; s = a[p+i]; i+=d; nBits = -7;
+ for (e = s&((1<<(-nBits))-1), s>>=(-nBits), nBits += eLen; nBits > 0; e=e*256+a[p+i], i+=d, nBits-=8);
+ for (m = e&((1<<(-nBits))-1), e>>=(-nBits), nBits += mLen; nBits > 0; m=m*256+a[p+i], i+=d, nBits-=8);
+
+ switch (e)
+ {
+ case 0:
+ // Zero, or denormalized number
+ e = 1-eBias;
+ break;
+ case eMax:
+ // NaN, or +/-Infinity
+ return m?NaN:((s?-1:1)*Infinity);
+ default:
+ // Normalized number
+ m = m + Math.pow(2, mLen);
+ e = e - eBias;
+ break;
+ }
+ return (s?-1:1) * m * Math.pow(2, e-mLen);
+ };
+ m._En754 = function (a, p, v)
+ {
+ var s, e, m, i, d, c, mLen, eLen, eBias, eMax;
+ mLen = el.mLen, eLen = el.len*8-el.mLen-1, eMax = (1<<eLen)-1, eBias = eMax>>1;
+
+ s = v<0?1:0;
+ v = Math.abs(v);
+ if (isNaN(v) || (v == Infinity))
+ {
+ m = isNaN(v)?1:0;
+ e = eMax;
+ }
+ else
+ {
+ e = Math.floor(Math.log(v)/Math.LN2); // Calculate log2 of the value
+ if (v*(c = Math.pow(2, -e)) < 1) { e--; c*=2; } // Math.log() isn't 100% reliable
+
+ // Round by adding 1/2 the significand's LSD
+ if (e+eBias >= 1) { v += el.rt/c; } // Normalized: mLen significand digits
+ else { v += el.rt*Math.pow(2, 1-eBias); } // Denormalized: <= mLen significand digits
+ if (v*c >= 2) { e++; c/=2; } // Rounding can increment the exponent
+
+ if (e+eBias >= eMax)
+ {
+ // Overflow
+ m = 0;
+ e = eMax;
+ }
+ else if (e+eBias >= 1)
+ {
+ // Normalized - term order matters, as Math.pow(2, 52-e) and v*Math.pow(2, 52) can overflow
+ m = (v*c-1)*Math.pow(2, mLen);
+ e = e + eBias;
+ }
+ else
+ {
+ // Denormalized - also catches the '0' case, somewhat by chance
+ m = v*Math.pow(2, eBias-1)*Math.pow(2, mLen);
+ e = 0;
+ }
+ }
+
+ for (i = bBE?(el.len-1):0, d=bBE?-1:1; mLen >= 8; a[p+i]=m&0xff, i+=d, m/=256, mLen-=8);
+ for (e=(e<<mLen)|m, eLen+=mLen; eLen > 0; a[p+i]=e&0xff, i+=d, e/=256, eLen-=8);
+ a[p+i-d] |= s*128;
+ };
+
+
+ // Class data
+ m._sPattern = '(\\d+)?([AxcbBhHsfdiIlL])';
+ m._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};
+ m._elLut = { 'A': {en:m._EnArray, de:m._DeArray},
+ 's': {en:m._EnString, de:m._DeString},
+ 'c': {en:m._EnChar, de:m._DeChar},
+ 'b': {en:m._EnInt, de:m._DeInt, len:1, bSigned:true, min:-Math.pow(2, 7), max:Math.pow(2, 7)-1},
+ 'B': {en:m._EnInt, de:m._DeInt, len:1, bSigned:false, min:0, max:Math.pow(2, 8)-1},
+ 'h': {en:m._EnInt, de:m._DeInt, len:2, bSigned:true, min:-Math.pow(2, 15), max:Math.pow(2, 15)-1},
+ 'H': {en:m._EnInt, de:m._DeInt, len:2, bSigned:false, min:0, max:Math.pow(2, 16)-1},
+ 'i': {en:m._EnInt, de:m._DeInt, len:4, bSigned:true, min:-Math.pow(2, 31), max:Math.pow(2, 31)-1},
+ 'I': {en:m._EnInt, de:m._DeInt, len:4, bSigned:false, min:0, max:Math.pow(2, 32)-1},
+ 'l': {en:m._EnInt, de:m._DeInt, len:4, bSigned:true, min:-Math.pow(2, 31), max:Math.pow(2, 31)-1},
+ 'L': {en:m._EnInt, de:m._DeInt, len:4, bSigned:false, min:0, max:Math.pow(2, 32)-1},
+ 'f': {en:m._En754, de:m._De754, len:4, mLen:23, rt:Math.pow(2, -24)-Math.pow(2, -77)},
+ 'd': {en:m._En754, de:m._De754, len:8, mLen:52, rt:0}};
+
+ // Unpack a series of n elements of size s from array a at offset p with fxn
+ m._UnpackSeries = function (n, s, a, p)
+ {
+ for (var fxn = el.de, rv = [], i = 0; i < n; rv.push(fxn(a, p+i*s)), i++);
+ return rv;
+ };
+
+ // Pack a series of n elements of size s from array v at offset i to array a at offset p with fxn
+ m._PackSeries = function (n, s, a, p, v, i)
+ {
+ for (var fxn = el.en, o = 0; o < n; fxn(a, p+o*s, v[i+o]), o++);
+ };
+
+ // Unpack the octet array a, beginning at offset p, according to the fmt string
+ m.Unpack = function (fmt, a, p)
+ {
+ // Set the private bBE flag based on the format string - assume big-endianness
+ bBE = (fmt.charAt(0) != '<');
+
+ p = p?p:0;
+ var re = new RegExp(this._sPattern, 'g'), m, n, s, rv = [];
+ while (m = re.exec(fmt))
+ {
+ n = ((m[1]==undefined)||(m[1]==''))?1:parseInt(m[1]);
+ s = this._lenLut[m[2]];
+ if ((p + n*s) > a.length)
+ {
+ return undefined;
+ }
+ switch (m[2])
+ {
+ case 'A': case 's':
+ rv.push(this._elLut[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':
+ el = this._elLut[m[2]];
+ rv.push(this._UnpackSeries(n, s, a, p));
+ break;
+ }
+ p += n*s;
+ }
+ return Array.prototype.concat.apply([], rv);
+ };
+
+ // Pack the supplied values into the octet array a, beginning at offset p, according to the fmt string
+ m.PackTo = function (fmt, a, p, values)
+ {
+ // Set the private bBE flag based on the format string - assume big-endianness
+ bBE = (fmt.charAt(0) != '<');
+
+ var re = new RegExp(this._sPattern, 'g'), m, n, s, i = 0, j;
+ while (m = re.exec(fmt))
+ {
+ n = ((m[1]==undefined)||(m[1]==''))?1:parseInt(m[1]);
+ 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._elLut[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':
+ el = this._elLut[m[2]];
+ if ((i + n) > values.length) { return false; }
+ this._PackSeries(n, s, a, p, values, i);
+ i += n;
+ break;
+ case 'x':
+ for (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
+ m.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
+ m.CalcLength = function (fmt)
+ {
+ var re = new RegExp(this._sPattern, 'g'), m, sum = 0;
+ while (m = re.exec(fmt))
+ {
+ sum += (((m[1]==undefined)||(m[1]==''))?1:parseInt(m[1])) * this._lenLut[m[2]];
+ }
+ return sum;
+ };
+}();
26 docs/LICENSE
@@ -0,0 +1,26 @@
+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.
126 docs/README
@@ -0,0 +1,126 @@
+Disclaimer: The jspack module and documentation are essentially ports of the
+Python struct module and documentation, with such changes as were necessary.
+If any Python people are miffed that I've ripped off their docs, let me know,
+and I'll gladly revise them.
+
+This module performs conversions between JavaScript values and C structs
+represented as octet arrays (i.e. JavaScript arrays of integral numbers
+between 0 and 255, inclusive). It uses format strings (explained below) as
+compact descriptions of the layout of the C structs and the intended conversion
+to/from JavaScript values. This can be used to handle binary data stored in
+files, or received from network connections or other sources.
+
+
+The module defines the following functions:
+
+ Unpack(fmt, a, p)
+ Return an array containing values unpacked from the octet array a,
+ beginning at position p, according to the supplied format string. If there
+ are more octets in a than required by the format string, the excess is
+ ignored. If there are fewer octets than required, Unpack() will return
+ undefined. If no value is supplied for the p argument, zero is assumed.
+
+ PackTo(fmt, a, p, values)
+ Pack and store the values array into the supplied octet array a, beginning
+ at position p. If there are more values supplied than are specified in the
+ format string, the excess is ignored. If there are fewer values supplied,
+ PackTo() will return false. If there is insufficient space in a to store
+ the packed values, PackTo() will return false. On success, PackTo() returns
+ the a argument. If any value is of an inappropriate type, the results are
+ undefined.
+
+ Pack(fmt, values)
+ Return an octet array containing the packed values array. If there are
+ more values supplied than are specified in the format string, the excess is
+ ignored. If there are fewer values supplied, Pack() will return false. If
+ any value is of an inappropriate type, the results are undefined.
+
+ CalcLength(fmt)
+ Return the number of octets required to store the given format string.
+
+
+Format characters have the following meanings; the conversion between C and
+JavaScript values should be obvious given their types:
+
+ Format | C Type | JavaScript Type | Size (octets) | Notes
+ -------------------------------------------------------------------
+ A | char[] | Array | Length | (1)
+ x | pad byte | N/A | 1 |
+ c | char | string (length 1) | 1 | (2)
+ b | signed char | number | 1 | (3)
+ B | unsigned char | number | 1 | (3)
+ h | signed short | number | 2 | (3)
+ H | unsigned short | number | 2 | (3)
+ i | signed long | number | 4 | (3)
+ I | unsigned long | number | 4 | (3)
+ l | signed long | number | 4 | (3)
+ L | unsigned long | number | 4 | (3)
+ s | char[] | string | Length | (2)
+ f | float | number | 4 | (4)
+ d | double | number | 8 | (5)
+
+Notes:
+
+ (1) The "A" code simply returns a slice of the source octet array. This is
+ primarily useful when a data structure contains bytes which are subject to
+ multiple intepretations (e.g. unions), and the data structure is being
+ decoded in multiple passes.
+
+ (2) The "c" and "s" codes handle strings with codepoints between 0 and 255,
+ inclusive. The data are not bounds-checked, so strings containing characters
+ with codepoints outside this range will encode to "octet" arrays that contain
+ values outside the range of an octet. Furthermore, since these codes decode
+ octet arrays by assuming the octets represent UNICODE codepoints, they may
+ not "correctly" decode bytes in the range 128-255, since that range is subject
+ to multiple interpretations. Caveat coder!
+
+ (3) The 8 "integer" codes clip their encoded values to the minima and maxmima
+ of their respective types: If you invoke Struct.Pack('b', [-129]), for
+ instance, the result will be [128], which is the octet encoding of -128,
+ which is the minima of a signed char. Similarly, Struct.Pack('h', [-32769])
+ returns [128, 0]. Fractions are truncated.
+
+ (4) Since JavaScript doesn't natively support 32-bit floats, whenever a float
+ is stored, the source JavaScript number must be rounded. This module applies
+ correct rounding during this process. Numbers with magnitude greater than or
+ equal to 2**128-2**103 round to either positive or negative Infinity. The
+ rounding algorithm assumes that JavsScript is using exactly 64 bits of
+ floating point precision; 128-bit floating point will result in subtle errors.
+
+ (5) This module assumes that JavaScript is using 64 bits of floating point
+ precision, so the "d" code performs no rounding. 128-bit floating point will
+ cause the "d" code to simply truncate significands to 52 bits.
+
+A format character may be preceded by an integral repeat count. For example,
+the format string "4h" means exactly the same thing as "hhhh".
+
+Whitespace characters between formats are ignored; a count and its format must
+not be separated by whitespace, however.
+
+For the "A" format character, the count is interpreted as the size of the
+array, not a repeat count as for the other format characters; for example, "10A"
+means a single 10-octet array. When packing, the Array is truncated or padded
+with 0 bytes as appropriate to make it conform to the specified length. When
+unpacking, the resulting Array always has exactly the specified number of bytes.
+As a special case, "0A" means a single, empty Array.
+
+For the "s" format character, the count is interpreted as the size of the
+string, not a repeat count as for the other format characters; for example,
+"10s" means a single 10-byte string, while "10c" means 10 characters. When
+packing, the string is truncated or padded with 0 bytes as appropriate to make
+it conform to the specified length. When unpacking, the resulting string always
+has exactly the specified number of bytes. As a special case, "0s" means a
+single, empty string (while "0c" means 0 characters).
+
+
+By default, C numbers are represented in network (or big-endian) byte order.
+Alternatively, the first character of the format string can be used to indicate
+byte order of the packed data, according to the following table:
+
+ Character | Byte Order
+ ----------------------------------
+ < | little-endian
+ > | big-endian
+ ! | network (= big-endian)
+
+If the first character is not one of these, "!" is assumed.

0 comments on commit 1591439

Please sign in to comment.