• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1<?php
2
3// Protocol Buffers - Google's data interchange format
4// Copyright 2008 Google Inc.  All rights reserved.
5//
6// Use of this source code is governed by a BSD-style
7// license that can be found in the LICENSE file or at
8// https://developers.google.com/open-source/licenses/bsd
9
10namespace Google\Protobuf\Internal;
11
12class GPBWire
13{
14
15    const TAG_TYPE_BITS = 3;
16
17    const WIRETYPE_VARINT  = 0;
18    const WIRETYPE_FIXED64 = 1;
19    const WIRETYPE_LENGTH_DELIMITED = 2;
20    const WIRETYPE_START_GROUP = 3;
21    const WIRETYPE_END_GROUP = 4;
22    const WIRETYPE_FIXED32 = 5;
23
24    const UNKNOWN = 0;
25    const NORMAL_FORMAT = 1;
26    const PACKED_FORMAT = 2;
27
28    public static function getTagFieldNumber($tag)
29    {
30        // We have to mask because PHP has no arithmetic shift.
31        return ($tag >> self::TAG_TYPE_BITS) & 0x1fffffff;
32    }
33
34    public static function getTagWireType($tag)
35    {
36        return $tag & 0x7;
37    }
38
39    public static function getWireType($type)
40    {
41        switch ($type) {
42            case GPBType::FLOAT:
43            case GPBType::FIXED32:
44            case GPBType::SFIXED32:
45                return self::WIRETYPE_FIXED32;
46            case GPBType::DOUBLE:
47            case GPBType::FIXED64:
48            case GPBType::SFIXED64:
49                return self::WIRETYPE_FIXED64;
50            case GPBType::UINT32:
51            case GPBType::UINT64:
52            case GPBType::INT32:
53            case GPBType::INT64:
54            case GPBType::SINT32:
55            case GPBType::SINT64:
56            case GPBType::ENUM:
57            case GPBType::BOOL:
58                return self::WIRETYPE_VARINT;
59            case GPBType::STRING:
60            case GPBType::BYTES:
61            case GPBType::MESSAGE:
62                return self::WIRETYPE_LENGTH_DELIMITED;
63            case GPBType::GROUP:
64                user_error("Unsupported type.");
65                return 0;
66            default:
67                user_error("Unsupported type.");
68                return 0;
69        }
70    }
71
72  // ZigZag Transform:  Encodes signed integers so that they can be effectively
73  // used with varint encoding.
74  //
75  // varint operates on unsigned integers, encoding smaller numbers into fewer
76  // bytes.  If you try to use it on a signed integer, it will treat this
77  // number as a very large unsigned integer, which means that even small
78  // signed numbers like -1 will take the maximum number of bytes (10) to
79  // encode.  zigZagEncode() maps signed integers to unsigned in such a way
80  // that those with a small absolute value will have smaller encoded values,
81  // making them appropriate for encoding using varint.
82  //
83  // int32 ->     uint32
84  // -------------------------
85  //           0 ->          0
86  //          -1 ->          1
87  //           1 ->          2
88  //          -2 ->          3
89  //         ... ->        ...
90  //  2147483647 -> 4294967294
91  // -2147483648 -> 4294967295
92  //
93  //        >> encode >>
94  //        << decode <<
95  public static function zigZagEncode32($int32)
96  {
97      if (PHP_INT_SIZE == 8) {
98          $trim_int32 = $int32 & 0xFFFFFFFF;
99          return (($trim_int32 << 1) ^ ($int32 << 32 >> 63)) & 0xFFFFFFFF;
100      } else {
101          return ($int32 << 1) ^ ($int32 >> 31);
102      }
103  }
104
105    public static function zigZagDecode32($uint32)
106    {
107        // Fill high 32 bits.
108        if (PHP_INT_SIZE === 8) {
109            $uint32 |= ($uint32 & 0xFFFFFFFF);
110        }
111
112        $int32 = (($uint32 >> 1) & 0x7FFFFFFF) ^ (-($uint32 & 1));
113
114        return $int32;
115    }
116
117    public static function zigZagEncode64($int64)
118    {
119        if (PHP_INT_SIZE == 4) {
120            if (bccomp($int64, 0) >= 0) {
121                return bcmul($int64, 2);
122            } else {
123                return bcsub(bcmul(bcsub(0, $int64), 2), 1);
124            }
125        } else {
126            return ((int)$int64 << 1) ^ ((int)$int64 >> 63);
127        }
128    }
129
130    public static function zigZagDecode64($uint64)
131    {
132        if (PHP_INT_SIZE == 4) {
133            if (bcmod($uint64, 2) == 0) {
134                return bcdiv($uint64, 2, 0);
135            } else {
136                return bcsub(0, bcdiv(bcadd($uint64, 1), 2, 0));
137            }
138        } else {
139            return (($uint64 >> 1) & 0x7FFFFFFFFFFFFFFF) ^ (-($uint64 & 1));
140        }
141    }
142
143    public static function readInt32(&$input, &$value)
144    {
145        return $input->readVarint32($value);
146    }
147
148    public static function readInt64(&$input, &$value)
149    {
150        $success = $input->readVarint64($value);
151        if (PHP_INT_SIZE == 4 && bccomp($value, "9223372036854775807") > 0) {
152            $value = bcsub($value, "18446744073709551616");
153        }
154        return $success;
155    }
156
157    public static function readUint32(&$input, &$value)
158    {
159        return self::readInt32($input, $value);
160    }
161
162    public static function readUint64(&$input, &$value)
163    {
164        return self::readInt64($input, $value);
165    }
166
167    public static function readSint32(&$input, &$value)
168    {
169        if (!$input->readVarint32($value)) {
170            return false;
171        }
172        $value = GPBWire::zigZagDecode32($value);
173        return true;
174    }
175
176    public static function readSint64(&$input, &$value)
177    {
178        if (!$input->readVarint64($value)) {
179            return false;
180        }
181        $value = GPBWire::zigZagDecode64($value);
182        return true;
183    }
184
185    public static function readFixed32(&$input, &$value)
186    {
187        return $input->readLittleEndian32($value);
188    }
189
190    public static function readFixed64(&$input, &$value)
191    {
192        return $input->readLittleEndian64($value);
193    }
194
195    public static function readSfixed32(&$input, &$value)
196    {
197        if (!self::readFixed32($input, $value)) {
198            return false;
199        }
200        if (PHP_INT_SIZE === 8) {
201            $value |= (-($value >> 31) << 32);
202        }
203        return true;
204    }
205
206    public static function readSfixed64(&$input, &$value)
207    {
208        $success = $input->readLittleEndian64($value);
209        if (PHP_INT_SIZE == 4 && bccomp($value, "9223372036854775807") > 0) {
210            $value = bcsub($value, "18446744073709551616");
211        }
212        return $success;
213    }
214
215    public static function readFloat(&$input, &$value)
216    {
217        $data = null;
218        if (!$input->readRaw(4, $data)) {
219            return false;
220        }
221        $value = unpack('g', $data)[1];
222        return true;
223    }
224
225    public static function readDouble(&$input, &$value)
226    {
227        $data = null;
228        if (!$input->readRaw(8, $data)) {
229            return false;
230        }
231        $value = unpack('e', $data)[1];
232        return true;
233    }
234
235    public static function readBool(&$input, &$value)
236    {
237        if (!$input->readVarint64($value)) {
238            return false;
239        }
240        if ($value == 0) {
241            $value = false;
242        } else {
243            $value = true;
244        }
245        return true;
246    }
247
248    public static function readString(&$input, &$value)
249    {
250        $length = 0;
251        return $input->readVarintSizeAsInt($length) && $input->readRaw($length, $value);
252    }
253
254    public static function readMessage(&$input, &$message)
255    {
256        $length = 0;
257        if (!$input->readVarintSizeAsInt($length)) {
258            return false;
259        }
260        $old_limit = 0;
261        $recursion_limit = 0;
262        $input->incrementRecursionDepthAndPushLimit(
263            $length,
264            $old_limit,
265            $recursion_limit);
266        if ($recursion_limit < 0 || !$message->parseFromStream($input)) {
267            return false;
268        }
269        return $input->decrementRecursionDepthAndPopLimit($old_limit);
270    }
271
272    public static function writeTag(&$output, $tag)
273    {
274        return $output->writeTag($tag);
275    }
276
277    public static function writeInt32(&$output, $value)
278    {
279        return $output->writeVarint32($value, false);
280    }
281
282    public static function writeInt64(&$output, $value)
283    {
284        return $output->writeVarint64($value);
285    }
286
287    public static function writeUint32(&$output, $value)
288    {
289        return $output->writeVarint32($value, true);
290    }
291
292    public static function writeUint64(&$output, $value)
293    {
294        return $output->writeVarint64($value);
295    }
296
297    public static function writeSint32(&$output, $value)
298    {
299        $value = GPBWire::zigZagEncode32($value);
300        return $output->writeVarint32($value, true);
301    }
302
303    public static function writeSint64(&$output, $value)
304    {
305        $value = GPBWire::zigZagEncode64($value);
306        return $output->writeVarint64($value);
307    }
308
309    public static function writeFixed32(&$output, $value)
310    {
311        return $output->writeLittleEndian32($value);
312    }
313
314    public static function writeFixed64(&$output, $value)
315    {
316        return $output->writeLittleEndian64($value);
317    }
318
319    public static function writeSfixed32(&$output, $value)
320    {
321        return $output->writeLittleEndian32($value);
322    }
323
324    public static function writeSfixed64(&$output, $value)
325    {
326        return $output->writeLittleEndian64($value);
327    }
328
329    public static function writeBool(&$output, $value)
330    {
331        if ($value) {
332            return $output->writeVarint32(1, true);
333        } else {
334            return $output->writeVarint32(0, true);
335        }
336    }
337
338    public static function writeFloat(&$output, $value)
339    {
340        $data = pack("g", $value);
341        return $output->writeRaw($data, 4);
342    }
343
344    public static function writeDouble(&$output, $value)
345    {
346        $data = pack("e", $value);
347        return $output->writeRaw($data, 8);
348    }
349
350    public static function writeString(&$output, $value)
351    {
352        return self::writeBytes($output, $value);
353    }
354
355    public static function writeBytes(&$output, $value)
356    {
357        $size = strlen($value);
358        if (!$output->writeVarint32($size, true)) {
359            return false;
360        }
361        return $output->writeRaw($value, $size);
362    }
363
364    public static function writeMessage(&$output, $value)
365    {
366        $size = $value->byteSize();
367        if (!$output->writeVarint32($size, true)) {
368            return false;
369        }
370        return $value->serializeToStream($output);
371    }
372
373    public static function makeTag($number, $type)
374    {
375        return ($number << 3) | self::getWireType($type);
376    }
377
378    public static function tagSize($field)
379    {
380        $tag = self::makeTag($field->getNumber(), $field->getType());
381        return self::varint32Size($tag);
382    }
383
384    public static function varint32Size($value, $sign_extended = false)
385    {
386        if ($value < 0) {
387            if ($sign_extended) {
388                return 10;
389            } else {
390                return 5;
391            }
392        }
393        if ($value < (1 <<  7)) {
394            return 1;
395        }
396        if ($value < (1 << 14)) {
397            return 2;
398        }
399        if ($value < (1 << 21)) {
400            return 3;
401        }
402        if ($value < (1 << 28)) {
403            return 4;
404        }
405        return 5;
406    }
407
408    public static function sint32Size($value)
409    {
410        $value = self::zigZagEncode32($value);
411        return self::varint32Size($value);
412    }
413
414    public static function sint64Size($value)
415    {
416        $value = self::zigZagEncode64($value);
417        return self::varint64Size($value);
418    }
419
420    public static function varint64Size($value)
421    {
422        if (PHP_INT_SIZE == 4) {
423            if (bccomp($value, 0) < 0 ||
424                bccomp($value, "9223372036854775807") > 0) {
425                return 10;
426            }
427            if (bccomp($value, 1 << 7) < 0) {
428                return 1;
429            }
430            if (bccomp($value, 1 << 14) < 0) {
431                return 2;
432            }
433            if (bccomp($value, 1 << 21) < 0) {
434                return 3;
435            }
436            if (bccomp($value, 1 << 28) < 0) {
437                return 4;
438            }
439            if (bccomp($value, '34359738368') < 0) {
440                return 5;
441            }
442            if (bccomp($value, '4398046511104') < 0) {
443                return 6;
444            }
445            if (bccomp($value, '562949953421312') < 0) {
446                return 7;
447            }
448            if (bccomp($value, '72057594037927936') < 0) {
449                return 8;
450            }
451            return 9;
452        } else {
453            if ($value < 0) {
454                return 10;
455            }
456            if ($value < (1 <<  7)) {
457                return 1;
458            }
459            if ($value < (1 << 14)) {
460                return 2;
461            }
462            if ($value < (1 << 21)) {
463                return 3;
464            }
465            if ($value < (1 << 28)) {
466                return 4;
467            }
468            if ($value < (1 << 35)) {
469                return 5;
470            }
471            if ($value < (1 << 42)) {
472                return 6;
473            }
474            if ($value < (1 << 49)) {
475                return 7;
476            }
477            if ($value < (1 << 56)) {
478                return 8;
479            }
480            return 9;
481        }
482    }
483
484    public static function serializeFieldToStream(
485        $value,
486        $field,
487        $need_tag,
488        &$output)
489    {
490        if ($need_tag) {
491            if (!GPBWire::writeTag(
492                $output,
493                self::makeTag(
494                    $field->getNumber(),
495                    $field->getType()))) {
496                return false;
497            }
498        }
499        switch ($field->getType()) {
500            case GPBType::DOUBLE:
501                if (!GPBWire::writeDouble($output, $value)) {
502                    return false;
503                }
504                break;
505            case GPBType::FLOAT:
506                if (!GPBWire::writeFloat($output, $value)) {
507                    return false;
508                }
509                break;
510            case GPBType::INT64:
511                if (!GPBWire::writeInt64($output, $value)) {
512                    return false;
513                }
514                break;
515            case GPBType::UINT64:
516                if (!GPBWire::writeUint64($output, $value)) {
517                    return false;
518                }
519                break;
520            case GPBType::INT32:
521                if (!GPBWire::writeInt32($output, $value)) {
522                    return false;
523                }
524                break;
525            case GPBType::FIXED32:
526                if (!GPBWire::writeFixed32($output, $value)) {
527                    return false;
528                }
529                break;
530            case GPBType::FIXED64:
531                if (!GPBWire::writeFixed64($output, $value)) {
532                    return false;
533                }
534                break;
535            case GPBType::BOOL:
536                if (!GPBWire::writeBool($output, $value)) {
537                    return false;
538                }
539                break;
540            case GPBType::STRING:
541                if (!GPBWire::writeString($output, $value)) {
542                    return false;
543                }
544                break;
545            //    case GPBType::GROUP:
546            //      echo "GROUP\xA";
547            //      trigger_error("Not implemented.", E_ERROR);
548            //      break;
549            case GPBType::MESSAGE:
550                if (!GPBWire::writeMessage($output, $value)) {
551                    return false;
552                }
553                break;
554            case GPBType::BYTES:
555                if (!GPBWire::writeBytes($output, $value)) {
556                    return false;
557                }
558                break;
559            case GPBType::UINT32:
560                if (PHP_INT_SIZE === 8 && $value < 0) {
561                    $value += 4294967296;
562                }
563                if (!GPBWire::writeUint32($output, $value)) {
564                    return false;
565                }
566                break;
567            case GPBType::ENUM:
568                if (!GPBWire::writeInt32($output, $value)) {
569                    return false;
570                }
571                break;
572            case GPBType::SFIXED32:
573                if (!GPBWire::writeSfixed32($output, $value)) {
574                    return false;
575                }
576                break;
577            case GPBType::SFIXED64:
578                if (!GPBWire::writeSfixed64($output, $value)) {
579                    return false;
580                }
581                break;
582            case GPBType::SINT32:
583                if (!GPBWire::writeSint32($output, $value)) {
584                    return false;
585                }
586                break;
587            case GPBType::SINT64:
588                if (!GPBWire::writeSint64($output, $value)) {
589                    return false;
590                }
591                break;
592            default:
593                user_error("Unsupported type.");
594                return false;
595        }
596
597        return true;
598    }
599}
600