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 22namespace Google\FlatBuffers; 23 24final 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 282 /** 283 * @param $off 284 */ 285 public function putOffset($off) 286 { 287 $new_off = $this->offset() - $off + Constants::SIZEOF_INT; 288 $this->putInt($new_off); 289 } 290 /// @endcond 291 292 /** 293 * Add a `bool` to the buffer, properly aligned, and grows the buffer (if necessary). 294 * @param $x The `bool` to add to the buffer. 295 */ 296 public function addBool($x) 297 { 298 $this->prep(1, 0); 299 $this->putBool($x); 300 } 301 302 /** 303 * Add a `byte` to the buffer, properly aligned, and grows the buffer (if necessary). 304 * @param $x The `byte` to add to the buffer. 305 */ 306 public function addByte($x) 307 { 308 $this->prep(1, 0); 309 $this->putByte($x); 310 } 311 312 /** 313 * Add a `signed byte` to the buffer, properly aligned, and grows the buffer (if necessary). 314 * @param $x The `signed byte` to add to the buffer. 315 */ 316 public function addSbyte($x) 317 { 318 $this->prep(1, 0); 319 $this->putSbyte($x); 320 } 321 322 /** 323 * Add a `short` to the buffer, properly aligned, and grows the buffer (if necessary). 324 * @param $x The `short` to add to the buffer. 325 */ 326 public function addShort($x) 327 { 328 $this->prep(2, 0); 329 $this->putShort($x); 330 } 331 332 /** 333 * Add an `unsigned short` to the buffer, properly aligned, and grows the buffer (if necessary). 334 * @param $x The `unsigned short` to add to the buffer. 335 */ 336 public function addUshort($x) 337 { 338 $this->prep(2, 0); 339 $this->putUshort($x); 340 } 341 342 /** 343 * Add an `int` to the buffer, properly aligned, and grows the buffer (if necessary). 344 * @param $x The `int` to add to the buffer. 345 */ 346 public function addInt($x) 347 { 348 $this->prep(4, 0); 349 $this->putInt($x); 350 } 351 352 /** 353 * Add an `unsigned int` to the buffer, properly aligned, and grows the buffer (if necessary). 354 * @param $x The `unsigned int` to add to the buffer. 355 */ 356 public function addUint($x) 357 { 358 $this->prep(4, 0); 359 $this->putUint($x); 360 } 361 362 /** 363 * Add a `long` to the buffer, properly aligned, and grows the buffer (if necessary). 364 * @param $x The `long` to add to the buffer. 365 */ 366 public function addLong($x) 367 { 368 $this->prep(8, 0); 369 $this->putLong($x); 370 } 371 372 /** 373 * Add an `unsigned long` to the buffer, properly aligned, and grows the buffer (if necessary). 374 * @param $x The `unsigned long` to add to the buffer. 375 */ 376 public function addUlong($x) 377 { 378 $this->prep(8, 0); 379 $this->putUlong($x); 380 } 381 382 /** 383 * Add a `float` to the buffer, properly aligned, and grows the buffer (if necessary). 384 * @param $x The `float` to add to the buffer. 385 */ 386 public function addFloat($x) 387 { 388 $this->prep(4, 0); 389 $this->putFloat($x); 390 } 391 392 /** 393 * Add a `double` to the buffer, properly aligned, and grows the buffer (if necessary). 394 * @param $x The `double` to add to the buffer. 395 */ 396 public function addDouble($x) 397 { 398 $this->prep(8, 0); 399 $this->putDouble($x); 400 } 401 402 /// @cond FLATBUFFERS_INTERNAL 403 /** 404 * @param $o 405 * @param $x 406 * @param $d 407 */ 408 public function addBoolX($o, $x, $d) 409 { 410 if ($this->force_defaults || $x != $d) { 411 $this->addBool($x); 412 $this->slot($o); 413 } 414 } 415 416 /** 417 * @param $o 418 * @param $x 419 * @param $d 420 */ 421 public function addByteX($o, $x, $d) 422 { 423 if ($this->force_defaults || $x != $d) { 424 $this->addByte($x); 425 $this->slot($o); 426 } 427 } 428 429 /** 430 * @param $o 431 * @param $x 432 * @param $d 433 */ 434 public function addSbyteX($o, $x, $d) 435 { 436 if ($this->force_defaults || $x != $d) { 437 $this->addSbyte($x); 438 $this->slot($o); 439 } 440 } 441 442 /** 443 * @param $o 444 * @param $x 445 * @param $d 446 */ 447 public function addShortX($o, $x, $d) 448 { 449 if ($this->force_defaults || $x != $d) { 450 $this->addShort($x); 451 $this->slot($o); 452 } 453 } 454 455 /** 456 * @param $o 457 * @param $x 458 * @param $d 459 */ 460 public function addUshortX($o, $x, $d) 461 { 462 if ($this->force_defaults || $x != $d) { 463 $this->addUshort($x); 464 $this->slot($o); 465 } 466 } 467 468 /** 469 * @param $o 470 * @param $x 471 * @param $d 472 */ 473 public function addIntX($o, $x, $d) 474 { 475 if ($this->force_defaults || $x != $d) { 476 $this->addInt($x); 477 $this->slot($o); 478 } 479 } 480 481 /** 482 * @param $o 483 * @param $x 484 * @param $d 485 */ 486 public function addUintX($o, $x, $d) 487 { 488 if ($this->force_defaults || $x != $d) { 489 $this->addUint($x); 490 $this->slot($o); 491 } 492 } 493 494 /** 495 * @param $o 496 * @param $x 497 * @param $d 498 */ 499 public function addLongX($o, $x, $d) 500 { 501 if ($this->force_defaults || $x != $d) { 502 $this->addLong($x); 503 $this->slot($o); 504 } 505 } 506 507 /** 508 * @param $o 509 * @param $x 510 * @param $d 511 */ 512 public function addUlongX($o, $x, $d) 513 { 514 if ($this->force_defaults || $x != $d) { 515 $this->addUlong($x); 516 $this->slot($o); 517 } 518 } 519 520 521 /** 522 * @param $o 523 * @param $x 524 * @param $d 525 */ 526 public function addFloatX($o, $x, $d) 527 { 528 if ($this->force_defaults || $x != $d) { 529 $this->addFloat($x); 530 $this->slot($o); 531 } 532 } 533 534 /** 535 * @param $o 536 * @param $x 537 * @param $d 538 */ 539 public function addDoubleX($o, $x, $d) 540 { 541 if ($this->force_defaults || $x != $d) { 542 $this->addDouble($x); 543 $this->slot($o); 544 } 545 } 546 547 /** 548 * @param $o 549 * @param $x 550 * @param $d 551 * @throws \Exception 552 */ 553 public function addOffsetX($o, $x, $d) 554 { 555 if ($this->force_defaults || $x != $d) { 556 $this->addOffset($x); 557 $this->slot($o); 558 } 559 } 560 /// @endcond 561 562 /** 563 * Adds on offset, relative to where it will be written. 564 * @param $off The offset to add to the buffer. 565 * @throws \Exception Throws an exception if `$off` is greater than the underlying ByteBuffer's 566 * offest. 567 */ 568 public function addOffset($off) 569 { 570 $this->prep(Constants::SIZEOF_INT, 0); // Ensure alignment is already done 571 if ($off > $this->offset()) { 572 throw new \Exception(""); 573 } 574 $this->putOffset($off); 575 } 576 577 /// @cond FLATBUFFERS_INTERNAL 578 /** 579 * @param $elem_size 580 * @param $num_elems 581 * @param $alignment 582 * @throws \Exception 583 */ 584 public function startVector($elem_size, $num_elems, $alignment) 585 { 586 $this->notNested(); 587 $this->vector_num_elems = $num_elems; 588 $this->prep(Constants::SIZEOF_INT, $elem_size * $num_elems); 589 $this->prep($alignment, $elem_size * $num_elems); // Just in case alignemnt > int; 590 } 591 592 /** 593 * @return int 594 */ 595 public function endVector() 596 { 597 $this->putUint($this->vector_num_elems); 598 return $this->offset(); 599 } 600 601 protected function is_utf8($bytes) 602 { 603 if (function_exists('mb_detect_encoding')) { 604 return (bool) mb_detect_encoding($bytes, 'UTF-8', true); 605 } 606 607 $len = strlen($bytes); 608 if ($len < 1) { 609 /* NOTE: always return 1 when passed string is null */ 610 return true; 611 } 612 613 for ($j = 0, $i = 0; $i < $len; $i++) { 614 // check ACII 615 if ($bytes[$j] == "\x09" || 616 $bytes[$j] == "\x0A" || 617 $bytes[$j] == "\x0D" || 618 ($bytes[$j] >= "\x20" && $bytes[$j] <= "\x7E")) { 619 $j++; 620 continue; 621 } 622 623 /* non-overlong 2-byte */ 624 if ((($i+1) <= $len) && 625 ($bytes[$j] >= "\xC2" && $bytes[$j] <= "\xDF" && 626 ($bytes[$j+1] >= "\x80" && $bytes[$j+1] <= "\xBF"))) { 627 $j += 2; 628 $i++; 629 continue; 630 } 631 632 /* excluding overlongs */ 633 if ((($i + 2) <= $len) && 634 $bytes[$j] == "\xE0" && 635 ($bytes[$j+1] >= "\xA0" && $bytes[$j+1] <= "\xBF" && 636 ($bytes[$j+2] >= "\x80" && $bytes[$j+2] <= "\xBF"))) { 637 $bytes += 3; 638 $i +=2; 639 continue; 640 } 641 642 /* straight 3-byte */ 643 if ((($i+2) <= $len) && 644 (($bytes[$j] >= "\xE1" && $bytes[$j] <= "\xEC") || 645 $bytes[$j] == "\xEE" || 646 $bytes[$j] = "\xEF") && 647 ($bytes[$j+1] >= "\x80" && $bytes[$j+1] <= "\xBF") && 648 ($bytes[$j+2] >= "\x80" && $bytes[$j+2] <= "\xBF")) { 649 $j += 3; 650 $i += 2; 651 continue; 652 } 653 654 /* excluding surrogates */ 655 if ((($i+2) <= $len) && 656 $bytes[$j] == "\xED" && 657 ($bytes[$j+1] >= "\x80" && $bytes[$j+1] <= "\x9f" && 658 ($bytes[$j+2] >= "\x80" && $bytes[$j+2] <= "\xBF"))) { 659 $j += 3; 660 $i += 2; 661 continue; 662 } 663 664 /* planes 1-3 */ 665 if ((($i + 3) <= $len) && 666 $bytes[$j] == "\xF0" && 667 ($bytes[$j+1] >= "\x90" && $bytes[$j+1] <= "\xBF") && 668 ($bytes[$j+2] >= "\x80" && $bytes[$j+2] <= "\xBF") && 669 ($bytes[$j+3] >= "\x80" && $bytes[$j+3] <= "\xBF")) { 670 $j += 4; 671 $i += 3; 672 continue; 673 } 674 675 676 /* planes 4-15 */ 677 if ((($i+3) <= $len) && 678 $bytes[$j] >= "\xF1" && $bytes[$j] <= "\xF3" && 679 $bytes[$j+1] >= "\x80" && $bytes[$j+1] <= "\xBF" && 680 $bytes[$j+2] >= "\x80" && $bytes[$j+2] <= "\xBF" && 681 $bytes[$j+3] >= "\x80" && $bytes[$j+3] <= "\xBF" 682 ) { 683 $j += 4; 684 $i += 3; 685 continue; 686 } 687 688 /* plane 16 */ 689 if ((($i+3) <= $len) && 690 $bytes[$j] == "\xF4" && 691 ($bytes[$j+1] >= "\x80" && $bytes[$j+1] <= "\x8F") && 692 ($bytes[$j+2] >= "\x80" && $bytes[$j+2] <= "\xBF") && 693 ($bytes[$j+3] >= "\x80" && $bytes[$j+3] <= "\xBF") 694 ) { 695 $bytes += 4; 696 $i += 3; 697 continue; 698 } 699 700 701 return false; 702 } 703 704 return true; 705 } 706 /// @endcond 707 708 /** 709 * Encode the string `$s` in the buffer using UTF-8. 710 * @param string $s The string to encode. 711 * @return int The offset in the buffer where the encoded string starts. 712 * @throws InvalidArgumentException Thrown if the input string `$s` is not 713 * UTF-8. 714 */ 715 public function createString($s) 716 { 717 if (!$this->is_utf8($s)) { 718 throw new \InvalidArgumentException("string must be utf-8 encoded value."); 719 } 720 721 $this->notNested(); 722 $this->addByte(0); // null terminated 723 $this->startVector(1, strlen($s), 1); 724 $this->space -= strlen($s); 725 for ($i = $this->space, $j = 0 ; $j < strlen($s) ; $i++, $j++) { 726 $this->bb->_buffer[$i] = $s[$j]; 727 } 728 return $this->endVector(); 729 } 730 731 /// @cond FLATBUFFERS_INTERNAL 732 /** 733 * @throws \Exception 734 */ 735 public function notNested() 736 { 737 if ($this->nested) { 738 throw new \Exception("FlatBuffers; object serialization must not be nested"); 739 } 740 } 741 742 /** 743 * @param $obj 744 * @throws \Exception 745 */ 746 public function nested($obj) 747 { 748 if ($obj != $this->offset()) { 749 throw new \Exception("FlatBuffers: struct must be serialized inline"); 750 } 751 } 752 753 /** 754 * @param $numfields 755 * @throws \Exception 756 */ 757 public function startObject($numfields) 758 { 759 $this->notNested(); 760 if ($this->vtable == null || count($this->vtable) < $numfields) { 761 $this->vtable = array(); 762 } 763 764 $this->vtable_in_use = $numfields; 765 for ($i = 0; $i < $numfields; $i++) { 766 $this->vtable[$i] = 0; 767 } 768 769 $this->nested = true; 770 $this->object_start = $this->offset(); 771 } 772 773 /** 774 * @param $voffset 775 * @param $x 776 * @param $d 777 * @throws \Exception 778 */ 779 public function addStructX($voffset, $x, $d) 780 { 781 if ($x != $d) { 782 $this->nested($x); 783 $this->slot($voffset); 784 } 785 } 786 787 /** 788 * @param $voffset 789 * @param $x 790 * @param $d 791 * @throws \Exception 792 */ 793 public function addStruct($voffset, $x, $d) 794 { 795 if ($x != $d) { 796 $this->nested($x); 797 $this->slot($voffset); 798 } 799 } 800 801 /** 802 * @param $voffset 803 */ 804 public function slot($voffset) 805 { 806 $this->vtable[$voffset] = $this->offset(); 807 } 808 809 /** 810 * @return int 811 * @throws \Exception 812 */ 813 public function endObject() 814 { 815 if ($this->vtable == null || !$this->nested) { 816 throw new \Exception("FlatBuffers: endObject called without startObject"); 817 } 818 819 $this->addInt(0); 820 $vtableloc = $this->offset(); 821 822 $i = $this->vtable_in_use -1; 823 // Trim trailing zeroes. 824 for (; $i >= 0 && $this->vtable[$i] == 0; $i--) {} 825 $trimmed_size = $i + 1; 826 for (; $i >= 0; $i--) { 827 $off = ($this->vtable[$i] != 0) ? $vtableloc - $this->vtable[$i] : 0; 828 $this->addShort($off); 829 } 830 831 $standard_fields = 2; // the fields below 832 $this->addShort($vtableloc - $this->object_start); 833 $this->addShort(($trimmed_size + $standard_fields) * Constants::SIZEOF_SHORT); 834 835 // search for an existing vtable that matches the current one. 836 $existing_vtable = 0; 837 838 for ($i = 0; $i < $this->num_vtables; $i++) { 839 $vt1 = $this->bb->capacity() - $this->vtables[$i]; 840 $vt2 = $this->space; 841 842 $len = $this->bb->getShort($vt1); 843 844 if ($len == $this->bb->getShort($vt2)) { 845 for ($j = Constants::SIZEOF_SHORT; $j < $len; $j += Constants::SIZEOF_SHORT) { 846 if ($this->bb->getShort($vt1 + $j) != $this->bb->getShort($vt2 + $j)) { 847 continue 2; 848 } 849 } 850 $existing_vtable = $this->vtables[$i]; 851 break; 852 } 853 } 854 855 if ($existing_vtable != 0) { 856 // Found a match: 857 // Remove the current vtable 858 $this->space = $this->bb->capacity() - $vtableloc; 859 $this->bb->putInt($this->space, $existing_vtable - $vtableloc); 860 } else { 861 // No Match: 862 // Add the location of the current vtable to the list of vtables 863 if ($this->num_vtables == count($this->vtables)) { 864 $vtables = $this->vtables; 865 $this->vtables = array(); 866 // copy of 867 for ($i = 0; $i < count($vtables) * 2; $i++) { 868 $this->vtables[$i] = ($i < count($vtables)) ? $vtables[$i] : 0; 869 } 870 } 871 $this->vtables[$this->num_vtables++] = $this->offset(); 872 $this->bb->putInt($this->bb->capacity() - $vtableloc, $this->offset() - $vtableloc); 873 } 874 875 $this->nested = false; 876 $this->vtable = null; 877 return $vtableloc; 878 } 879 880 /** 881 * @param $table 882 * @param $field 883 * @throws \Exception 884 */ 885 public function required($table, $field) 886 { 887 $table_start = $this->bb->capacity() - $table; 888 $vtable_start = $table_start - $this->bb->getInt($table_start); 889 $ok = $this->bb->getShort($vtable_start + $field) != 0; 890 891 if (!$ok) { 892 throw new \Exception("FlatBuffers: field " . $field . " must be set"); 893 } 894 } 895 /// @endcond 896 897 /** 898 * Finalize a buffer, pointing to the given `$root_table`. 899 * @param $root_table An offest to be added to the buffer. 900 * @param $file_identifier A FlatBuffer file identifier to be added to the 901 * buffer before `$root_table`. This defaults to `null`. 902 * @throws InvalidArgumentException Thrown if an invalid `$identifier` is 903 * given, where its length is not equal to 904 * `Constants::FILE_IDENTIFIER_LENGTH`. 905 */ 906 public function finish($root_table, $identifier = null) 907 { 908 if ($identifier == null) { 909 $this->prep($this->minalign, Constants::SIZEOF_INT); 910 $this->addOffset($root_table); 911 $this->bb->setPosition($this->space); 912 } else { 913 $this->prep($this->minalign, Constants::SIZEOF_INT + Constants::FILE_IDENTIFIER_LENGTH); 914 if (strlen($identifier) != Constants::FILE_IDENTIFIER_LENGTH) { 915 throw new \InvalidArgumentException( 916 sprintf("FlatBuffers: file identifier must be length %d", 917 Constants::FILE_IDENTIFIER_LENGTH)); 918 } 919 920 for ($i = Constants::FILE_IDENTIFIER_LENGTH - 1; $i >= 0; 921 $i--) { 922 $this->addByte(ord($identifier[$i])); 923 } 924 $this->finish($root_table); 925 } 926 } 927 928 /** 929 * In order to save space, fields that are set to their default value don't 930 * get serialized into the buffer. 931 * @param bool $forceDefaults When set to `true`, always serializes default 932 * values. 933 */ 934 public function forceDefaults($forceDefaults) 935 { 936 $this->force_defaults = $forceDefaults; 937 } 938 939 /** 940 * Get the ByteBuffer representing the FlatBuffer. 941 * @return ByteBuffer The ByteBuffer containing the FlatBuffer data. 942 */ 943 public function dataBuffer() 944 { 945 return $this->bb; 946 } 947 948 /// @cond FLATBUFFERS_INTERNAL 949 /** 950 * @return int 951 */ 952 public function dataStart() 953 { 954 return $this->space; 955 } 956 /// @endcond 957 958 /** 959 * Utility function to copy and return the FlatBuffer data from the 960 * underlying ByteBuffer. 961 * @return string A string (representing a byte[]) that contains a copy 962 * of the FlatBuffer data. 963 */ 964 public function sizedByteArray() 965 { 966 $start = $this->space; 967 $length = $this->bb->capacity() - $this->space; 968 969 $result = str_repeat("\0", $length); 970 $this->bb->setPosition($start); 971 $this->bb->getX($result); 972 973 return $result; 974 } 975} 976 977/// @} 978