]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blob - includes/libs/MWMessagePack.php
MediaWiki 1.30.2
[autoinstallsdev/mediawiki.git] / includes / libs / MWMessagePack.php
1 <?php
2 /**
3  * MessagePack serializer
4  *
5  * MessagePack is a space-efficient binary data interchange format. This
6  * class provides a pack() method that encodes native PHP values as MessagePack
7  * binary strings. The implementation is derived from msgpack-php.
8  *
9  * Copyright (c) 2013 Ori Livneh <ori@wikimedia.org>
10  * Copyright (c) 2011 OnlineCity <https://github.com/onlinecity/msgpack-php>.
11  *
12  * Permission is hereby granted, free of charge, to any person obtaining a copy
13  * of this software and associated documentation files (the "Software"), to
14  * deal in the Software without restriction, including without limitation the
15  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
16  * sell copies of the Software, and to permit persons to whom the Software is
17  * furnished to do so, subject to the following conditions:
18  *
19  * The above copyright notice and this permission notice shall be included in
20  * all copies or substantial portions of the Software.
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
27  * IN THE SOFTWARE.
28  *
29  * @see <http://msgpack.org/>
30  * @see <http://wiki.msgpack.org/display/MSGPACK/Format+specification>
31  *
32  * @since 1.23
33  * @file
34  */
35 class MWMessagePack {
36         /** @var bool|null Whether current system is bigendian. **/
37         public static $bigendian = null;
38
39         /**
40          * Encode a value using MessagePack
41          *
42          * This method supports null, boolean, integer, float, string and array
43          * (both indexed and associative) types. Object serialization is not
44          * supported.
45          *
46          * @param mixed $value
47          * @return string
48          * @throws InvalidArgumentException if $value is an unsupported type or too long a string
49          */
50         public static function pack( $value ) {
51                 if ( self::$bigendian === null ) {
52                         self::$bigendian = pack( 'S', 1 ) === pack( 'n', 1 );
53                 }
54
55                 switch ( gettype( $value ) ) {
56                 case 'NULL':
57                         return "\xC0";
58
59                 case 'boolean':
60                         return $value ? "\xC3" : "\xC2";
61
62                 case 'double':
63                 case 'float':
64                         return self::$bigendian
65                                 ? "\xCB" . pack( 'd', $value )
66                                 : "\xCB" . strrev( pack( 'd', $value ) );
67
68                 case 'string':
69                         $length = strlen( $value );
70                         if ( $length < 32 ) {
71                                 return pack( 'Ca*', 0xA0 | $length, $value );
72                         } elseif ( $length <= 0xFFFF ) {
73                                 return pack( 'Cna*', 0xDA, $length, $value );
74                         } elseif ( $length <= 0xFFFFFFFF ) {
75                                 return pack( 'CNa*', 0xDB, $length, $value );
76                         }
77                         throw new InvalidArgumentException( __METHOD__
78                                 . ": string too long (length: $length; max: 4294967295)" );
79
80                 case 'integer':
81                         if ( $value >= 0 ) {
82                                 if ( $value <= 0x7F ) {
83                                         // positive fixnum
84                                         return chr( $value );
85                                 }
86                                 if ( $value <= 0xFF ) {
87                                         // uint8
88                                         return pack( 'CC', 0xCC, $value );
89                                 }
90                                 if ( $value <= 0xFFFF ) {
91                                         // uint16
92                                         return pack( 'Cn', 0xCD, $value );
93                                 }
94                                 if ( $value <= 0xFFFFFFFF ) {
95                                         // uint32
96                                         return pack( 'CN', 0xCE, $value );
97                                 }
98                                 if ( $value <= 0xFFFFFFFFFFFFFFFF ) {
99                                         // uint64
100                                         $hi = ( $value & 0xFFFFFFFF00000000 ) >> 32;
101                                         $lo = $value & 0xFFFFFFFF;
102                                         return self::$bigendian
103                                                 ? pack( 'CNN', 0xCF, $lo, $hi )
104                                                 : pack( 'CNN', 0xCF, $hi, $lo );
105                                 }
106                         } else {
107                                 if ( $value >= -32 ) {
108                                         // negative fixnum
109                                         return pack( 'c', $value );
110                                 }
111                                 if ( $value >= -0x80 ) {
112                                         // int8
113                                         return pack( 'Cc', 0xD0, $value );
114                                 }
115                                 if ( $value >= -0x8000 ) {
116                                         // int16
117                                         $p = pack( 's', $value );
118                                         return self::$bigendian
119                                                 ? pack( 'Ca2', 0xD1, $p )
120                                                 : pack( 'Ca2', 0xD1, strrev( $p ) );
121                                 }
122                                 if ( $value >= -0x80000000 ) {
123                                         // int32
124                                         $p = pack( 'l', $value );
125                                         return self::$bigendian
126                                                 ? pack( 'Ca4', 0xD2, $p )
127                                                 : pack( 'Ca4', 0xD2, strrev( $p ) );
128                                 }
129                                 if ( $value >= -0x8000000000000000 ) {
130                                         // int64
131                                         // pack() does not support 64-bit ints either so pack into two 32-bits
132                                         $p1 = pack( 'l', $value & 0xFFFFFFFF );
133                                         $p2 = pack( 'l', ( $value >> 32 ) & 0xFFFFFFFF );
134                                         return self::$bigendian
135                                                 ? pack( 'Ca4a4', 0xD3, $p1, $p2 )
136                                                 : pack( 'Ca4a4', 0xD3, strrev( $p2 ), strrev( $p1 ) );
137                                 }
138                         }
139                         throw new InvalidArgumentException( __METHOD__ . ": invalid integer '$value'" );
140
141                 case 'array':
142                         $buffer = '';
143                         $length = count( $value );
144                         if ( $length > 0xFFFFFFFF ) {
145                                 throw new InvalidArgumentException( __METHOD__
146                                         . ": array too long (length: $length, max: 4294967295)" );
147                         }
148
149                         $index = 0;
150                         foreach ( $value as $k => $v ) {
151                                 if ( $index !== $k || $index === $length ) {
152                                         break;
153                                 } else {
154                                         $index++;
155                                 }
156                         }
157                         $associative = $index !== $length;
158
159                         if ( $associative ) {
160                                 if ( $length < 16 ) {
161                                         $buffer .= pack( 'C', 0x80 | $length );
162                                 } elseif ( $length <= 0xFFFF ) {
163                                         $buffer .= pack( 'Cn', 0xDE, $length );
164                                 } else {
165                                         $buffer .= pack( 'CN', 0xDF, $length );
166                                 }
167                                 foreach ( $value as $k => $v ) {
168                                         $buffer .= self::pack( $k );
169                                         $buffer .= self::pack( $v );
170                                 }
171                         } else {
172                                 if ( $length < 16 ) {
173                                         $buffer .= pack( 'C', 0x90 | $length );
174                                 } elseif ( $length <= 0xFFFF ) {
175                                         $buffer .= pack( 'Cn', 0xDC, $length );
176                                 } else {
177                                         $buffer .= pack( 'CN', 0xDD, $length );
178                                 }
179                                 foreach ( $value as $v ) {
180                                         $buffer .= self::pack( $v );
181                                 }
182                         }
183                         return $buffer;
184
185                 default:
186                         throw new InvalidArgumentException( __METHOD__ . ': unsupported type ' . gettype( $value ) );
187                 }
188         }
189 }