From 6b940f4404ef9b357fd725b7aa7fb24c96680abd Mon Sep 17 00:00:00 2001 From: David Date: Tue, 8 May 2012 00:45:14 +0000 Subject: david - created a phpgats library that uses native php types... it's still using gmp, though... --- php/phpgats2.php | 471 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 471 insertions(+) create mode 100644 php/phpgats2.php diff --git a/php/phpgats2.php b/php/phpgats2.php new file mode 100644 index 0000000..1e713fc --- /dev/null +++ b/php/phpgats2.php @@ -0,0 +1,471 @@ + int32_max, it will be delivered as a string. + * + * If a string is encodable as an integer without loosing data, it will be. + * + * @section Usage + * + * function phpgats2_readGats( $str_data (string) ); + * returns mixed + * + * function phpgats2_writeGats( $elem (mixed) ); + * returns binary encoded gats string + * + */ + +/** + * Call this function to generate a binary gats stream from the given php object + * @param $elem (mixed) the gats element object from which to generate a binary blob + * @returns (string) binary gats data + */ +function phpgats2_writeGats( $elem ) +{ + $str_out = _phpgats2__write( $elem ); + $str_out = "\x01" . pack( "N", strlen($str_out)+5 ) . $str_out; + return $str_out; +} + +/** + * Call this function to parse a gats binary string into a php type + * @param $str_data (string) the binary gats data to be parsed + * @returns (mixed) + */ +function phpgats2_readGats( $str_data ) +{ + //print "parsing\n"; + $offset = 0; + $data_size = strlen( $str_data ); + if( $data_size < 5 ) + { + throw new Exception( "invalid size (< 5)\n" ); + return false; + } + if( ord($str_data) != 1 ) //version + { + throw new Exception( "invalid gats version" ); + return false; + } + $size = "" . $str_data[1] . $str_data[2] . $str_data[3] . $str_data[4]; + $size = unpack( "Nsize", $size ); + $size = $size["size"]; + if( $data_size < $size ) + { + throw new Exception( "Not enough data" ); + return false; + } + $offset+=5; + return _phpgats2_parseMaster( $str_data, $offset ); +} + +/* --- STOP READING --- Below are internal functions you shouldn't call --- */ + +/** + * Write a gats packed integer + * @param $iIn (either a string representing a number, a gmp_int, or an int) The integer to be converted + * @returns (string) packed int binary data + */ +//TODO::Make this use bcmath +function _phpgats2_packInt( $iIn ) +{ + $ret = ""; + if( gmp_cmp( $iIn, 0 ) < 0 ) + { + $iIn = gmp_mul( $iIn, -1 ); + $b = gmp_intval( gmp_and( $iIn, 0x3f ) ); + if( gmp_cmp( $iIn, $b ) > 0 ) + $b |= 0x80 | 0x40; + else + $b |= 0x40; + } + else + { + $b = gmp_intval( gmp_and( $iIn, 0x3f ) ); + if( gmp_cmp( $iIn, $b ) > 0 ) + $b |= 0x80; + } + + $ret .= chr( $b ); + $iIn = gmp_div( $iIn, 64 ); + + while( gmp_cmp( $iIn, 0 ) > 0 ) + { + $b = gmp_intval( gmp_and( $iIn, 0x7f ) ); + if( gmp_cmp( $iIn, $b ) > 0 ) + $b |= 0x80; + $ret .= chr( $b ); + $iIn = gmp_div( $iIn, 128 ); + } + + return $ret; +} + +/** + * Read a gats packed integer into a gmp_int + * @param $sIn (string) the input stream + * @param &$pos (integer) the current position in the input stream (will be modified) + * @returns (gmp_int) the integer + */ +//TODO::Make this use bcmath +function _phpgats2_unpackInt( $sIn, &$pos ) +{ + $neg = false; + + $b = ord($sIn[$pos++]); + if( ($b&0x40) == 0x40 ) + $neg = true; + $iOut = gmp_init( $b&0x3f ); + $mult = gmp_init( 64 ); + while( ($b&0x80) ) + { + $b = ord($sIn[$pos++]); + $iOut = gmp_or( $iOut, gmp_mul( $b&0x7f, $mult ) ); + $mult = gmp_mul( $mult, 128 ); + } + if( $neg == true ) + $iOut = gmp_mul( $iOut, -1 ); + + return $iOut; +} + +function _phpgats2_writeBoolean( $b ) +{ + return ($b==true)?"1":"0"; +} + +function _phpgats2_writeString( $s ) +{ + $s = (string)$s; + return 's' . _phpgats2_packInt(strlen($s)) . $s; +} + +function _phpgats2_packInteger( $i ) +{ + $i = (string)$i; + return "i" . _phpgats2_packInt(gmp_init(bcmul($i,'1',0))); +} + +function _phpgats2_writeFloat( $f ) +{ + $f = $f+0.0; + if( $f == 0.0 ) + { + return 'Fz'; + } + else if( is_nan( $f ) ) + { + return 'Fn'; + } + else if( is_infinite( $f ) ) + { + if( $f < 0.0 ) + return 'FI'; + else + return 'Fi'; + } + else + { + $e = $f; + $neg = false; + if( $e < 0.0 ) + { + $e = -$e; + $neg = true; + } + $iScale = (int)(floor(log($e)/log(256.0))); + $e = $e/pow(256.0, $iScale); + $s = chr((int)($e)); + $e = $e - (int)($e); + for( $j = 0; $j < 150 && $e > 0.0; $j++ ) + { + $e = $e * 256.0; + $s .= chr((int)($e)); + $e -= (int)($e); + } + $ilen = strlen($s); + if( $neg ) $ilen = -$ilen; + return "f" . _phpgats2_packInt($ilen) . $s . + _phpgats2_packInt($iScale); + } +} + +function _phpgats2_writeList( $l ) +{ + $elems = array_values((array)$l); + + $s_out = "l"; + foreach( $elems as $val ) + { + $s_out .= _phpgats2__write( $val ); + } + $s_out .= "e"; + return $s_out; +} + +function _phpgats2_writeDictionary( $d ) +{ + $elems = (array) $d; + $s_out = "d"; + foreach( $elems as $key => $val ) + { + $s_out .= _phpgats2_writeString( $key ); + $s_out .= _phpgats2__write( $val ); + } + $s_out .= "e"; + return $s_out; +} + +function _phpgats2__write( $unknown ) +{ + if( is_bool( $unknown ) ) + { + return _phpgats2_writeBoolean( $unknown ); + } + else if( is_float( $unknown ) ) + { + return _phpgats2_writeFloat( $unknown ); + } + else if( is_array( $unknown ) ) + { + if( array_values($unknown)===$unknown ) + return _phpgats2_writeList( $unknown ); + else + return _phpgats2_writeDictionary( $unknown ); + } + else if( is_object( $unknown ) ) + { + return _phpgats2_writeDictionary( $unknown ); + } + else if( is_int( $unknown ) ) + { + return _phpgats2_packInteger( $unknown ); + } + else if( is_string( $unknown ) ) + { + if( bcmul( $unknown, "1", 0 ) === $unknown ) + return _phpgats2_packInteger( $unknown ); + return _phpgats2_writeString( $unknown ); + } +} + +/** + * Make sure we can read a character off the input stream + * @param $str_data (string) the input stream + * @param $offset (integer) the current position in the input stream + */ +function _phpgats2_parseChar( $str_data, $offset ) +{ + if($offset>strlen($str_data)) + throw new Exception("Not enough data"); +} + +/** + * Parse a string element out of the input stream + * @param $str_data (string) the input stream + * @param &$offset (integer) the current position in the input stream (will be updated) + * @param $dbg (integer) the current depth (for pretty printing) + * @returns (string) the element read + */ +function _phpgats2_parseString( $str_data, &$offset, $dbg ) +{ + $str_tmp = ""; + $gmpSize = _phpgats2_unpackInt( $str_data, $offset ); + if( gmp_cmp( $gmpSize, 2147483647 ) > 0 ) + { + throw new Exception( + "size (" . gmp_strval($gmpSize) . ") > phpgats2 can handle\n"); + } + $iSize = gmp_intval($gmpSize); + $i=0; + $str_tmp = ""; + while( $i<$iSize ) + { + _phpgats2_parseChar( $str_data, $offset+1 ); + $str_tmp .= $str_data[$offset++]; + ++$i; + } + return $str_tmp; +} + +/** + * Parse an integer element out of the input stream + * @param $str_data (string) the input stream + * @param &$offset (integer) the current position in the input stream (will be updated) + * @param $dbg (integer) the current depth (for pretty printing) + * @returns (integer or string) the element read + */ +function _phpgats2_parseInteger( $str_data, &$offset, $dbg ) +{ + $gmp = _phpgats2_unpackInt($str_data, $offset); + $str = gmp_strval($gmp); + $int = gmp_intval($gmp); + if( $str === ((string)(int)$int) ) + return $int; + return $str; +} + +/** + * Parse a float element out of the input stream + * @param $str_data (string) the input stream + * @param &$offset (integer) the current position in the input stream (will be updated) + * @param $dbg (integer) the current depth (for pretty printing) + * @returns (float) the element read + */ +function _phpgats2_parseFloat( $str_data, &$offset, $dbg ) +{ + $str_tmp = ""; + $iSize = gmp_intval(_phpgats2_unpackInt( $str_data, $offset )); + $neg = false; + if( $iSize < 0.0 ) + { + $iSize = -$iSize; + $neg = true; + } + $i=0; + $str_tmp = ""; + while( $i<$iSize ) + { + _phpgats2_parseChar( $str_data, $offset+1 ); + $str_tmp .= $str_data[$offset++]; + ++$i; + } + $iScale = gmp_intval(_phpgats2_unpackInt( $str_data, $offset )); + $e = 0.0; + for( $j = $iSize-1; $j > 0; $j-- ) + { + $e = ($e+ord($str_tmp[$j]))*.00390625; + } + $e = ($e+ord($str_tmp[0])) * pow( 256.0, $iScale ); + if( $neg ) $e = -$e; + return $e; +} + +/** + * Parse a list element out of the input stream + * @param $str_data (string) the input stream + * @param &$offset (integer) the current position in the input stream (will be updated) + * @param $dbg (integer) the current depth (for pretty printing) + * @returns (array) the element read + */ +function _phpgats2_parseList( $str_data, &$offset, $dbg ) +{ + _phpgats2_parseChar( $str_data, $offset ); + $c = $str_data[$offset]; + $l_out = array(); + while( $c != "e" ) + { + $obj = _phpgats2_parseMaster( $str_data, $offset, $dbg ); + array_push( $l_out, $obj ); + _phpgats2_parseChar( $str_data, $offset ); + $c = $str_data[$offset]; + } + $offset++; + return $l_out; +} + +/** + * Parse a dictionary element out of the input stream + * @param $str_data (string) the input stream + * @param &$offset (integer) the current position in the input stream (will be updated) + * @param $dbg (integer) the current depth (for pretty printing) + * @returns (array) the element read + */ +function _phpgats2_parseDictionary( $str_data, &$offset, $dbg ) +{ + _phpgats2_parseChar( $str_data, $offset ); + $c = $str_data[$offset]; + $d_out = array(); + while( $c != "e" ) + { + $obj1 = _phpgats2_parseMaster( $str_data, $offset, $dbg ); + $obj2 = _phpgats2_parseMaster( $str_data, $offset, $dbg ); + $d_out[$obj1] = $obj2; + _phpgats2_parseChar( $str_data, $offset ); + $c = $str_data[$offset]; + } + $offset++; + return $d_out; +} + +/** + * The internal master recursive parse function + * @param $str_data (string) the input stream + * @param &$offset (integer) the current position in the input stream (will be updated) + * @param $dbg (integer) the current depth (for pretty printing) + * @returns (mixed) the element read + */ +function _phpgats2_parseMaster( $str_data, &$offset, $dbg=0 ) +{ + _phpgats2_parseChar( $str_data, $offset ); + $c = $str_data[$offset++]; + //for( $meme=0; $meme<$dbg; $meme++ ) + // echo " "; + switch( $c ) + { + case 'i': + //echo "int:"; + $obj = _phpgats2_parseInteger( $str_data, $offset, $dbg ); + //echo gmp_strval($obj->get()) . "\n"; + return $obj; + break; + case 'l': + //echo "list:\n"; + $obj = _phpgats2_parseList( $str_data, $offset, $dbg+1 ); + return $obj; + break; + case 'd': + //echo "dic:\n"; + $obj = _phpgats2_parseDictionary( $str_data, $offset, $dbg+1 ); + return $obj; + break; + case 'f': + //echo "float:\n"; + $obj = _phpgats2_parseFloat( $str_data, $offset, $dbg ); + return $obj; + break; + case 'F': + _phpgats2_parseChar( $str_data, $offset ); + switch( $str_data[$offset++] ) + { + case 'Z': return -0.0; + case 'z': return 0.0; + case 'N': return -NAN; + case 'n': return NAN; + case 'I': return -INF; + case 'i': return INF; + } + break; + case '1': + //echo "true\n"; + return true; + break; + case '0': + //echo "false\n"; + return false; + break; + default: + //echo "str:"; + $obj = _phpgats2_parseString( $str_data, $offset, $dbg ); + //echo $obj->get() . "\n"; + return $obj; + break; + } +} + +/** @cond */ +} //defined("_phpgats2_defined"); +/** @endcond */ + -- cgit v1.2.3