MediaWiki  REL1_24
MWMessagePack.php
Go to the documentation of this file.
00001 <?php
00035 class MWMessagePack {
00037     public static $bigendian = null;
00038 
00050     public static function pack( $value ) {
00051         if ( self::$bigendian === null ) {
00052             self::$bigendian = pack( 'S', 1 ) === pack( 'n', 1 );
00053         }
00054 
00055         switch ( gettype( $value ) ) {
00056         case 'NULL':
00057             return "\xC0";
00058 
00059         case 'boolean':
00060             return $value ? "\xC3" : "\xC2";
00061 
00062         case 'double':
00063         case 'float':
00064             return self::$bigendian
00065                 ? "\xCB" . pack( 'd', $value )
00066                 : "\xCB" . strrev( pack( 'd', $value ) );
00067 
00068         case 'string':
00069             $length = strlen( $value );
00070             if ( $length < 32 ) {
00071                 return pack( 'Ca*', 0xA0 | $length, $value );
00072             } elseif ( $length <= 0xFFFF ) {
00073                 return pack( 'Cna*', 0xDA, $length, $value );
00074             } elseif ( $length <= 0xFFFFFFFF ) {
00075                 return pack( 'CNa*', 0xDB, $length, $value );
00076             }
00077             throw new InvalidArgumentException( __METHOD__
00078                 . ": string too long (length: $length; max: 4294967295)" );
00079 
00080         case 'integer':
00081             if ( $value >= 0 ) {
00082                 if ( $value <= 0x7F ) {
00083                     // positive fixnum
00084                     return chr( $value );
00085                 }
00086                 if ( $value <= 0xFF ) {
00087                     // uint8
00088                     return pack( 'CC', 0xCC, $value );
00089                 }
00090                 if ( $value <= 0xFFFF ) {
00091                     // uint16
00092                     return pack( 'Cn', 0xCD, $value );
00093                 }
00094                 if ( $value <= 0xFFFFFFFF ) {
00095                     // uint32
00096                     return pack( 'CN', 0xCE, $value );
00097                 }
00098                 if ( $value <= 0xFFFFFFFFFFFFFFFF ) {
00099                     // uint64
00100                     $hi = ( $value & 0xFFFFFFFF00000000 ) >> 32;
00101                     $lo = $value & 0xFFFFFFFF;
00102                     return self::$bigendian
00103                         ? pack( 'CNN', 0xCF, $lo, $hi )
00104                         : pack( 'CNN', 0xCF, $hi, $lo );
00105                 }
00106             } else {
00107                 if ( $value >= -32 ) {
00108                     // negative fixnum
00109                     return pack( 'c', $value );
00110                 }
00111                 if ( $value >= -0x80 ) {
00112                     // int8
00113                     return pack( 'Cc', 0xD0, $value );
00114                 }
00115                 if ( $value >= -0x8000 ) {
00116                     // int16
00117                     $p = pack( 's', $value );
00118                     return self::$bigendian
00119                         ? pack( 'Ca2', 0xD1, $p )
00120                         : pack( 'Ca2', 0xD1, strrev( $p ) );
00121                 }
00122                 if ( $value >= -0x80000000 ) {
00123                     // int32
00124                     $p = pack( 'l', $value );
00125                     return self::$bigendian
00126                         ? pack( 'Ca4', 0xD2, $p )
00127                         : pack( 'Ca4', 0xD2, strrev( $p ) );
00128                 }
00129                 if ( $value >= -0x8000000000000000 ) {
00130                     // int64
00131                     // pack() does not support 64-bit ints either so pack into two 32-bits
00132                     $p1 = pack( 'l', $value & 0xFFFFFFFF );
00133                     $p2 = pack( 'l', ( $value >> 32 ) & 0xFFFFFFFF );
00134                     return self::$bigendian
00135                         ? pack( 'Ca4a4', 0xD3, $p1, $p2 )
00136                         : pack( 'Ca4a4', 0xD3, strrev( $p2 ), strrev( $p1 ) );
00137                 }
00138             }
00139             throw new InvalidArgumentException( __METHOD__ . ": invalid integer '$value'" );
00140 
00141         case 'array':
00142             $buffer = '';
00143             $length = count( $value );
00144             if ( $length > 0xFFFFFFFF ) {
00145                 throw new InvalidArgumentException( __METHOD__
00146                     . ": array too long (length: $length, max: 4294967295)" );
00147             }
00148 
00149             $index = 0;
00150             foreach ( $value as $k => $v ) {
00151                 if ( $index !== $k || $index === $length ) {
00152                     break;
00153                 } else {
00154                     $index++;
00155                 }
00156             }
00157             $associative = $index !== $length;
00158 
00159             if ( $associative ) {
00160                 if ( $length < 16 ) {
00161                     $buffer .= pack( 'C', 0x80 | $length );
00162                 } elseif ( $length <= 0xFFFF ) {
00163                     $buffer .= pack( 'Cn', 0xDE, $length );
00164                 } else {
00165                     $buffer .= pack( 'CN', 0xDF, $length );
00166                 }
00167                 foreach ( $value as $k => $v ) {
00168                     $buffer .= self::pack( $k );
00169                     $buffer .= self::pack( $v );
00170                 }
00171             } else {
00172                 if ( $length < 16 ) {
00173                     $buffer .= pack( 'C', 0x90 | $length );
00174                 } elseif ( $length <= 0xFFFF ) {
00175                     $buffer .= pack( 'Cn', 0xDC, $length );
00176                 } else {
00177                     $buffer .= pack( 'CN', 0xDD, $length );
00178                 }
00179                 foreach ( $value as $v ) {
00180                     $buffer .= self::pack( $v );
00181                 }
00182             }
00183             return $buffer;
00184 
00185         default:
00186             throw new InvalidArgumentException( __METHOD__ . ': unsupported type ' . gettype( $value ) );
00187         }
00188     }
00189 }