1 <?php 2 /* 3 * Copyright 2015 Google Inc. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 /// @file 19 /// @addtogroup flatbuffers_php_api 20 /// @{ 21 22 namespace Google\FlatBuffers; 23 24 class FlatbufferBuilder 25 { 26 /** 27 * Internal ByteBuffer for the FlatBuffer data. 28 * @var ByteBuffer $bb 29 */ 30 public $bb; 31 32 /// @cond FLATBUFFERS_INTERNAL 33 /** 34 * @var int $space 35 */ 36 protected $space; 37 38 /** 39 * @var int $minalign 40 */ 41 protected $minalign = 1; 42 43 /** 44 * @var array $vtable 45 */ 46 protected $vtable; 47 48 /** 49 * @var int $vtable_in_use 50 */ 51 protected $vtable_in_use = 0; 52 53 /** 54 * @var bool $nested 55 */ 56 protected $nested = false; 57 58 /** 59 * @var int $object_start 60 */ 61 protected $object_start; 62 63 /** 64 * @var array $vtables 65 */ 66 protected $vtables = array(); 67 68 /** 69 * @var int $num_vtables 70 */ 71 protected $num_vtables = 0; 72 73 /** 74 * @var int $vector_num_elems 75 */ 76 protected $vector_num_elems = 0; 77 78 /** 79 * @var bool $force_defaults 80 */ 81 protected $force_defaults = false; 82 /// @endcond 83 84 /** 85 * Create a FlatBufferBuilder with a given initial size. 86 * 87 * @param $initial_size initial byte buffer size. 88 */ 89 public function __construct($initial_size) 90 { 91 if ($initial_size <= 0) { 92 $initial_size = 1; 93 } 94 $this->space = $initial_size; 95 $this->bb = $this->newByteBuffer($initial_size); 96 } 97 98 /// @cond FLATBUFFERS_INTERNAL 99 /** 100 * create new bytebuffer 101 * 102 * @param $size 103 * @return ByteBuffer 104 */ 105 private function newByteBuffer($size) 106 { 107 return new ByteBuffer($size); 108 } 109 110 /** 111 * Returns the current ByteBuffer offset. 112 * 113 * @return int 114 */ 115 public function offset() 116 { 117 return $this->bb->capacity() - $this->space; 118 } 119 120 /** 121 * padding buffer 122 * 123 * @param $byte_size 124 */ 125 public function pad($byte_size) 126 { 127 for ($i = 0; $i < $byte_size; $i++) { 128 $this->bb->putByte(--$this->space, "\0"); 129 } 130 } 131 132 /** 133 * prepare bytebuffer 134 * 135 * @param $size 136 * @param $additional_bytes 137 * @throws \Exception 138 */ 139 public function prep($size, $additional_bytes) 140 { 141 if ($size > $this->minalign) { 142 $this->minalign = $size; 143 } 144 145 $align_size = ((~($this->bb->capacity() - $this->space + $additional_bytes)) + 1) & ($size - 1); 146 while ($this->space < $align_size + $size + $additional_bytes) { 147 $old_buf_size = $this->bb->capacity(); 148 $this->bb = $this->growByteBuffer($this->bb); 149 $this->space += $this->bb->capacity() - $old_buf_size; 150 } 151 152 $this->pad($align_size); 153 } 154 155 /** 156 * @param ByteBuffer $bb 157 * @return ByteBuffer 158 * @throws \Exception 159 */ 160 private static function growByteBuffer(ByteBuffer $bb) 161 { 162 $old_buf_size = $bb->capacity(); 163 if (($old_buf_size & 0xC0000000) != 0) { 164 throw new \Exception("FlatBuffers: cannot grow buffer beyond 2 gigabytes"); 165 } 166 $new_buf_size = $old_buf_size << 1; 167 168 $bb->setPosition(0); 169 $nbb = new ByteBuffer($new_buf_size); 170 171 $nbb->setPosition($new_buf_size - $old_buf_size); 172 173 // TODO(chobie): is this little bit faster? 174 //$nbb->_buffer = substr_replace($nbb->_buffer, $bb->_buffer, $new_buf_size - $old_buf_size, strlen($bb->_buffer)); 175 for ($i = $new_buf_size - $old_buf_size, $j = 0; $j < strlen($bb->_buffer); $i++, $j++) { 176 $nbb->_buffer[$i] = $bb->_buffer[$j]; 177 } 178 179 return $nbb; 180 } 181 182 /** 183 * @param $x 184 */ 185 public function putBool($x) 186 { 187 $this->bb->put($this->space -= 1, chr((int)(bool)($x))); 188 } 189 190 /** 191 * @param $x 192 */ 193 public function putByte($x) 194 { 195 $this->bb->put($this->space -= 1, chr($x)); 196 } 197 198 /** 199 * @param $x 200 */ 201 public function putSbyte($x) 202 { 203 $this->bb->put($this->space -= 1, chr($x)); 204 } 205 206 /** 207 * @param $x 208 */ 209 public function putShort($x) 210 { 211 $this->bb->putShort($this->space -= 2, $x); 212 } 213 214 /** 215 * @param $x 216 */ 217 public function putUshort($x) 218 { 219 $this->bb->putUshort($this->space -= 2, $x); 220 } 221 222 /** 223 * @param $x 224 */ 225 public function putInt($x) 226 { 227 $this->bb->putInt($this->space -= 4, $x); 228 } 229 230 /** 231 * @param $x 232 */ 233 public function putUint($x) 234 { 235 if ($x > PHP_INT_MAX) { 236 throw new \InvalidArgumentException("your platform can't handle uint correctly. use 64bit machine."); 237 } 238 239 $this->bb->putUint($this->space -= 4, $x); 240 } 241 242 /** 243 * @param $x 244 */ 245 public function putLong($x) 246 { 247 if ($x > PHP_INT_MAX) { 248 throw new \InvalidArgumentException("Your platform can't handle long correctly. Use a 64bit machine."); 249 } 250 251 $this->bb->putLong($this->space -= 8, $x); 252 } 253 254 /** 255 * @param $x 256 */ 257 public function putUlong($x) 258 { 259 if ($x > PHP_INT_MAX) { 260 throw new \InvalidArgumentException("Your platform can't handle ulong correctly. This is a php limitation. Please wait for the extension release."); 261 } 262 263 $this->bb->putUlong($this->space -= 8, $x); 264 } 265 266 /** 267 * @param $x 268 */ 269 public function putFloat($x) 270 { 271 $this->bb->putFloat($this->space -= 4, $x); 272 } 273 274 /** 275 * @param $x 276 */ 277 public function putDouble($x) 278 { 279 $this->bb->putDouble($this->space -= 8, $x); 280 } 281 /// @endcond 282 283 /** 284 * Add a `bool` to the buffer, properly aligned, and grows the buffer (if necessary). 285 * @param $x The `bool` to add to the buffer. 286 */ 287 public function addBool($x) 288 { 289 $this->prep(1, 0); 290 $this->putBool($x); 291 } 292 293 /** 294 * Add a `byte` to the buffer, properly aligned, and grows the buffer (if necessary). 295 * @param $x The `byte` to add to the buffer. 296 */ 297 public function addByte($x) 298 { 299 $this->prep(1, 0); 300 $this->putByte($x); 301 } 302 303 /** 304 * Add a `signed byte` to the buffer, properly aligned, and grows the buffer (if necessary). 305 * @param $x The `signed byte` to add to the buffer. 306 */ 307 public function addSbyte($x) 308 { 309 $this->prep(1, 0); 310 $this->putSbyte($x); 311 } 312 313 /** 314 * Add a `short` to the buffer, properly aligned, and grows the buffer (if necessary). 315 * @param $x The `short` to add to the buffer. 316 */ 317 public function addShort($x) 318 { 319 $this->prep(2, 0); 320 $this->putShort($x); 321 } 322 323 /** 324 * Add an `unsigned short` to the buffer, properly aligned, and grows the buffer (if necessary). 325 * @param $x The `unsigned short` to add to the buffer. 326 */ 327 public function addUshort($x) 328 { 329 $this->prep(2, 0); 330 $this->putUshort($x); 331 } 332 333 /** 334 * Add an `int` to the buffer, properly aligned, and grows the buffer (if necessary). 335 * @param $x The `int` to add to the buffer. 336 */ 337 public function addInt($x) 338 { 339 $this->prep(4, 0); 340 $this->putInt($x); 341 } 342 343 /** 344 * Add an `unsigned int` to the buffer, properly aligned, and grows the buffer (if necessary). 345 * @param $x The `unsigned int` to add to the buffer. 346 */ 347 public function addUint($x) 348 { 349 $this->prep(4, 0); 350 $this->putUint($x); 351 } 352 353 /** 354 * Add a `long` to the buffer, properly aligned, and grows the buffer (if necessary). 355 * @param $x The `long` to add to the buffer. 356 */ 357 public function addLong($x) 358 { 359 $this->prep(8, 0); 360 $this->putLong($x); 361 } 362 363 /** 364 * Add an `unsigned long` to the buffer, properly aligned, and grows the buffer (if necessary). 365 * @param $x The `unsigned long` to add to the buffer. 366 */ 367 public function addUlong($x) 368 { 369 $this->prep(8, 0); 370 $this->putUlong($x); 371 } 372 373 /** 374 * Add a `float` to the buffer, properly aligned, and grows the buffer (if necessary). 375 * @param $x The `float` to add to the buffer. 376 */ 377 public function addFloat($x) 378 { 379 $this->prep(4, 0); 380 $this->putFloat($x); 381 } 382 383 /** 384 * Add a `double` to the buffer, properly aligned, and grows the buffer (if necessary). 385 * @param $x The `double` to add to the buffer. 386 */ 387 public function addDouble($x) 388 { 389 $this->prep(8, 0); 390 $this->putDouble($x); 391 } 392 393 /// @cond FLATBUFFERS_INTERNAL 394 /** 395 * @param $o 396 * @param $x 397 * @param $d 398 */ 399 public function addBoolX($o, $x, $d) 400 { 401 if ($this->force_defaults || $x != $d) { 402 $this->addBool($x); 403 $this->slot($o); 404 } 405 } 406 407 /** 408 * @param $o 409 * @param $x 410 * @param $d 411 */ 412 public function addByteX($o, $x, $d) 413 { 414 if ($this->force_defaults || $x != $d) { 415 $this->addByte($x); 416 $this->slot($o); 417 } 418 } 419 420 /** 421 * @param $o 422 * @param $x 423 * @param $d 424 */ 425 public function addSbyteX($o, $x, $d) 426 { 427 if ($this->force_defaults || $x != $d) { 428 $this->addSbyte($x); 429 $this->slot($o); 430 } 431 } 432 433 /** 434 * @param $o 435 * @param $x 436 * @param $d 437 */ 438 public function addShortX($o, $x, $d) 439 { 440 if ($this->force_defaults || $x != $d) { 441 $this->addShort($x); 442 $this->slot($o); 443 } 444 } 445 446 /** 447 * @param $o 448 * @param $x 449 * @param $d 450 */ 451 public function addUshortX($o, $x, $d) 452 { 453 if ($this->force_defaults || $x != $d) { 454 $this->addUshort($x); 455 $this->slot($o); 456 } 457 } 458 459 /** 460 * @param $o 461 * @param $x 462 * @param $d 463 */ 464 public function addIntX($o, $x, $d) 465 { 466 if ($this->force_defaults || $x != $d) { 467 $this->addInt($x); 468 $this->slot($o); 469 } 470 } 471 472 /** 473 * @param $o 474 * @param $x 475 * @param $d 476 */ 477 public function addUintX($o, $x, $d) 478 { 479 if ($this->force_defaults || $x != $d) { 480 $this->addUint($x); 481 $this->slot($o); 482 } 483 } 484 485 /** 486 * @param $o 487 * @param $x 488 * @param $d 489 */ 490 public function addLongX($o, $x, $d) 491 { 492 if ($this->force_defaults || $x != $d) { 493 $this->addLong($x); 494 $this->slot($o); 495 } 496 } 497 498 /** 499 * @param $o 500 * @param $x 501 * @param $d 502 */ 503 public function addUlongX($o, $x, $d) 504 { 505 if ($this->force_defaults || $x != $d) { 506 $this->addUlong($x); 507 $this->slot($o); 508 } 509 } 510 511 512 /** 513 * @param $o 514 * @param $x 515 * @param $d 516 */ 517 public function addFloatX($o, $x, $d) 518 { 519 if ($this->force_defaults || $x != $d) { 520 $this->addFloat($x); 521 $this->slot($o); 522 } 523 } 524 525 /** 526 * @param $o 527 * @param $x 528 * @param $d 529 */ 530 public function addDoubleX($o, $x, $d) 531 { 532 if ($this->force_defaults || $x != $d) { 533 $this->addDouble($x); 534 $this->slot($o); 535 } 536 } 537 538 /** 539 * @param $o 540 * @param $x 541 * @param $d 542 * @throws \Exception 543 */ 544 public function addOffsetX($o, $x, $d) 545 { 546 if ($this->force_defaults || $x != $d) { 547 $this->addOffset($x); 548 $this->slot($o); 549 } 550 } 551 /// @endcond 552 553 /** 554 * Adds on offset, relative to where it will be written. 555 * @param $off The offset to add to the buffer. 556 * @throws \Exception Throws an exception if `$off` is greater than the underlying ByteBuffer's 557 * offest. 558 */ 559 public function addOffset($off) 560 { 561 $this->prep(Constants::SIZEOF_INT, 0); // Ensure alignment is already done 562 if ($off > $this->offset()) { 563 throw new \Exception(""); 564 } 565 566 $off = $this->offset() - $off + Constants::SIZEOF_INT; 567 $this->putInt($off); 568 } 569 570 /// @cond FLATBUFFERS_INTERNAL 571 /** 572 * @param $elem_size 573 * @param $num_elems 574 * @param $alignment 575 * @throws \Exception 576 */ 577 public function startVector($elem_size, $num_elems, $alignment) 578 { 579 $this->notNested(); 580 $this->vector_num_elems = $num_elems; 581 $this->prep(Constants::SIZEOF_INT, $elem_size * $num_elems); 582 $this->prep($alignment, $elem_size * $num_elems); // Just in case alignemnt > int; 583 } 584 585 /** 586 * @return int 587 */ 588 public function endVector() 589 { 590 $this->putUint($this->vector_num_elems); 591 return $this->offset(); 592 } 593 594 protected function is_utf8($bytes) 595 { 596 if (function_exists('mb_detect_encoding')) { 597 return (bool) mb_detect_encoding($bytes, 'UTF-8', true); 598 } 599 600 $len = strlen($bytes); 601 if ($len < 1) { 602 /* NOTE: always return 1 when passed string is null */ 603 return true; 604 } 605 606 for ($j = 0, $i = 0; $i < $len; $i++) { 607 // check ACII 608 if ($bytes[$j] == "\x09" || 609 $bytes[$j] == "\x0A" || 610 $bytes[$j] == "\x0D" || 611 ($bytes[$j] >= "\x20" && $bytes[$j] <= "\x7E")) { 612 $j++; 613 continue; 614 } 615 616 /* non-overlong 2-byte */ 617 if ((($i+1) <= $len) && 618 ($bytes[$j] >= "\xC2" && $bytes[$j] <= "\xDF" && 619 ($bytes[$j+1] >= "\x80" && $bytes[$j+1] <= "\xBF"))) { 620 $j += 2; 621 $i++; 622 continue; 623 } 624 625 /* excluding overlongs */ 626 if ((($i + 2) <= $len) && 627 $bytes[$j] == "\xE0" && 628 ($bytes[$j+1] >= "\xA0" && $bytes[$j+1] <= "\xBF" && 629 ($bytes[$j+2] >= "\x80" && $bytes[$j+2] <= "\xBF"))) { 630 $bytes += 3; 631 $i +=2; 632 continue; 633 } 634 635 /* straight 3-byte */ 636 if ((($i+2) <= $len) && 637 (($bytes[$j] >= "\xE1" && $bytes[$j] <= "\xEC") || 638 $bytes[$j] == "\xEE" || 639 $bytes[$j] = "\xEF") && 640 ($bytes[$j+1] >= "\x80" && $bytes[$j+1] <= "\xBF") && 641 ($bytes[$j+2] >= "\x80" && $bytes[$j+2] <= "\xBF")) { 642 $j += 3; 643 $i += 2; 644 continue; 645 } 646 647 /* excluding surrogates */ 648 if ((($i+2) <= $len) && 649 $bytes[$j] == "\xED" && 650 ($bytes[$j+1] >= "\x80" && $bytes[$j+1] <= "\x9f" && 651 ($bytes[$j+2] >= "\x80" && $bytes[$j+2] <= "\xBF"))) { 652 $j += 3; 653 $i += 2; 654 continue; 655 } 656 657 /* planes 1-3 */ 658 if ((($i + 3) <= $len) && 659 $bytes[$j] == "\xF0" && 660 ($bytes[$j+1] >= "\x90" && $bytes[$j+1] <= "\xBF") && 661 ($bytes[$j+2] >= "\x80" && $bytes[$j+2] <= "\xBF") && 662 ($bytes[$j+3] >= "\x80" && $bytes[$j+3] <= "\xBF")) { 663 $j += 4; 664 $i += 3; 665 continue; 666 } 667 668 669 /* planes 4-15 */ 670 if ((($i+3) <= $len) && 671 $bytes[$j] >= "\xF1" && $bytes[$j] <= "\xF3" && 672 $bytes[$j+1] >= "\x80" && $bytes[$j+1] <= "\xBF" && 673 $bytes[$j+2] >= "\x80" && $bytes[$j+2] <= "\xBF" && 674 $bytes[$j+3] >= "\x80" && $bytes[$j+3] <= "\xBF" 675 ) { 676 $j += 4; 677 $i += 3; 678 continue; 679 } 680 681 /* plane 16 */ 682 if ((($i+3) <= $len) && 683 $bytes[$j] == "\xF4" && 684 ($bytes[$j+1] >= "\x80" && $bytes[$j+1] <= "\x8F") && 685 ($bytes[$j+2] >= "\x80" && $bytes[$j+2] <= "\xBF") && 686 ($bytes[$j+3] >= "\x80" && $bytes[$j+3] <= "\xBF") 687 ) { 688 $bytes += 4; 689 $i += 3; 690 continue; 691 } 692 693 694 return false; 695 } 696 697 return true; 698 } 699 /// @endcond 700 701 /** 702 * Encode the string `$s` in the buffer using UTF-8. 703 * @param string $s The string to encode. 704 * @return int The offset in the buffer where the encoded string starts. 705 * @throws InvalidArgumentException Thrown if the input string `$s` is not 706 * UTF-8. 707 */ 708 public function createString($s) 709 { 710 if (!$this->is_utf8($s)) { 711 throw new \InvalidArgumentException("string must be utf-8 encoded value."); 712 } 713 714 $this->notNested(); 715 $this->addByte(0); // null terminated 716 $this->startVector(1, strlen($s), 1); 717 $this->space -= strlen($s); 718 for ($i = $this->space, $j = 0 ; $j < strlen($s) ; $i++, $j++) { 719 $this->bb->_buffer[$i] = $s[$j]; 720 } 721 return $this->endVector(); 722 } 723 724 /// @cond FLATBUFFERS_INTERNAL 725 /** 726 * @throws \Exception 727 */ 728 public function notNested() 729 { 730 if ($this->nested) { 731 throw new \Exception("FlatBuffers; object serialization must not be nested"); 732 } 733 } 734 735 /** 736 * @param $obj 737 * @throws \Exception 738 */ 739 public function nested($obj) 740 { 741 if ($obj != $this->offset()) { 742 throw new \Exception("FlatBuffers: struct must be serialized inline"); 743 } 744 } 745 746 /** 747 * @param $numfields 748 * @throws \Exception 749 */ 750 public function startObject($numfields) 751 { 752 $this->notNested(); 753 if ($this->vtable == null || count($this->vtable) < $numfields) { 754 $this->vtable = array(); 755 } 756 757 $this->vtable_in_use = $numfields; 758 for ($i = 0; $i < $numfields; $i++) { 759 $this->vtable[$i] = 0; 760 } 761 762 $this->nested = true; 763 $this->object_start = $this->offset(); 764 } 765 766 /** 767 * @param $voffset 768 * @param $x 769 * @param $d 770 * @throws \Exception 771 */ 772 public function addStructX($voffset, $x, $d) 773 { 774 if ($x != $d) { 775 $this->nested($x); 776 $this->slot($voffset); 777 } 778 } 779 780 /** 781 * @param $voffset 782 * @param $x 783 * @param $d 784 * @throws \Exception 785 */ 786 public function addStruct($voffset, $x, $d) 787 { 788 if ($x != $d) { 789 $this->nested($x); 790 $this->slot($voffset); 791 } 792 } 793 794 /** 795 * @param $voffset 796 */ 797 public function slot($voffset) 798 { 799 $this->vtable[$voffset] = $this->offset(); 800 } 801 802 /** 803 * @return int 804 * @throws \Exception 805 */ 806 public function endObject() 807 { 808 if ($this->vtable == null || !$this->nested) { 809 throw new \Exception("FlatBuffers: endObject called without startObject"); 810 } 811 812 $this->addInt(0); 813 $vtableloc = $this->offset(); 814 815 $i = $this->vtable_in_use -1; 816 // Trim trailing zeroes. 817 for (; $i >= 0 && $this->vtable[$i] == 0; $i--) {} 818 $trimmed_size = $i + 1; 819 for (; $i >= 0; $i--) { 820 $off = ($this->vtable[$i] != 0) ? $vtableloc - $this->vtable[$i] : 0; 821 $this->addShort($off); 822 } 823 824 $standard_fields = 2; // the fields below 825 $this->addShort($vtableloc - $this->object_start); 826 $this->addShort(($trimmed_size + $standard_fields) * Constants::SIZEOF_SHORT); 827 828 // search for an existing vtable that matches the current one. 829 $existing_vtable = 0; 830 831 for ($i = 0; $i < $this->num_vtables; $i++) { 832 $vt1 = $this->bb->capacity() - $this->vtables[$i]; 833 $vt2 = $this->space; 834 835 $len = $this->bb->getShort($vt1); 836 837 if ($len == $this->bb->getShort($vt2)) { 838 for ($j = Constants::SIZEOF_SHORT; $j < $len; $j += Constants::SIZEOF_SHORT) { 839 if ($this->bb->getShort($vt1 + $j) != $this->bb->getShort($vt2 + $j)) { 840 continue 2; 841 } 842 } 843 $existing_vtable = $this->vtables[$i]; 844 break; 845 } 846 } 847 848 if ($existing_vtable != 0) { 849 // Found a match: 850 // Remove the current vtable 851 $this->space = $this->bb->capacity() - $vtableloc; 852 $this->bb->putInt($this->space, $existing_vtable - $vtableloc); 853 } else { 854 // No Match: 855 // Add the location of the current vtable to the list of vtables 856 if ($this->num_vtables == count($this->vtables)) { 857 $vtables = $this->vtables; 858 $this->vtables = array(); 859 // copy of 860 for ($i = 0; $i < count($vtables) * 2; $i++) { 861 $this->vtables[$i] = ($i < count($vtables)) ? $vtables[$i] : 0; 862 } 863 } 864 $this->vtables[$this->num_vtables++] = $this->offset(); 865 $this->bb->putInt($this->bb->capacity() - $vtableloc, $this->offset() - $vtableloc); 866 } 867 868 $this->nested = false; 869 $this->vtable = null; 870 return $vtableloc; 871 } 872 873 /** 874 * @param $table 875 * @param $field 876 * @throws \Exception 877 */ 878 public function required($table, $field) 879 { 880 $table_start = $this->bb->capacity() - $table; 881 $vtable_start = $table_start - $this->bb->getInt($table_start); 882 $ok = $this->bb->getShort($vtable_start + $field) != 0; 883 884 if (!$ok) { 885 throw new \Exception("FlatBuffers: field " . $field . " must be set"); 886 } 887 } 888 /// @endcond 889 890 /** 891 * Finalize a buffer, pointing to the given `$root_table`. 892 * @param $root_table An offest to be added to the buffer. 893 * @param $file_identifier A FlatBuffer file identifier to be added to the 894 * buffer before `$root_table`. This defaults to `null`. 895 * @throws InvalidArgumentException Thrown if an invalid `$identifier` is 896 * given, where its length is not equal to 897 * `Constants::FILE_IDENTIFIER_LENGTH`. 898 */ 899 public function finish($root_table, $identifier = null) 900 { 901 if ($identifier == null) { 902 $this->prep($this->minalign, Constants::SIZEOF_INT); 903 $this->addOffset($root_table); 904 $this->bb->setPosition($this->space); 905 } else { 906 $this->prep($this->minalign, Constants::SIZEOF_INT + Constants::FILE_IDENTIFIER_LENGTH); 907 if (strlen($identifier) != Constants::FILE_IDENTIFIER_LENGTH) { 908 throw new \InvalidArgumentException( 909 sprintf("FlatBuffers: file identifier must be length %d", 910 Constants::FILE_IDENTIFIER_LENGTH)); 911 } 912 913 for ($i = Constants::FILE_IDENTIFIER_LENGTH - 1; $i >= 0; 914 $i--) { 915 $this->addByte(ord($identifier[$i])); 916 } 917 $this->finish($root_table); 918 } 919 } 920 921 /** 922 * In order to save space, fields that are set to their default value don't 923 * get serialized into the buffer. 924 * @param bool $forceDefaults When set to `true`, always serializes default 925 * values. 926 */ 927 public function forceDefaults($forceDefaults) 928 { 929 $this->force_defaults = $forceDefaults; 930 } 931 932 /** 933 * Get the ByteBuffer representing the FlatBuffer. 934 * @return ByteBuffer The ByteBuffer containing the FlatBuffer data. 935 */ 936 public function dataBuffer() 937 { 938 return $this->bb; 939 } 940 941 /// @cond FLATBUFFERS_INTERNAL 942 /** 943 * @return int 944 */ 945 public function dataStart() 946 { 947 return $this->space; 948 } 949 /// @endcond 950 951 /** 952 * Utility function to copy and return the FlatBuffer data from the 953 * underlying ByteBuffer. 954 * @return string A string (representing a byte[]) that contains a copy 955 * of the FlatBuffer data. 956 */ 957 public function sizedByteArray() 958 { 959 $start = $this->space; 960 $length = $this->bb->capacity() - $this->space; 961 962 $result = str_repeat("\0", $length); 963 $this->bb->setPosition($start); 964 $this->bb->getX($result); 965 966 return $result; 967 } 968 } 969 970 /// @} 971