From 50fb50176f25fe9367c0e2a08cb5536f1bb16f08 Mon Sep 17 00:00:00 2001 From: Henrique Mendonca Date: Fri, 26 Oct 2012 07:29:47 +0000 Subject: [PATCH] Thrift-1353: Switch to performance branch, get rid of BinaryParser Client: Node.js Patch: Wade Simmons add missing files and licenses git-svn-id: https://svn.apache.org/repos/asf/thrift/trunk@1402414 13f79535-47bb-0310-9956-ffa450edef68 --- lib/nodejs/lib/thrift/binary.js | 164 +++++++++++++++ lib/nodejs/lib/thrift/binary_parser.js | 274 ------------------------- lib/nodejs/test/binary.test.js | 114 ++++++++++ 3 files changed, 278 insertions(+), 274 deletions(-) create mode 100644 lib/nodejs/lib/thrift/binary.js delete mode 100644 lib/nodejs/lib/thrift/binary_parser.js create mode 100644 lib/nodejs/test/binary.test.js diff --git a/lib/nodejs/lib/thrift/binary.js b/lib/nodejs/lib/thrift/binary.js new file mode 100644 index 00000000..3be4d01b --- /dev/null +++ b/lib/nodejs/lib/thrift/binary.js @@ -0,0 +1,164 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +var POW_8 = Math.pow(2, 8) +var POW_16 = Math.pow(2, 16) +var POW_24 = Math.pow(2, 24) +var POW_32 = Math.pow(2, 32) +var POW_40 = Math.pow(2, 40) +var POW_48 = Math.pow(2, 48) +var POW_52 = Math.pow(2, 52) +var POW_1022 = Math.pow(2, 1022) + +exports.readI16 = function(buff, off) { + off = off || 0; + var v = buff[off + 1]; + v += buff[off] << 8; + if (buff[off] & 128) { + v -= POW_16; + } + return v; +} + +exports.readI32 = function(buff, off) { + off = off || 0; + var v = buff[off + 3]; + v += buff[off + 2] << 8; + v += buff[off + 1] << 16; + v += buff[off] * POW_24; + if (buff[off] & 0x80) { + v -= POW_32; + } + return v; +} + +exports.writeI16 = function(buff, v) { + buff[1] = v & 0xff; + v >>= 8; + buff[0] = v & 0xff; + return buff; +} + +exports.writeI32 = function(buff, v) { + buff[3] = v & 0xff; + v >>= 8; + buff[2] = v & 0xff; + v >>= 8; + buff[1] = v & 0xff; + v >>= 8; + buff[0] = v & 0xff; + return buff; +} + +exports.readDouble = function(buff, off) { + off = off || 0; + var signed = buff[off] & 0x80; + var e = (buff[off+1] & 0xF0) >> 4; + e += (buff[off] & 0x7F) << 4; + + var m = buff[off+7]; + m += buff[off+6] << 8; + m += buff[off+5] << 16; + m += buff[off+4] * POW_24; + m += buff[off+3] * POW_32; + m += buff[off+2] * POW_40; + m += (buff[off+1] & 0x0F) * POW_48; + + switch (e) { + case 0: + e = -1022 + break; + case 2047: + return m ? NaN : (signed ? -Infinity : Infinity); + default: + m += POW_52; + e -= 1023; + } + + if (signed) { + m *= -1; + } + + return m * Math.pow(2, e - 52); +} + +/* + * Based on code from the jspack module: + * http://code.google.com/p/jspack/ + */ +exports.writeDouble = function(buff, v) { + var m, e, c; + + buff[0] = (v < 0 ? 0x80 : 0x00) + + v = Math.abs(v) + if (v !== v) { + // NaN, use QNaN IEEE format + m = 2251799813685248; + e = 2047; + } else if (v === Infinity) { + m = 0; + e = 2047; + } else { + e = Math.floor(Math.log(v) / Math.LN2) + c = Math.pow(2, -e) + if (v * c < 1) { + e--; + c *= 2; + } + + if (e + 1023 >= 2047) + { + // Overflow + m = 0; + e = 2047; + } + else if (e + 1023 >= 1) + { + // Normalized - term order matters, as Math.pow(2, 52-e) and v*Math.pow(2, 52) can overflow + m = (v*c-1) * POW_52; + e += 1023; + } + else + { + // Denormalized - also catches the '0' case, somewhat by chance + m = (v * POW_1022) * POW_52; + e = 0; + } + } + + buff[1] = (e << 4) & 0xf0; + buff[0] |= (e >> 4) & 0x7f; + + buff[7] = m & 0xff; + m = Math.floor(m / POW_8); + buff[6] = m & 0xff; + m = Math.floor(m / POW_8); + buff[5] = m & 0xff; + m = Math.floor(m / POW_8); + buff[4] = m & 0xff; + m >>= 8; + buff[3] = m & 0xff; + m >>= 8; + buff[2] = m & 0xff; + m >>= 8; + buff[1] |= m & 0x0f; + + return buff; +} diff --git a/lib/nodejs/lib/thrift/binary_parser.js b/lib/nodejs/lib/thrift/binary_parser.js deleted file mode 100644 index 273440b7..00000000 --- a/lib/nodejs/lib/thrift/binary_parser.js +++ /dev/null @@ -1,274 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -var util = require('util'); -var chr = String.fromCharCode; - -var p = exports.BinaryParser = function( bigEndian, allowExceptions ){ - this.bigEndian = bigEndian || true; - this.allowExceptions = allowExceptions; -}; -p.bigEndian = true; - -var IBuffer = exports.BinaryParser.IBuffer = function( bigEndian, buffer ){ - this.bigEndian = bigEndian || 1; - this.buffer = []; - this.setBuffer( buffer ); -}; - -IBuffer.prototype.setBuffer = function( data ){ - if( data ){ - for( var l, i = l = data.length, b = this.buffer = new Array( l ); i; b[l - i] = data[--i] ); - this.bigEndian && b.reverse(); - } -}; - -IBuffer.prototype.hasNeededBits = function( neededBits ){ - return this.buffer.length >= -( -neededBits >> 3 ); -}; - -IBuffer.prototype.checkBuffer = function( neededBits ){ - if( !this.hasNeededBits( neededBits ) ) { - console.log("missing: " + neededBits + " " + this.buffer.length); - throw new Error( "checkBuffer::missing bytes" ); - } -}; - -IBuffer.prototype.readBits = function( start, length ){ - //shl fix: Henri Torgemane ~1996 (compressed by Jonas Raoni) - function shl( a, b ){ - for( ; 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.warn = function( msg ){ - if( this.allowExceptions ) - throw new Error( msg ); - return 1; -}; -p.decodeFloat = function( data, precisionBits, exponentBits ){ - var b = new this.IBuffer( this.bigEndian, data ); - b.checkBuffer( precisionBits + exponentBits + 1 ); - var 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; - do{ - for( var 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, forceBigEndian ){ - //console.log("decodeInt: ", data, bits, signed); - var b = new this.IBuffer( this.bigEndian||forceBigEndian, data ), x = b.readBits( 0, bits ), max = Math.pow( 2, bits ); - return signed && x >= max / 2 ? x - max : x; -}; -p.encodeFloat = function( data, precisionBits, exponentBits ){ - var bias = Math.pow( 2, exponentBits - 1 ) - 1, minExp = -bias + 1, maxExp = bias, minUnnormExp = minExp - precisionBits, - status = isNaN( n = parseFloat( data ) ) || 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]; ); - if( ( exp = bias + 1 - i ) >= minExp && exp <= maxExp ) - ++i; - else if( exp < minExp ){ - exp != bias + 1 - len && exp < minUnnormExp && this.warn( "encodeFloat::float underflow" ); - i = bias + 1 - ( exp = minExp - 1 ); - } - if( intPart || status !== 0 ){ - this.warn( intPart ? "encodeFloat::float overflow" : "encodeFloat::" + status ); - exp = maxExp + 1; - i = bias + 2; - if( status == -Infinity ) - signal = 1; - else if( 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; j = ( j + 1 ) % 8 ){ - n += ( 1 << j ) * result.charAt( --i ); - if( j == 7 ){ - r[r.length] = n; - n = 0; - } - } - if (n) { - r[r.length] = n; - } - return new Buffer( this.bigEndian ? r.reverse() : r ); -}; -p.encodeInt = function( data, bits, signed, forceBigEndian ){ - var max = Math.pow( 2, bits ); - ( data >= max || data < -( max / 2 ) ) && this.warn( "encodeInt::overflow" ) && ( data = 0 ); - data < 0 && ( data += max ); - for( var r = []; data; r[r.length] = data % 256, data = Math.floor( data / 256 ) ); - for( bits = -( -bits >> 3 ) - r.length; bits--; r[r.length] = 0 ); - return new Buffer((this.bigEndian||forceBigEndian) ? r.reverse() : r ); -}; -p.toSmall = function( data ){ return this.decodeInt( data, 8, true ); }; -p.fromSmall = function( data ){ return this.encodeInt( data, 8, true ); }; -p.toByte = function( data ){ return this.decodeInt( data, 8, false ); }; -p.fromByte = function( data ){ return this.encodeInt( data, 8, false ); }; -p.toShort = function( data ){ return this.decodeInt( data, 16, true ); }; -p.fromShort = function( data ){ return this.encodeInt( data, 16, true ); }; -p.toWord = function( data ){ return this.decodeInt( data, 16, false ); }; -p.fromWord = function( data ){ return this.encodeInt( data, 16, false ); }; -p.toInt = function( data ){ return this.decodeInt( data, 32, true ); }; -p.fromInt = function( data ){ return this.encodeInt( data, 32, true ); }; -p.toLong = function( data ){ return this.decodeInt( data, 64, true ); }; -p.fromLong = function( data ){ return this.encodeInt( data, 64, true ); }; -p.toDWord = function( data ){ return this.decodeInt( data, 32, false ); }; -p.fromDWord = function( data ){ return this.encodeInt( data, 32, false ); }; -p.toQWord = function( data ){ return this.decodeInt( data, 64, true ); }; -p.fromQWord = function( data ){ return this.encodeInt( data, 64, true ); }; -p.toFloat = function( data ){ return this.decodeFloat( data, 23, 8 ); }; -p.fromFloat = function( data ){ return this.encodeFloat( data, 23, 8 ); }; -p.toDouble = function( data ){ return this.decodeFloat( data, 52, 11 ); }; -p.fromDouble = function( data ){ return this.encodeFloat( data, 52, 11 ); }; - -// Factor out the encode so it can be shared by add_header and push_int32 -p.encode_int32 = function(number) { - var a, b, c, d, unsigned; - unsigned = (number < 0) ? (number + 0x100000000) : number; - a = Math.floor(unsigned / 0xffffff); - unsigned &= 0xffffff; - b = Math.floor(unsigned / 0xffff); - unsigned &= 0xffff; - c = Math.floor(unsigned / 0xff); - unsigned &= 0xff; - d = Math.floor(unsigned); - return chr(a) + chr(b) + chr(c) + chr(d); -}; - -p.encode_int64 = function(number) { - var a, b, c, d, e, f, g, h, unsigned; - unsigned = (number < 0) ? (number + 0x10000000000000000) : number; - a = Math.floor(unsigned / 0xffffffffffffff); - unsigned &= 0xffffffffffffff; - b = Math.floor(unsigned / 0xffffffffffff); - unsigned &= 0xffffffffffff; - c = Math.floor(unsigned / 0xffffffffff); - unsigned &= 0xffffffffff; - d = Math.floor(unsigned / 0xffffffff); - unsigned &= 0xffffffff; - e = Math.floor(unsigned / 0xffffff); - unsigned &= 0xffffff; - f = Math.floor(unsigned / 0xffff); - unsigned &= 0xffff; - g = Math.floor(unsigned / 0xff); - unsigned &= 0xff; - h = Math.floor(unsigned); - return chr(a) + chr(b) + chr(c) + chr(d) + chr(e) + chr(f) + chr(g) + chr(h); -}; - -/** - UTF8 methods -**/ - -// Take a raw binary string and return a utf8 string -p.decode_utf8 = function(a) { - var string = ""; - var i = 0; - var c = c1 = c2 = 0; - - while ( i < a.length ) { - c = a.charCodeAt(i); - if (c < 128) { - string += String.fromCharCode(c); - i++; - } else if((c > 191) && (c < 224)) { - c2 = a.charCodeAt(i+1); - string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); - i += 2; - } else { - c2 = a.charCodeAt(i+1); - c3 = a.charCodeAt(i+2); - string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); - i += 3; - } - } - return string; -}; - -// Encode a cstring correctly -p.encode_cstring = function(s) { - return unescape(encodeURIComponent(s)) + p.fromByte(0); -}; - -// Take a utf8 string and return a binary string -p.encode_utf8 = function(s) { - var a=""; - for (var n=0; n< s.length; n++) { - var c=s.charCodeAt(n); - if (c<128) { - a += String.fromCharCode(c); - } else if ((c>127)&&(c<2048)) { - a += String.fromCharCode( (c>>6) | 192) ; - a += String.fromCharCode( (c&63) | 128); - } else { - a += String.fromCharCode( (c>>12) | 224); - a += String.fromCharCode( ((c>>6) & 63) | 128); - a += String.fromCharCode( (c&63) | 128); - } - } - return a; -}; - -p.hprint = function(s) { - for (var i=0; i