1 |
|
2 |
|
3 | var chr = String.fromCharCode;
|
4 |
|
5 | var p = exports.BinaryParser = function( bigEndian, allowExceptions ){
|
6 | this.bigEndian = bigEndian;
|
7 | this.allowExceptions = allowExceptions;
|
8 | };
|
9 |
|
10 | var Buffer = exports.BinaryParser.Buffer = function( bigEndian, buffer ){
|
11 | this.bigEndian = bigEndian || 0;
|
12 | this.buffer = [];
|
13 | this.setBuffer( buffer );
|
14 | };
|
15 |
|
16 | Buffer.prototype.setBuffer = function( data ){
|
17 | if( data ){
|
18 | for( var l, i = l = data.length, b = this.buffer = new Array( l ); i; b[l - i] = data.charCodeAt( --i ) );
|
19 | this.bigEndian && b.reverse();
|
20 | }
|
21 | };
|
22 |
|
23 | Buffer.prototype.hasNeededBits = function( neededBits ){
|
24 | return this.buffer.length >= -( -neededBits >> 3 );
|
25 | };
|
26 |
|
27 | Buffer.prototype.checkBuffer = function( neededBits ){
|
28 | if( !this.hasNeededBits( neededBits ) )
|
29 | throw new Error( "checkBuffer::missing bytes" );
|
30 | };
|
31 |
|
32 | Buffer.prototype.readBits = function( start, length ){
|
33 |
|
34 | function shl( a, b ){
|
35 | for( ; b--; a = ( ( a %= 0x7fffffff + 1 ) & 0x40000000 ) == 0x40000000 ? a * 2 : ( a - 0x40000000 ) * 2 + 0x7fffffff + 1 );
|
36 | return a;
|
37 | }
|
38 | if( start < 0 || length <= 0 )
|
39 | return 0;
|
40 | this.checkBuffer( start + length );
|
41 | 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 ) );
|
42 | return sum;
|
43 | };
|
44 |
|
45 | p.warn = function( msg ){
|
46 | if( this.allowExceptions )
|
47 | throw new Error( msg );
|
48 | return 1;
|
49 | };
|
50 | p.decodeFloat = function( data, precisionBits, exponentBits ){
|
51 | var b = new this.Buffer( this.bigEndian, data );
|
52 | b.checkBuffer( precisionBits + exponentBits + 1 );
|
53 | var bias = Math.pow( 2, exponentBits - 1 ) - 1, signal = b.readBits( precisionBits + exponentBits, 1 ), exponent = b.readBits( precisionBits, exponentBits ), significand = 0,
|
54 | divisor = 2, curByte = b.buffer.length + ( -precisionBits >> 3 ) - 1;
|
55 | do{
|
56 | for( var byteValue = b.buffer[ ++curByte ], startBit = precisionBits % 8 || 8, mask = 1 << startBit; mask >>= 1; ( byteValue & mask ) && ( significand += 1 / divisor ), divisor *= 2 );
|
57 | }while( precisionBits -= startBit );
|
58 | 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 );
|
59 | };
|
60 | p.decodeInt = function( data, bits, signed, forceBigEndian ){
|
61 | var b = new this.Buffer( this.bigEndian||forceBigEndian, data ), x = b.readBits( 0, bits ), max = Math.pow( 2, bits );
|
62 | return signed && x >= max / 2 ? x - max : x;
|
63 | };
|
64 | p.encodeFloat = function( data, precisionBits, exponentBits ){
|
65 | var bias = Math.pow( 2, exponentBits - 1 ) - 1, minExp = -bias + 1, maxExp = bias, minUnnormExp = minExp - precisionBits,
|
66 | status = isNaN( n = parseFloat( data ) ) || n == -Infinity || n == +Infinity ? n : 0,
|
67 | exp = 0, len = 2 * bias + 1 + precisionBits + 3, bin = new Array( len ),
|
68 | signal = ( n = status !== 0 ? 0 : n ) < 0, n = Math.abs( n ), intPart = Math.floor( n ), floatPart = n - intPart,
|
69 | i, lastBit, rounded, j, result;
|
70 | for( i = len; i; bin[--i] = 0 );
|
71 | for( i = bias + 2; intPart && i; bin[--i] = intPart % 2, intPart = Math.floor( intPart / 2 ) );
|
72 | for( i = bias + 1; floatPart > 0 && i; ( bin[++i] = ( ( floatPart *= 2 ) >= 1 ) - 0 ) && --floatPart );
|
73 | for( i = -1; ++i < len && !bin[i]; );
|
74 | if( bin[( lastBit = precisionBits - 1 + ( i = ( exp = bias + 1 - i ) >= minExp && exp <= maxExp ? i + 1 : bias + 1 - ( exp = minExp - 1 ) ) ) + 1] ){
|
75 | if( !( rounded = bin[lastBit] ) ){
|
76 | for( j = lastBit + 2; !rounded && j < len; rounded = bin[j++] );
|
77 | }
|
78 | for( j = lastBit + 1; rounded && --j >= 0; ( bin[j] = !bin[j] - 0 ) && ( rounded = 0 ) );
|
79 | }
|
80 | for( i = i - 2 < 0 ? -1 : i - 3; ++i < len && !bin[i]; );
|
81 | if( ( exp = bias + 1 - i ) >= minExp && exp <= maxExp )
|
82 | ++i;
|
83 | else if( exp < minExp ){
|
84 | exp != bias + 1 - len && exp < minUnnormExp && this.warn( "encodeFloat::float underflow" );
|
85 | i = bias + 1 - ( exp = minExp - 1 );
|
86 | }
|
87 | if( intPart || status !== 0 ){
|
88 | this.warn( intPart ? "encodeFloat::float overflow" : "encodeFloat::" + status );
|
89 | exp = maxExp + 1;
|
90 | i = bias + 2;
|
91 | if( status == -Infinity )
|
92 | signal = 1;
|
93 | else if( isNaN( status ) )
|
94 | bin[i] = 1;
|
95 | }
|
96 | for( n = Math.abs( exp + bias ), j = exponentBits + 1, result = ""; --j; result = ( n % 2 ) + result, n = n >>= 1 );
|
97 | for( n = 0, j = 0, i = ( result = ( signal ? "1" : "0" ) + result + bin.slice( i, i + precisionBits ).join( "" ) ).length, r = []; i; j = ( j + 1 ) % 8 ){
|
98 | n += ( 1 << j ) * result.charAt( --i );
|
99 | if( j == 7 ){
|
100 | r[r.length] = String.fromCharCode( n );
|
101 | n = 0;
|
102 | }
|
103 | }
|
104 | r[r.length] = n ? String.fromCharCode( n ) : "";
|
105 | return ( this.bigEndian ? r.reverse() : r ).join( "" );
|
106 | };
|
107 | p.encodeInt = function( data, bits, signed, forceBigEndian ){
|
108 | var max = Math.pow( 2, bits );
|
109 | ( data >= max || data < -( max / 2 ) ) && this.warn( "encodeInt::overflow" ) && ( data = 0 );
|
110 | data < 0 && ( data += max );
|
111 | for( var r = []; data; r[r.length] = String.fromCharCode( data % 256 ), data = Math.floor( data / 256 ) );
|
112 | for( bits = -( -bits >> 3 ) - r.length; bits--; r[r.length] = "\0" );
|
113 | return ( (this.bigEndian||forceBigEndian) ? r.reverse() : r ).join( "" );
|
114 | };
|
115 | p.toSmall = function( data ){ return this.decodeInt( data, 8, true ); };
|
116 | p.fromSmall = function( data ){ return this.encodeInt( data, 8, true ); };
|
117 | p.toByte = function( data ){ return this.decodeInt( data, 8, false ); };
|
118 | p.fromByte = function( data ){ return this.encodeInt( data, 8, false ); };
|
119 | p.toShort = function( data ){ return this.decodeInt( data, 16, true ); };
|
120 | p.fromShort = function( data ){ return this.encodeInt( data, 16, true ); };
|
121 | p.toWord = function( data ){ return this.decodeInt( data, 16, false ); };
|
122 | p.fromWord = function( data ){ return this.encodeInt( data, 16, false ); };
|
123 | p.toInt = function( data ){ return this.decodeInt( data, 32, true ); };
|
124 | p.fromInt = function( data ){ return this.encodeInt( data, 32, true ); };
|
125 | p.toLong = function( data ){ return this.decodeInt( data, 64, true ); };
|
126 | p.fromLong = function( data ){ return this.encodeInt( data, 64, true ); };
|
127 | p.toDWord = function( data ){ return this.decodeInt( data, 32, false ); };
|
128 | p.fromDWord = function( data ){ return this.encodeInt( data, 32, false ); };
|
129 | p.toQWord = function( data ){ return this.decodeInt( data, 64, true ); };
|
130 | p.fromQWord = function( data ){ return this.encodeInt( data, 64, true ); };
|
131 | p.toFloat = function( data ){ return this.decodeFloat( data, 23, 8 ); };
|
132 | p.fromFloat = function( data ){ return this.encodeFloat( data, 23, 8 ); };
|
133 | p.toDouble = function( data ){ return this.decodeFloat( data, 52, 11 ); };
|
134 | p.fromDouble = function( data ){ return this.encodeFloat( data, 52, 11 ); };
|
135 |
|
136 | // Factor out the encode so it can be shared by add_header and push_int32
|
137 | p.encode_int32 = function(number) {
|
138 | var a, b, c, d, unsigned;
|
139 | unsigned = (number < 0) ? (number + 0x100000000) : number;
|
140 | a = Math.floor(unsigned / 0xffffff);
|
141 | unsigned &= 0xffffff;
|
142 | b = Math.floor(unsigned / 0xffff);
|
143 | unsigned &= 0xffff;
|
144 | c = Math.floor(unsigned / 0xff);
|
145 | unsigned &= 0xff;
|
146 | d = Math.floor(unsigned);
|
147 | return chr(a) + chr(b) + chr(c) + chr(d);
|
148 | };
|
149 |
|
150 | p.encode_int64 = function(number) {
|
151 | var a, b, c, d, e, f, g, h, unsigned;
|
152 | unsigned = (number < 0) ? (number + 0x10000000000000000) : number;
|
153 | a = Math.floor(unsigned / 0xffffffffffffff);
|
154 | unsigned &= 0xffffffffffffff;
|
155 | b = Math.floor(unsigned / 0xffffffffffff);
|
156 | unsigned &= 0xffffffffffff;
|
157 | c = Math.floor(unsigned / 0xffffffffff);
|
158 | unsigned &= 0xffffffffff;
|
159 | d = Math.floor(unsigned / 0xffffffff);
|
160 | unsigned &= 0xffffffff;
|
161 | e = Math.floor(unsigned / 0xffffff);
|
162 | unsigned &= 0xffffff;
|
163 | f = Math.floor(unsigned / 0xffff);
|
164 | unsigned &= 0xffff;
|
165 | g = Math.floor(unsigned / 0xff);
|
166 | unsigned &= 0xff;
|
167 | h = Math.floor(unsigned);
|
168 | return chr(a) + chr(b) + chr(c) + chr(d) + chr(e) + chr(f) + chr(g) + chr(h);
|
169 | };
|
170 |
|
171 | /**
|
172 | UTF8 methods
|
173 | **/
|
174 |
|
175 | // Take a raw binary string and return a utf8 string
|
176 | p.decode_utf8 = function(a) {
|
177 | var string = "";
|
178 | var i = 0;
|
179 | var c = c1 = c2 = 0;
|
180 |
|
181 | while ( i < a.length ) {
|
182 | c = a.charCodeAt(i);
|
183 | if (c < 128) {
|
184 | string += String.fromCharCode(c);
|
185 | i++;
|
186 | } else if((c > 191) && (c < 224)) {
|
187 | c2 = a.charCodeAt(i+1);
|
188 | string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
|
189 | i += 2;
|
190 | } else {
|
191 | c2 = a.charCodeAt(i+1);
|
192 | c3 = a.charCodeAt(i+2);
|
193 | string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
|
194 | i += 3;
|
195 | }
|
196 | }
|
197 | return string;
|
198 | };
|
199 |
|
200 | // Encode a cstring correctly
|
201 | p.encode_cstring = function(s) {
|
202 | return unescape(encodeURIComponent(s)) + p.fromByte(0);
|
203 | };
|
204 |
|
205 | // Take a utf8 string and return a binary string
|
206 | p.encode_utf8 = function(s) {
|
207 | var a="";
|
208 | for (var n=0; n< s.length; n++) {
|
209 | var c=s.charCodeAt(n);
|
210 | if (c<128) {
|
211 | a += String.fromCharCode(c);
|
212 | } else if ((c>127)&&(c<2048)) {
|
213 | a += String.fromCharCode( (c>>6) | 192) ;
|
214 | a += String.fromCharCode( (c&63) | 128);
|
215 | } else {
|
216 | a += String.fromCharCode( (c>>12) | 224);
|
217 | a += String.fromCharCode( ((c>>6) & 63) | 128);
|
218 | a += String.fromCharCode( (c&63) | 128);
|
219 | }
|
220 | }
|
221 | return a;
|
222 | };
|
223 |
|
224 | p.pprint = function(s) {
|
225 | var util = require('util');
|
226 |
|
227 | for (var i=0; i<s.length; i++) {
|
228 | if (s.charCodeAt(i)<32) {util.puts(s.charCodeAt(i)+' : ');}
|
229 | else {util.puts(s.charCodeAt(i)+' : '+ s.charAt(i));}
|
230 | }
|
231 | };
|
232 |
|
233 | p.hprint = function(s) {
|
234 | var util = require('util');
|
235 |
|
236 | for (var i=0; i<s.length; i++) {
|
237 | if (s.charCodeAt(i)<32) {util.puts(s.charCodeAt(i)+' : ');}
|
238 | else {util.puts(s.charCodeAt(i).toString(16)+' : '+ s.charAt(i));}
|
239 | }
|
240 | };
|
241 |
|
242 | p.hex = function(s) {
|
243 | var util = require('util');
|
244 | var string = ''
|
245 |
|
246 | for (var i=0; i<s.length; i++) {
|
247 | var c = s.charCodeAt(i).toString(16);
|
248 | c = c.length == 1 ? "0" + c : c;
|
249 | string = string + c;
|
250 | }
|
251 |
|
252 | return string;
|
253 | };
|