UNPKG

10.4 kBJavaScriptView Raw
1//+ Jonas Raoni Soares Silva
2//@ http://jsfromhell.com/classes/binary-parser [v1.0]
3var chr = String.fromCharCode;
4
5var p = exports.BinaryParser = function( bigEndian, allowExceptions ){
6 this.bigEndian = bigEndian;
7 this.allowExceptions = allowExceptions;
8};
9
10var Buffer = exports.BinaryParser.Buffer = function( bigEndian, buffer ){
11 this.bigEndian = bigEndian || 0;
12 this.buffer = [];
13 this.setBuffer( buffer );
14};
15
16Buffer.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
23Buffer.prototype.hasNeededBits = function( neededBits ){
24 return this.buffer.length >= -( -neededBits >> 3 );
25};
26
27Buffer.prototype.checkBuffer = function( neededBits ){
28 if( !this.hasNeededBits( neededBits ) )
29 throw new Error( "checkBuffer::missing bytes" );
30};
31
32Buffer.prototype.readBits = function( start, length ){
33 //shl fix: Henri Torgemane ~1996 (compressed by Jonas Raoni)
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
45p.warn = function( msg ){
46 if( this.allowExceptions )
47 throw new Error( msg );
48 return 1;
49};
50p.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};
60p.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};
64p.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};
107p.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};
115p.toSmall = function( data ){ return this.decodeInt( data, 8, true ); };
116p.fromSmall = function( data ){ return this.encodeInt( data, 8, true ); };
117p.toByte = function( data ){ return this.decodeInt( data, 8, false ); };
118p.fromByte = function( data ){ return this.encodeInt( data, 8, false ); };
119p.toShort = function( data ){ return this.decodeInt( data, 16, true ); };
120p.fromShort = function( data ){ return this.encodeInt( data, 16, true ); };
121p.toWord = function( data ){ return this.decodeInt( data, 16, false ); };
122p.fromWord = function( data ){ return this.encodeInt( data, 16, false ); };
123p.toInt = function( data ){ return this.decodeInt( data, 32, true ); };
124p.fromInt = function( data ){ return this.encodeInt( data, 32, true ); };
125p.toLong = function( data ){ return this.decodeInt( data, 64, true ); };
126p.fromLong = function( data ){ return this.encodeInt( data, 64, true ); };
127p.toDWord = function( data ){ return this.decodeInt( data, 32, false ); };
128p.fromDWord = function( data ){ return this.encodeInt( data, 32, false ); };
129p.toQWord = function( data ){ return this.decodeInt( data, 64, true ); };
130p.fromQWord = function( data ){ return this.encodeInt( data, 64, true ); };
131p.toFloat = function( data ){ return this.decodeFloat( data, 23, 8 ); };
132p.fromFloat = function( data ){ return this.encodeFloat( data, 23, 8 ); };
133p.toDouble = function( data ){ return this.decodeFloat( data, 52, 11 ); };
134p.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
137p.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
150p.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
176p.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
201p.encode_cstring = function(s) {
202 return unescape(encodeURIComponent(s)) + p.fromByte(0);
203};
204
205// Take a utf8 string and return a binary string
206p.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
224p.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
233p.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
242p.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};