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