blob: 925b4387d16eea5809b7f4d20953db10b080a5d9 [file] [log] [blame]
Shuhei Taunma5ce86822015-11-05 16:19:28 +09001<?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
Mark Klara69a31b82015-12-03 20:30:54 -080018/// @file
19/// @addtogroup flatbuffers_php_api
20/// @{
21
Shuhei Taunma5ce86822015-11-05 16:19:28 +090022namespace Google\FlatBuffers;
23
24class FlatbufferBuilder
25{
26 /**
Mark Klara69a31b82015-12-03 20:30:54 -080027 * Internal ByteBuffer for the FlatBuffer data.
Shuhei Taunma5ce86822015-11-05 16:19:28 +090028 * @var ByteBuffer $bb
29 */
30 public $bb;
31
Mark Klara69a31b82015-12-03 20:30:54 -080032 /// @cond FLATBUFFERS_INTERNAL
Shuhei Taunma5ce86822015-11-05 16:19:28 +090033 /**
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;
Mark Klara69a31b82015-12-03 20:30:54 -080082 /// @endcond
Shuhei Taunma5ce86822015-11-05 16:19:28 +090083
84 /**
Mark Klara69a31b82015-12-03 20:30:54 -080085 * Create a FlatBufferBuilder with a given initial size.
Shuhei Taunma5ce86822015-11-05 16:19:28 +090086 *
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
Mark Klara69a31b82015-12-03 20:30:54 -080098 /// @cond FLATBUFFERS_INTERNAL
Shuhei Taunma5ce86822015-11-05 16:19:28 +090099 /**
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 /**
Mark Klara69a31b82015-12-03 20:30:54 -0800111 * Returns the current ByteBuffer offset.
Shuhei Taunma5ce86822015-11-05 16:19:28 +0900112 *
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) {
Antoine Descampscf7e4b02016-06-08 09:32:37 +0200236 throw new \InvalidArgumentException("your platform can't handle uint correctly. use 64bit machine.");
Shuhei Taunma5ce86822015-11-05 16:19:28 +0900237 }
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) {
Antoine Descamps64fa8ba2016-07-19 12:22:37 +0200248 throw new \InvalidArgumentException("Your platform can't handle long correctly. Use a 64bit machine.");
Shuhei Taunma5ce86822015-11-05 16:19:28 +0900249 }
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) {
Antoine Descamps64fa8ba2016-07-19 12:22:37 +0200260 throw new \InvalidArgumentException("Your platform can't handle ulong correctly. This is a php limitation. Please wait for the extension release.");
Shuhei Taunma5ce86822015-11-05 16:19:28 +0900261 }
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 }
Mark Klara69a31b82015-12-03 20:30:54 -0800281 /// @endcond
Shuhei Taunma5ce86822015-11-05 16:19:28 +0900282
283 /**
Mark Klara69a31b82015-12-03 20:30:54 -0800284 * Add a `bool` to the buffer, properly aligned, and grows the buffer (if necessary).
285 * @param $x The `bool` to add to the buffer.
Shuhei Taunma5ce86822015-11-05 16:19:28 +0900286 */
287 public function addBool($x)
288 {
289 $this->prep(1, 0);
290 $this->putBool($x);
291 }
292
293 /**
Mark Klara69a31b82015-12-03 20:30:54 -0800294 * Add a `byte` to the buffer, properly aligned, and grows the buffer (if necessary).
295 * @param $x The `byte` to add to the buffer.
Shuhei Taunma5ce86822015-11-05 16:19:28 +0900296 */
297 public function addByte($x)
298 {
299 $this->prep(1, 0);
300 $this->putByte($x);
301 }
302
303 /**
Mark Klara69a31b82015-12-03 20:30:54 -0800304 * 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.
Shuhei Taunma5ce86822015-11-05 16:19:28 +0900306 */
307 public function addSbyte($x)
308 {
309 $this->prep(1, 0);
310 $this->putSbyte($x);
311 }
312
313 /**
Mark Klara69a31b82015-12-03 20:30:54 -0800314 * Add a `short` to the buffer, properly aligned, and grows the buffer (if necessary).
315 * @param $x The `short` to add to the buffer.
Shuhei Taunma5ce86822015-11-05 16:19:28 +0900316 */
317 public function addShort($x)
318 {
319 $this->prep(2, 0);
320 $this->putShort($x);
321 }
322
323 /**
Mark Klara69a31b82015-12-03 20:30:54 -0800324 * 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.
Shuhei Taunma5ce86822015-11-05 16:19:28 +0900326 */
327 public function addUshort($x)
328 {
329 $this->prep(2, 0);
330 $this->putUshort($x);
331 }
332
333 /**
Mark Klara69a31b82015-12-03 20:30:54 -0800334 * Add an `int` to the buffer, properly aligned, and grows the buffer (if necessary).
335 * @param $x The `int` to add to the buffer.
Shuhei Taunma5ce86822015-11-05 16:19:28 +0900336 */
337 public function addInt($x)
338 {
339 $this->prep(4, 0);
340 $this->putInt($x);
341 }
342
343 /**
Mark Klara69a31b82015-12-03 20:30:54 -0800344 * 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.
Shuhei Taunma5ce86822015-11-05 16:19:28 +0900346 */
347 public function addUint($x)
348 {
349 $this->prep(4, 0);
350 $this->putUint($x);
351 }
352
Shuhei Taunma5ce86822015-11-05 16:19:28 +0900353 /**
Mark Klara69a31b82015-12-03 20:30:54 -0800354 * Add a `long` to the buffer, properly aligned, and grows the buffer (if necessary).
355 * @param $x The `long` to add to the buffer.
Shuhei Taunma5ce86822015-11-05 16:19:28 +0900356 */
357 public function addLong($x)
358 {
359 $this->prep(8, 0);
360 $this->putLong($x);
361 }
362
363 /**
Mark Klara69a31b82015-12-03 20:30:54 -0800364 * 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.
Shuhei Taunma5ce86822015-11-05 16:19:28 +0900366 */
367 public function addUlong($x)
368 {
369 $this->prep(8, 0);
370 $this->putUlong($x);
371 }
372
373 /**
Mark Klara69a31b82015-12-03 20:30:54 -0800374 * Add a `float` to the buffer, properly aligned, and grows the buffer (if necessary).
375 * @param $x The `float` to add to the buffer.
Shuhei Taunma5ce86822015-11-05 16:19:28 +0900376 */
377 public function addFloat($x)
378 {
379 $this->prep(4, 0);
380 $this->putFloat($x);
381 }
382
383 /**
Mark Klara69a31b82015-12-03 20:30:54 -0800384 * Add a `double` to the buffer, properly aligned, and grows the buffer (if necessary).
385 * @param $x The `double` to add to the buffer.
Shuhei Taunma5ce86822015-11-05 16:19:28 +0900386 */
387 public function addDouble($x)
388 {
389 $this->prep(8, 0);
390 $this->putDouble($x);
391 }
392
Mark Klara69a31b82015-12-03 20:30:54 -0800393 /// @cond FLATBUFFERS_INTERNAL
Shuhei Taunma5ce86822015-11-05 16:19:28 +0900394 /**
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 }
Mark Klara69a31b82015-12-03 20:30:54 -0800551 /// @endcond
Shuhei Taunma5ce86822015-11-05 16:19:28 +0900552
553 /**
Mark Klara69a31b82015-12-03 20:30:54 -0800554 * 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.
Shuhei Taunma5ce86822015-11-05 16:19:28 +0900558 */
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
Mark Klara69a31b82015-12-03 20:30:54 -0800570 /// @cond FLATBUFFERS_INTERNAL
Shuhei Taunma5ce86822015-11-05 16:19:28 +0900571 /**
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 {
Antoine Descampsdbecdf22016-11-16 18:54:57 +0100596 if (function_exists('mb_detect_encoding')) {
597 return (bool) mb_detect_encoding($bytes, 'UTF-8', true);
598 }
Wouter van Oortmerssenac1015e2017-08-21 13:44:23 -0700599
Shuhei Taunma5ce86822015-11-05 16:19:28 +0900600 $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 }
Mark Klara69a31b82015-12-03 20:30:54 -0800699 /// @endcond
Shuhei Taunma5ce86822015-11-05 16:19:28 +0900700
701 /**
Mark Klara69a31b82015-12-03 20:30:54 -0800702 * 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.
Shuhei Taunma5ce86822015-11-05 16:19:28 +0900707 */
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
Mark Klara69a31b82015-12-03 20:30:54 -0800724 /// @cond FLATBUFFERS_INTERNAL
Shuhei Taunma5ce86822015-11-05 16:19:28 +0900725 /**
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
Wouter van Oortmerssenac1015e2017-08-21 13:44:23 -0700815 $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--) {
Shuhei Taunma5ce86822015-11-05 16:19:28 +0900820 $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);
Wouter van Oortmerssenac1015e2017-08-21 13:44:23 -0700826 $this->addShort(($trimmed_size + $standard_fields) * Constants::SIZEOF_SHORT);
Shuhei Taunma5ce86822015-11-05 16:19:28 +0900827
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 }
Mark Klara69a31b82015-12-03 20:30:54 -0800888 /// @endcond
Shuhei Taunma5ce86822015-11-05 16:19:28 +0900889
890 /**
Mark Klara69a31b82015-12-03 20:30:54 -0800891 * 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`.
Shuhei Taunma5ce86822015-11-05 16:19:28 +0900898 */
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 /**
Mark Klara69a31b82015-12-03 20:30:54 -0800922 * 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.
Shuhei Taunma5ce86822015-11-05 16:19:28 +0900926 */
927 public function forceDefaults($forceDefaults)
928 {
929 $this->force_defaults = $forceDefaults;
930 }
931
932 /**
Mark Klara69a31b82015-12-03 20:30:54 -0800933 * Get the ByteBuffer representing the FlatBuffer.
934 * @return ByteBuffer The ByteBuffer containing the FlatBuffer data.
Shuhei Taunma5ce86822015-11-05 16:19:28 +0900935 */
936 public function dataBuffer()
937 {
938 return $this->bb;
939 }
940
Mark Klara69a31b82015-12-03 20:30:54 -0800941 /// @cond FLATBUFFERS_INTERNAL
Shuhei Taunma5ce86822015-11-05 16:19:28 +0900942 /**
943 * @return int
944 */
945 public function dataStart()
946 {
947 return $this->space;
948 }
Mark Klara69a31b82015-12-03 20:30:54 -0800949 /// @endcond
Shuhei Taunma5ce86822015-11-05 16:19:28 +0900950
951 /**
Mark Klara69a31b82015-12-03 20:30:54 -0800952 * 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.
Shuhei Taunma5ce86822015-11-05 16:19:28 +0900956 */
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}
Mark Klara69a31b82015-12-03 20:30:54 -0800969
970/// @}