aboutsummaryrefslogtreecommitdiff
path: root/php/phpgats2.php
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--php/phpgats2.php471
1 files changed, 471 insertions, 0 deletions
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 @@
1<?php
2
3/** @cond */
4if( !defined("_phpgats2_defined") )
5{
6define("_phpgats2_defined",1);
7/** @endcond */
8
9/** @mainpage phpgats.php PHP Gats Library
10 * @section Notes
11 *
12 * This library requires that php be built with the The GNU Multiple Precision Arithmetic Library (GMP).
13 *
14 * If array_values($a)===$a, the object will be encoded as a list, otherwise, it will be encoded as a dictionary.
15 *
16 * If an integer is > int32_max, it will be delivered as a string.
17 *
18 * If a string is encodable as an integer without loosing data, it will be.
19 *
20 * @section Usage
21 *
22 * function phpgats2_readGats( $str_data (string) );
23 * returns mixed
24 *
25 * function phpgats2_writeGats( $elem (mixed) );
26 * returns binary encoded gats string
27 *
28 */
29
30/**
31 * Call this function to generate a binary gats stream from the given php object
32 * @param $elem (mixed) the gats element object from which to generate a binary blob
33 * @returns (string) binary gats data
34 */
35function phpgats2_writeGats( $elem )
36{
37 $str_out = _phpgats2__write( $elem );
38 $str_out = "\x01" . pack( "N", strlen($str_out)+5 ) . $str_out;
39 return $str_out;
40}
41
42/**
43 * Call this function to parse a gats binary string into a php type
44 * @param $str_data (string) the binary gats data to be parsed
45 * @returns (mixed)
46 */
47function phpgats2_readGats( $str_data )
48{
49 //print "parsing\n";
50 $offset = 0;
51 $data_size = strlen( $str_data );
52 if( $data_size < 5 )
53 {
54 throw new Exception( "invalid size (< 5)\n" );
55 return false;
56 }
57 if( ord($str_data) != 1 ) //version
58 {
59 throw new Exception( "invalid gats version" );
60 return false;
61 }
62 $size = "" . $str_data[1] . $str_data[2] . $str_data[3] . $str_data[4];
63 $size = unpack( "Nsize", $size );
64 $size = $size["size"];
65 if( $data_size < $size )
66 {
67 throw new Exception( "Not enough data" );
68 return false;
69 }
70 $offset+=5;
71 return _phpgats2_parseMaster( $str_data, $offset );
72}
73
74/* --- STOP READING --- Below are internal functions you shouldn't call --- */
75
76/**
77 * Write a gats packed integer
78 * @param $iIn (either a string representing a number, a gmp_int, or an int) The integer to be converted
79 * @returns (string) packed int binary data
80 */
81//TODO::Make this use bcmath
82function _phpgats2_packInt( $iIn )
83{
84 $ret = "";
85 if( gmp_cmp( $iIn, 0 ) < 0 )
86 {
87 $iIn = gmp_mul( $iIn, -1 );
88 $b = gmp_intval( gmp_and( $iIn, 0x3f ) );
89 if( gmp_cmp( $iIn, $b ) > 0 )
90 $b |= 0x80 | 0x40;
91 else
92 $b |= 0x40;
93 }
94 else
95 {
96 $b = gmp_intval( gmp_and( $iIn, 0x3f ) );
97 if( gmp_cmp( $iIn, $b ) > 0 )
98 $b |= 0x80;
99 }
100
101 $ret .= chr( $b );
102 $iIn = gmp_div( $iIn, 64 );
103
104 while( gmp_cmp( $iIn, 0 ) > 0 )
105 {
106 $b = gmp_intval( gmp_and( $iIn, 0x7f ) );
107 if( gmp_cmp( $iIn, $b ) > 0 )
108 $b |= 0x80;
109 $ret .= chr( $b );
110 $iIn = gmp_div( $iIn, 128 );
111 }
112
113 return $ret;
114}
115
116/**
117 * Read a gats packed integer into a gmp_int
118 * @param $sIn (string) the input stream
119 * @param &$pos (integer) the current position in the input stream (will be modified)
120 * @returns (gmp_int) the integer
121 */
122//TODO::Make this use bcmath
123function _phpgats2_unpackInt( $sIn, &$pos )
124{
125 $neg = false;
126
127 $b = ord($sIn[$pos++]);
128 if( ($b&0x40) == 0x40 )
129 $neg = true;
130 $iOut = gmp_init( $b&0x3f );
131 $mult = gmp_init( 64 );
132 while( ($b&0x80) )
133 {
134 $b = ord($sIn[$pos++]);
135 $iOut = gmp_or( $iOut, gmp_mul( $b&0x7f, $mult ) );
136 $mult = gmp_mul( $mult, 128 );
137 }
138 if( $neg == true )
139 $iOut = gmp_mul( $iOut, -1 );
140
141 return $iOut;
142}
143
144function _phpgats2_writeBoolean( $b )
145{
146 return ($b==true)?"1":"0";
147}
148
149function _phpgats2_writeString( $s )
150{
151 $s = (string)$s;
152 return 's' . _phpgats2_packInt(strlen($s)) . $s;
153}
154
155function _phpgats2_packInteger( $i )
156{
157 $i = (string)$i;
158 return "i" . _phpgats2_packInt(gmp_init(bcmul($i,'1',0)));
159}
160
161function _phpgats2_writeFloat( $f )
162{
163 $f = $f+0.0;
164 if( $f == 0.0 )
165 {
166 return 'Fz';
167 }
168 else if( is_nan( $f ) )
169 {
170 return 'Fn';
171 }
172 else if( is_infinite( $f ) )
173 {
174 if( $f < 0.0 )
175 return 'FI';
176 else
177 return 'Fi';
178 }
179 else
180 {
181 $e = $f;
182 $neg = false;
183 if( $e < 0.0 )
184 {
185 $e = -$e;
186 $neg = true;
187 }
188 $iScale = (int)(floor(log($e)/log(256.0)));
189 $e = $e/pow(256.0, $iScale);
190 $s = chr((int)($e));
191 $e = $e - (int)($e);
192 for( $j = 0; $j < 150 && $e > 0.0; $j++ )
193 {
194 $e = $e * 256.0;
195 $s .= chr((int)($e));
196 $e -= (int)($e);
197 }
198 $ilen = strlen($s);
199 if( $neg ) $ilen = -$ilen;
200 return "f" . _phpgats2_packInt($ilen) . $s .
201 _phpgats2_packInt($iScale);
202 }
203}
204
205function _phpgats2_writeList( $l )
206{
207 $elems = array_values((array)$l);
208
209 $s_out = "l";
210 foreach( $elems as $val )
211 {
212 $s_out .= _phpgats2__write( $val );
213 }
214 $s_out .= "e";
215 return $s_out;
216}
217
218function _phpgats2_writeDictionary( $d )
219{
220 $elems = (array) $d;
221 $s_out = "d";
222 foreach( $elems as $key => $val )
223 {
224 $s_out .= _phpgats2_writeString( $key );
225 $s_out .= _phpgats2__write( $val );
226 }
227 $s_out .= "e";
228 return $s_out;
229}
230
231function _phpgats2__write( $unknown )
232{
233 if( is_bool( $unknown ) )
234 {
235 return _phpgats2_writeBoolean( $unknown );
236 }
237 else if( is_float( $unknown ) )
238 {
239 return _phpgats2_writeFloat( $unknown );
240 }
241 else if( is_array( $unknown ) )
242 {
243 if( array_values($unknown)===$unknown )
244 return _phpgats2_writeList( $unknown );
245 else
246 return _phpgats2_writeDictionary( $unknown );
247 }
248 else if( is_object( $unknown ) )
249 {
250 return _phpgats2_writeDictionary( $unknown );
251 }
252 else if( is_int( $unknown ) )
253 {
254 return _phpgats2_packInteger( $unknown );
255 }
256 else if( is_string( $unknown ) )
257 {
258 if( bcmul( $unknown, "1", 0 ) === $unknown )
259 return _phpgats2_packInteger( $unknown );
260 return _phpgats2_writeString( $unknown );
261 }
262}
263
264/**
265 * Make sure we can read a character off the input stream
266 * @param $str_data (string) the input stream
267 * @param $offset (integer) the current position in the input stream
268 */
269function _phpgats2_parseChar( $str_data, $offset )
270{
271 if($offset>strlen($str_data))
272 throw new Exception("Not enough data");
273}
274
275/**
276 * Parse a string element out of the input stream
277 * @param $str_data (string) the input stream
278 * @param &$offset (integer) the current position in the input stream (will be updated)
279 * @param $dbg (integer) the current depth (for pretty printing)
280 * @returns (string) the element read
281 */
282function _phpgats2_parseString( $str_data, &$offset, $dbg )
283{
284 $str_tmp = "";
285 $gmpSize = _phpgats2_unpackInt( $str_data, $offset );
286 if( gmp_cmp( $gmpSize, 2147483647 ) > 0 )
287 {
288 throw new Exception(
289 "size (" . gmp_strval($gmpSize) . ") > phpgats2 can handle\n");
290 }
291 $iSize = gmp_intval($gmpSize);
292 $i=0;
293 $str_tmp = "";
294 while( $i<$iSize )
295 {
296 _phpgats2_parseChar( $str_data, $offset+1 );
297 $str_tmp .= $str_data[$offset++];
298 ++$i;
299 }
300 return $str_tmp;
301}
302
303/**
304 * Parse an integer element out of the input stream
305 * @param $str_data (string) the input stream
306 * @param &$offset (integer) the current position in the input stream (will be updated)
307 * @param $dbg (integer) the current depth (for pretty printing)
308 * @returns (integer or string) the element read
309 */
310function _phpgats2_parseInteger( $str_data, &$offset, $dbg )
311{
312 $gmp = _phpgats2_unpackInt($str_data, $offset);
313 $str = gmp_strval($gmp);
314 $int = gmp_intval($gmp);
315 if( $str === ((string)(int)$int) )
316 return $int;
317 return $str;
318}
319
320/**
321 * Parse a float element out of the input stream
322 * @param $str_data (string) the input stream
323 * @param &$offset (integer) the current position in the input stream (will be updated)
324 * @param $dbg (integer) the current depth (for pretty printing)
325 * @returns (float) the element read
326 */
327function _phpgats2_parseFloat( $str_data, &$offset, $dbg )
328{
329 $str_tmp = "";
330 $iSize = gmp_intval(_phpgats2_unpackInt( $str_data, $offset ));
331 $neg = false;
332 if( $iSize < 0.0 )
333 {
334 $iSize = -$iSize;
335 $neg = true;
336 }
337 $i=0;
338 $str_tmp = "";
339 while( $i<$iSize )
340 {
341 _phpgats2_parseChar( $str_data, $offset+1 );
342 $str_tmp .= $str_data[$offset++];
343 ++$i;
344 }
345 $iScale = gmp_intval(_phpgats2_unpackInt( $str_data, $offset ));
346 $e = 0.0;
347 for( $j = $iSize-1; $j > 0; $j-- )
348 {
349 $e = ($e+ord($str_tmp[$j]))*.00390625;
350 }
351 $e = ($e+ord($str_tmp[0])) * pow( 256.0, $iScale );
352 if( $neg ) $e = -$e;
353 return $e;
354}
355
356/**
357 * Parse a list element out of the input stream
358 * @param $str_data (string) the input stream
359 * @param &$offset (integer) the current position in the input stream (will be updated)
360 * @param $dbg (integer) the current depth (for pretty printing)
361 * @returns (array) the element read
362 */
363function _phpgats2_parseList( $str_data, &$offset, $dbg )
364{
365 _phpgats2_parseChar( $str_data, $offset );
366 $c = $str_data[$offset];
367 $l_out = array();
368 while( $c != "e" )
369 {
370 $obj = _phpgats2_parseMaster( $str_data, $offset, $dbg );
371 array_push( $l_out, $obj );
372 _phpgats2_parseChar( $str_data, $offset );
373 $c = $str_data[$offset];
374 }
375 $offset++;
376 return $l_out;
377}
378
379/**
380 * Parse a dictionary element out of the input stream
381 * @param $str_data (string) the input stream
382 * @param &$offset (integer) the current position in the input stream (will be updated)
383 * @param $dbg (integer) the current depth (for pretty printing)
384 * @returns (array) the element read
385 */
386function _phpgats2_parseDictionary( $str_data, &$offset, $dbg )
387{
388 _phpgats2_parseChar( $str_data, $offset );
389 $c = $str_data[$offset];
390 $d_out = array();
391 while( $c != "e" )
392 {
393 $obj1 = _phpgats2_parseMaster( $str_data, $offset, $dbg );
394 $obj2 = _phpgats2_parseMaster( $str_data, $offset, $dbg );
395 $d_out[$obj1] = $obj2;
396 _phpgats2_parseChar( $str_data, $offset );
397 $c = $str_data[$offset];
398 }
399 $offset++;
400 return $d_out;
401}
402
403/**
404 * The internal master recursive parse function
405 * @param $str_data (string) the input stream
406 * @param &$offset (integer) the current position in the input stream (will be updated)
407 * @param $dbg (integer) the current depth (for pretty printing)
408 * @returns (mixed) the element read
409 */
410function _phpgats2_parseMaster( $str_data, &$offset, $dbg=0 )
411{
412 _phpgats2_parseChar( $str_data, $offset );
413 $c = $str_data[$offset++];
414 //for( $meme=0; $meme<$dbg; $meme++ )
415 // echo " ";
416 switch( $c )
417 {
418 case 'i':
419 //echo "int:";
420 $obj = _phpgats2_parseInteger( $str_data, $offset, $dbg );
421 //echo gmp_strval($obj->get()) . "\n";
422 return $obj;
423 break;
424 case 'l':
425 //echo "list:\n";
426 $obj = _phpgats2_parseList( $str_data, $offset, $dbg+1 );
427 return $obj;
428 break;
429 case 'd':
430 //echo "dic:\n";
431 $obj = _phpgats2_parseDictionary( $str_data, $offset, $dbg+1 );
432 return $obj;
433 break;
434 case 'f':
435 //echo "float:\n";
436 $obj = _phpgats2_parseFloat( $str_data, $offset, $dbg );
437 return $obj;
438 break;
439 case 'F':
440 _phpgats2_parseChar( $str_data, $offset );
441 switch( $str_data[$offset++] )
442 {
443 case 'Z': return -0.0;
444 case 'z': return 0.0;
445 case 'N': return -NAN;
446 case 'n': return NAN;
447 case 'I': return -INF;
448 case 'i': return INF;
449 }
450 break;
451 case '1':
452 //echo "true\n";
453 return true;
454 break;
455 case '0':
456 //echo "false\n";
457 return false;
458 break;
459 default:
460 //echo "str:";
461 $obj = _phpgats2_parseString( $str_data, $offset, $dbg );
462 //echo $obj->get() . "\n";
463 return $obj;
464 break;
465 }
466}
467
468/** @cond */
469} //defined("_phpgats2_defined");
470/** @endcond */
471