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