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_null( $unknown ) ) { return 'n'; } else 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; case 'n': return null; break; default: //echo "str:"; $obj = _phpgats2_parseString( $str_data, $offset, $dbg ); //echo $obj->get() . "\n"; return $obj; break; } } /** @cond */ } //defined("_phpgats2_defined"); /** @endcond */