1 /*
2 * Copyright 2018 Google Inc. All rights reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 extern crate smallvec;
18
19 use std::cmp::max;
20 use std::marker::PhantomData;
21 use std::ptr::write_bytes;
22 use std::slice::from_raw_parts;
23
24 use endian_scalar::{emplace_scalar, read_scalar_at};
25 use primitives::*;
26 use push::{Push, PushAlignment};
27 use table::Table;
28 use vector::{SafeSliceAccess, Vector};
29 use vtable::{field_index_to_field_offset, VTable};
30 use vtable_writer::VTableWriter;
31
32 pub const N_SMALLVEC_STRING_VECTOR_CAPACITY: usize = 16;
33
34 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
35 struct FieldLoc {
36 off: UOffsetT,
37 id: VOffsetT,
38 }
39
40 /// FlatBufferBuilder builds a FlatBuffer through manipulating its internal
41 /// state. It has an owned `Vec<u8>` that grows as needed (up to the hardcoded
42 /// limit of 2GiB, which is set by the FlatBuffers format).
43 #[derive(Clone, Debug, Eq, PartialEq)]
44 pub struct FlatBufferBuilder<'fbb> {
45 owned_buf: Vec<u8>,
46 head: usize,
47
48 field_locs: Vec<FieldLoc>,
49 written_vtable_revpos: Vec<UOffsetT>,
50
51 nested: bool,
52 finished: bool,
53
54 min_align: usize,
55
56 _phantom: PhantomData<&'fbb ()>,
57 }
58
59 impl<'fbb> FlatBufferBuilder<'fbb> {
60 /// Create a FlatBufferBuilder that is ready for writing.
new() -> Self61 pub fn new() -> Self {
62 Self::new_with_capacity(0)
63 }
64
65 /// Create a FlatBufferBuilder that is ready for writing, with a
66 /// ready-to-use capacity of the provided size.
67 ///
68 /// The maximum valid value is `FLATBUFFERS_MAX_BUFFER_SIZE`.
new_with_capacity(size: usize) -> Self69 pub fn new_with_capacity(size: usize) -> Self {
70 // we need to check the size here because we create the backing buffer
71 // directly, bypassing the typical way of using grow_owned_buf:
72 assert!(
73 size <= FLATBUFFERS_MAX_BUFFER_SIZE,
74 "cannot initialize buffer bigger than 2 gigabytes"
75 );
76
77 FlatBufferBuilder {
78 owned_buf: vec![0u8; size],
79 head: size,
80
81 field_locs: Vec::new(),
82 written_vtable_revpos: Vec::new(),
83
84 nested: false,
85 finished: false,
86
87 min_align: 0,
88
89 _phantom: PhantomData,
90 }
91 }
92
93 /// Reset the FlatBufferBuilder internal state. Use this method after a
94 /// call to a `finish` function in order to re-use a FlatBufferBuilder.
95 ///
96 /// This function is the only way to reset the `finished` state and start
97 /// again.
98 ///
99 /// If you are using a FlatBufferBuilder repeatedly, make sure to use this
100 /// function, because it re-uses the FlatBufferBuilder's existing
101 /// heap-allocated `Vec<u8>` internal buffer. This offers significant speed
102 /// improvements as compared to creating a new FlatBufferBuilder for every
103 /// new object.
reset(&mut self)104 pub fn reset(&mut self) {
105 // memset only the part of the buffer that could be dirty:
106 {
107 let to_clear = self.owned_buf.len() - self.head;
108 let ptr = (&mut self.owned_buf[self.head..]).as_mut_ptr();
109 unsafe {
110 write_bytes(ptr, 0, to_clear);
111 }
112 }
113
114 self.head = self.owned_buf.len();
115 self.written_vtable_revpos.clear();
116
117 self.nested = false;
118 self.finished = false;
119
120 self.min_align = 0;
121 }
122
123 /// Destroy the FlatBufferBuilder, returning its internal byte vector
124 /// and the index into it that represents the start of valid data.
collapse(self) -> (Vec<u8>, usize)125 pub fn collapse(self) -> (Vec<u8>, usize) {
126 (self.owned_buf, self.head)
127 }
128
129 /// Push a Push'able value onto the front of the in-progress data.
130 ///
131 /// This function uses traits to provide a unified API for writing
132 /// scalars, tables, vectors, and WIPOffsets.
133 #[inline]
push<P: Push>(&mut self, x: P) -> WIPOffset<P::Output>134 pub fn push<P: Push>(&mut self, x: P) -> WIPOffset<P::Output> {
135 let sz = P::size();
136 self.align(sz, P::alignment());
137 self.make_space(sz);
138 {
139 let (dst, rest) = (&mut self.owned_buf[self.head..]).split_at_mut(sz);
140 x.push(dst, rest);
141 }
142 WIPOffset::new(self.used_space() as UOffsetT)
143 }
144
145 /// Push a Push'able value onto the front of the in-progress data, and
146 /// store a reference to it in the in-progress vtable. If the value matches
147 /// the default, then this is a no-op.
148 #[inline]
push_slot<X: Push + PartialEq>(&mut self, slotoff: VOffsetT, x: X, default: X)149 pub fn push_slot<X: Push + PartialEq>(&mut self, slotoff: VOffsetT, x: X, default: X) {
150 self.assert_nested("push_slot");
151 if x == default {
152 return;
153 }
154 self.push_slot_always(slotoff, x);
155 }
156
157 /// Push a Push'able value onto the front of the in-progress data, and
158 /// store a reference to it in the in-progress vtable.
159 #[inline]
push_slot_always<X: Push>(&mut self, slotoff: VOffsetT, x: X)160 pub fn push_slot_always<X: Push>(&mut self, slotoff: VOffsetT, x: X) {
161 self.assert_nested("push_slot_always");
162 let off = self.push(x);
163 self.track_field(slotoff, off.value());
164 }
165
166 /// Retrieve the number of vtables that have been serialized into the
167 /// FlatBuffer. This is primarily used to check vtable deduplication.
168 #[inline]
num_written_vtables(&self) -> usize169 pub fn num_written_vtables(&self) -> usize {
170 self.written_vtable_revpos.len()
171 }
172
173 /// Start a Table write.
174 ///
175 /// Asserts that the builder is not in a nested state.
176 ///
177 /// Users probably want to use `push_slot` to add values after calling this.
178 #[inline]
start_table(&mut self) -> WIPOffset<TableUnfinishedWIPOffset>179 pub fn start_table(&mut self) -> WIPOffset<TableUnfinishedWIPOffset> {
180 self.assert_not_nested(
181 "start_table can not be called when a table or vector is under construction",
182 );
183 self.nested = true;
184
185 WIPOffset::new(self.used_space() as UOffsetT)
186 }
187
188 /// End a Table write.
189 ///
190 /// Asserts that the builder is in a nested state.
191 #[inline]
end_table( &mut self, off: WIPOffset<TableUnfinishedWIPOffset>, ) -> WIPOffset<TableFinishedWIPOffset>192 pub fn end_table(
193 &mut self,
194 off: WIPOffset<TableUnfinishedWIPOffset>,
195 ) -> WIPOffset<TableFinishedWIPOffset> {
196 self.assert_nested("end_table");
197
198 let o = self.write_vtable(off);
199
200 self.nested = false;
201 self.field_locs.clear();
202
203 WIPOffset::new(o.value())
204 }
205
206 /// Start a Vector write.
207 ///
208 /// Asserts that the builder is not in a nested state.
209 ///
210 /// Most users will prefer to call `create_vector`.
211 /// Speed optimizing users who choose to create vectors manually using this
212 /// function will want to use `push` to add values.
213 #[inline]
start_vector<T: Push>(&mut self, num_items: usize)214 pub fn start_vector<T: Push>(&mut self, num_items: usize) {
215 self.assert_not_nested(
216 "start_vector can not be called when a table or vector is under construction",
217 );
218 self.nested = true;
219 self.align(num_items * T::size(), T::alignment().max_of(SIZE_UOFFSET));
220 }
221
222 /// End a Vector write.
223 ///
224 /// Note that the `num_elems` parameter is the number of written items, not
225 /// the byte count.
226 ///
227 /// Asserts that the builder is in a nested state.
228 #[inline]
end_vector<T: Push>(&mut self, num_elems: usize) -> WIPOffset<Vector<'fbb, T>>229 pub fn end_vector<T: Push>(&mut self, num_elems: usize) -> WIPOffset<Vector<'fbb, T>> {
230 self.assert_nested("end_vector");
231 self.nested = false;
232 let o = self.push::<UOffsetT>(num_elems as UOffsetT);
233 WIPOffset::new(o.value())
234 }
235
236 /// Create a utf8 string.
237 ///
238 /// The wire format represents this as a zero-terminated byte vector.
239 #[inline]
create_string<'a: 'b, 'b>(&'a mut self, s: &'b str) -> WIPOffset<&'fbb str>240 pub fn create_string<'a: 'b, 'b>(&'a mut self, s: &'b str) -> WIPOffset<&'fbb str> {
241 self.assert_not_nested(
242 "create_string can not be called when a table or vector is under construction",
243 );
244 WIPOffset::new(self.create_byte_string(s.as_bytes()).value())
245 }
246
247 /// Create a zero-terminated byte vector.
248 #[inline]
create_byte_string(&mut self, data: &[u8]) -> WIPOffset<&'fbb [u8]>249 pub fn create_byte_string(&mut self, data: &[u8]) -> WIPOffset<&'fbb [u8]> {
250 self.assert_not_nested(
251 "create_byte_string can not be called when a table or vector is under construction",
252 );
253 self.align(data.len() + 1, PushAlignment::new(SIZE_UOFFSET));
254 self.push(0u8);
255 self.push_bytes_unprefixed(data);
256 self.push(data.len() as UOffsetT);
257 WIPOffset::new(self.used_space() as UOffsetT)
258 }
259
260 /// Create a vector by memcpy'ing. This is much faster than calling
261 /// `create_vector`, but the underlying type must be represented as
262 /// little-endian on the host machine. This property is encoded in the
263 /// type system through the SafeSliceAccess trait. The following types are
264 /// always safe, on any platform: bool, u8, i8, and any
265 /// FlatBuffers-generated struct.
266 #[inline]
create_vector_direct<'a: 'b, 'b, T: SafeSliceAccess + Push + Sized + 'b>( &'a mut self, items: &'b [T], ) -> WIPOffset<Vector<'fbb, T>>267 pub fn create_vector_direct<'a: 'b, 'b, T: SafeSliceAccess + Push + Sized + 'b>(
268 &'a mut self,
269 items: &'b [T],
270 ) -> WIPOffset<Vector<'fbb, T>> {
271 self.assert_not_nested(
272 "create_vector_direct can not be called when a table or vector is under construction",
273 );
274 let elem_size = T::size();
275 self.align(items.len() * elem_size, T::alignment().max_of(SIZE_UOFFSET));
276
277 let bytes = {
278 let ptr = items.as_ptr() as *const T as *const u8;
279 unsafe { from_raw_parts(ptr, items.len() * elem_size) }
280 };
281 self.push_bytes_unprefixed(bytes);
282 self.push(items.len() as UOffsetT);
283
284 WIPOffset::new(self.used_space() as UOffsetT)
285 }
286
287 /// Create a vector of strings.
288 ///
289 /// Speed-sensitive users may wish to reduce memory usage by creating the
290 /// vector manually: use `start_vector`, `push`, and `end_vector`.
291 #[inline]
create_vector_of_strings<'a, 'b>( &'a mut self, xs: &'b [&'b str], ) -> WIPOffset<Vector<'fbb, ForwardsUOffset<&'fbb str>>>292 pub fn create_vector_of_strings<'a, 'b>(
293 &'a mut self,
294 xs: &'b [&'b str],
295 ) -> WIPOffset<Vector<'fbb, ForwardsUOffset<&'fbb str>>> {
296 self.assert_not_nested("create_vector_of_strings can not be called when a table or vector is under construction");
297 // internally, smallvec can be a stack-allocated or heap-allocated vector:
298 // if xs.len() > N_SMALLVEC_STRING_VECTOR_CAPACITY then it will overflow to the heap.
299 let mut offsets: smallvec::SmallVec<[WIPOffset<&str>; N_SMALLVEC_STRING_VECTOR_CAPACITY]> =
300 smallvec::SmallVec::with_capacity(xs.len());
301 unsafe {
302 offsets.set_len(xs.len());
303 }
304
305 // note that this happens in reverse, because the buffer is built back-to-front:
306 for (i, &s) in xs.iter().enumerate().rev() {
307 let o = self.create_string(s);
308 offsets[i] = o;
309 }
310 self.create_vector(&offsets[..])
311 }
312
313 /// Create a vector of Push-able objects.
314 ///
315 /// Speed-sensitive users may wish to reduce memory usage by creating the
316 /// vector manually: use `start_vector`, `push`, and `end_vector`.
317 #[inline]
create_vector<'a: 'b, 'b, T: Push + Copy + 'b>( &'a mut self, items: &'b [T], ) -> WIPOffset<Vector<'fbb, T::Output>>318 pub fn create_vector<'a: 'b, 'b, T: Push + Copy + 'b>(
319 &'a mut self,
320 items: &'b [T],
321 ) -> WIPOffset<Vector<'fbb, T::Output>> {
322 let elem_size = T::size();
323 self.align(items.len() * elem_size, T::alignment().max_of(SIZE_UOFFSET));
324 for i in (0..items.len()).rev() {
325 self.push(items[i]);
326 }
327 WIPOffset::new(self.push::<UOffsetT>(items.len() as UOffsetT).value())
328 }
329
330 /// Get the byte slice for the data that has been written, regardless of
331 /// whether it has been finished.
332 #[inline]
unfinished_data(&self) -> &[u8]333 pub fn unfinished_data(&self) -> &[u8] {
334 &self.owned_buf[self.head..]
335 }
336 /// Get the byte slice for the data that has been written after a call to
337 /// one of the `finish` functions.
338 #[inline]
finished_data(&self) -> &[u8]339 pub fn finished_data(&self) -> &[u8] {
340 self.assert_finished("finished_bytes cannot be called when the buffer is not yet finished");
341 &self.owned_buf[self.head..]
342 }
343 /// Assert that a field is present in the just-finished Table.
344 ///
345 /// This is somewhat low-level and is mostly used by the generated code.
346 #[inline]
required( &self, tab_revloc: WIPOffset<TableFinishedWIPOffset>, slot_byte_loc: VOffsetT, assert_msg_name: &'static str, )347 pub fn required(
348 &self,
349 tab_revloc: WIPOffset<TableFinishedWIPOffset>,
350 slot_byte_loc: VOffsetT,
351 assert_msg_name: &'static str,
352 ) {
353 let idx = self.used_space() - tab_revloc.value() as usize;
354 let tab = Table::new(&self.owned_buf[self.head..], idx);
355 let o = tab.vtable().get(slot_byte_loc) as usize;
356 assert!(o != 0, "missing required field {}", assert_msg_name);
357 }
358
359 /// Finalize the FlatBuffer by: aligning it, pushing an optional file
360 /// identifier on to it, pushing a size prefix on to it, and marking the
361 /// internal state of the FlatBufferBuilder as `finished`. Afterwards,
362 /// users can call `finished_data` to get the resulting data.
363 #[inline]
finish_size_prefixed<T>(&mut self, root: WIPOffset<T>, file_identifier: Option<&str>)364 pub fn finish_size_prefixed<T>(&mut self, root: WIPOffset<T>, file_identifier: Option<&str>) {
365 self.finish_with_opts(root, file_identifier, true);
366 }
367
368 /// Finalize the FlatBuffer by: aligning it, pushing an optional file
369 /// identifier on to it, and marking the internal state of the
370 /// FlatBufferBuilder as `finished`. Afterwards, users can call
371 /// `finished_data` to get the resulting data.
372 #[inline]
finish<T>(&mut self, root: WIPOffset<T>, file_identifier: Option<&str>)373 pub fn finish<T>(&mut self, root: WIPOffset<T>, file_identifier: Option<&str>) {
374 self.finish_with_opts(root, file_identifier, false);
375 }
376
377 /// Finalize the FlatBuffer by: aligning it and marking the internal state
378 /// of the FlatBufferBuilder as `finished`. Afterwards, users can call
379 /// `finished_data` to get the resulting data.
380 #[inline]
finish_minimal<T>(&mut self, root: WIPOffset<T>)381 pub fn finish_minimal<T>(&mut self, root: WIPOffset<T>) {
382 self.finish_with_opts(root, None, false);
383 }
384
385 #[inline]
used_space(&self) -> usize386 fn used_space(&self) -> usize {
387 self.owned_buf.len() - self.head as usize
388 }
389
390 #[inline]
track_field(&mut self, slot_off: VOffsetT, off: UOffsetT)391 fn track_field(&mut self, slot_off: VOffsetT, off: UOffsetT) {
392 let fl = FieldLoc { id: slot_off, off };
393 self.field_locs.push(fl);
394 }
395
396 /// Write the VTable, if it is new.
write_vtable( &mut self, table_tail_revloc: WIPOffset<TableUnfinishedWIPOffset>, ) -> WIPOffset<VTableWIPOffset>397 fn write_vtable(
398 &mut self,
399 table_tail_revloc: WIPOffset<TableUnfinishedWIPOffset>,
400 ) -> WIPOffset<VTableWIPOffset> {
401 self.assert_nested("write_vtable");
402
403 // Write the vtable offset, which is the start of any Table.
404 // We fill its value later.
405 let object_revloc_to_vtable: WIPOffset<VTableWIPOffset> =
406 WIPOffset::new(self.push::<UOffsetT>(0xF0F0_F0F0 as UOffsetT).value());
407
408 // Layout of the data this function will create when a new vtable is
409 // needed.
410 // --------------------------------------------------------------------
411 // vtable starts here
412 // | x, x -- vtable len (bytes) [u16]
413 // | x, x -- object inline len (bytes) [u16]
414 // | x, x -- zero, or num bytes from start of object to field #0 [u16]
415 // | ...
416 // | x, x -- zero, or num bytes from start of object to field #n-1 [u16]
417 // vtable ends here
418 // table starts here
419 // | x, x, x, x -- offset (negative direction) to the vtable [i32]
420 // | aka "vtableoffset"
421 // | -- table inline data begins here, we don't touch it --
422 // table ends here -- aka "table_start"
423 // --------------------------------------------------------------------
424 //
425 // Layout of the data this function will create when we re-use an
426 // existing vtable.
427 //
428 // We always serialize this particular vtable, then compare it to the
429 // other vtables we know about to see if there is a duplicate. If there
430 // is, then we erase the serialized vtable we just made.
431 // We serialize it first so that we are able to do byte-by-byte
432 // comparisons with already-serialized vtables. This 1) saves
433 // bookkeeping space (we only keep revlocs to existing vtables), 2)
434 // allows us to convert to little-endian once, then do
435 // fast memcmp comparisons, and 3) by ensuring we are comparing real
436 // serialized vtables, we can be more assured that we are doing the
437 // comparisons correctly.
438 //
439 // --------------------------------------------------------------------
440 // table starts here
441 // | x, x, x, x -- offset (negative direction) to an existing vtable [i32]
442 // | aka "vtableoffset"
443 // | -- table inline data begins here, we don't touch it --
444 // table starts here: aka "table_start"
445 // --------------------------------------------------------------------
446
447 // fill the WIP vtable with zeros:
448 let vtable_byte_len = get_vtable_byte_len(&self.field_locs);
449 self.make_space(vtable_byte_len);
450
451 // compute the length of the table (not vtable!) in bytes:
452 let table_object_size = object_revloc_to_vtable.value() - table_tail_revloc.value();
453 debug_assert!(table_object_size < 0x10000); // vTable use 16bit offsets.
454
455 // Write the VTable (we may delete it afterwards, if it is a duplicate):
456 let vt_start_pos = self.head;
457 let vt_end_pos = self.head + vtable_byte_len;
458 {
459 // write the vtable header:
460 let vtfw = &mut VTableWriter::init(&mut self.owned_buf[vt_start_pos..vt_end_pos]);
461 vtfw.write_vtable_byte_length(vtable_byte_len as VOffsetT);
462 vtfw.write_object_inline_size(table_object_size as VOffsetT);
463
464 // serialize every FieldLoc to the vtable:
465 for &fl in self.field_locs.iter() {
466 let pos: VOffsetT = (object_revloc_to_vtable.value() - fl.off) as VOffsetT;
467 debug_assert_eq!(
468 vtfw.get_field_offset(fl.id),
469 0,
470 "tried to write a vtable field multiple times"
471 );
472 vtfw.write_field_offset(fl.id, pos);
473 }
474 }
475 let dup_vt_use = {
476 let this_vt = VTable::init(&self.owned_buf[..], self.head);
477 self.find_duplicate_stored_vtable_revloc(this_vt)
478 };
479
480 let vt_use = match dup_vt_use {
481 Some(n) => {
482 VTableWriter::init(&mut self.owned_buf[vt_start_pos..vt_end_pos]).clear();
483 self.head += vtable_byte_len;
484 n
485 }
486 None => {
487 let new_vt_use = self.used_space() as UOffsetT;
488 self.written_vtable_revpos.push(new_vt_use);
489 new_vt_use
490 }
491 };
492
493 {
494 let n = self.head + self.used_space() - object_revloc_to_vtable.value() as usize;
495 let saw = read_scalar_at::<UOffsetT>(&self.owned_buf, n);
496 debug_assert_eq!(saw, 0xF0F0_F0F0);
497 emplace_scalar::<SOffsetT>(
498 &mut self.owned_buf[n..n + SIZE_SOFFSET],
499 vt_use as SOffsetT - object_revloc_to_vtable.value() as SOffsetT,
500 );
501 }
502
503 self.field_locs.clear();
504
505 object_revloc_to_vtable
506 }
507
508 #[inline]
find_duplicate_stored_vtable_revloc(&self, needle: VTable) -> Option<UOffsetT>509 fn find_duplicate_stored_vtable_revloc(&self, needle: VTable) -> Option<UOffsetT> {
510 for &revloc in self.written_vtable_revpos.iter().rev() {
511 let o = VTable::init(
512 &self.owned_buf[..],
513 self.head + self.used_space() - revloc as usize,
514 );
515 if needle == o {
516 return Some(revloc);
517 }
518 }
519 None
520 }
521
522 // Only call this when you know it is safe to double the size of the buffer.
523 #[inline]
grow_owned_buf(&mut self)524 fn grow_owned_buf(&mut self) {
525 let old_len = self.owned_buf.len();
526 let new_len = max(1, old_len * 2);
527
528 let starting_active_size = self.used_space();
529
530 let diff = new_len - old_len;
531 self.owned_buf.resize(new_len, 0);
532 self.head += diff;
533
534 let ending_active_size = self.used_space();
535 debug_assert_eq!(starting_active_size, ending_active_size);
536
537 if new_len == 1 {
538 return;
539 }
540
541 // calculate the midpoint, and safely copy the old end data to the new
542 // end position:
543 let middle = new_len / 2;
544 {
545 let (left, right) = &mut self.owned_buf[..].split_at_mut(middle);
546 right.copy_from_slice(left);
547 }
548 // finally, zero out the old end data.
549 {
550 let ptr = (&mut self.owned_buf[..middle]).as_mut_ptr();
551 unsafe {
552 write_bytes(ptr, 0, middle);
553 }
554 }
555 }
556
557 // with or without a size prefix changes how we load the data, so finish*
558 // functions are split along those lines.
finish_with_opts<T>( &mut self, root: WIPOffset<T>, file_identifier: Option<&str>, size_prefixed: bool, )559 fn finish_with_opts<T>(
560 &mut self,
561 root: WIPOffset<T>,
562 file_identifier: Option<&str>,
563 size_prefixed: bool,
564 ) {
565 self.assert_not_finished("buffer cannot be finished when it is already finished");
566 self.assert_not_nested(
567 "buffer cannot be finished when a table or vector is under construction",
568 );
569 self.written_vtable_revpos.clear();
570
571 let to_align = {
572 // for the root offset:
573 let a = SIZE_UOFFSET;
574 // for the size prefix:
575 let b = if size_prefixed { SIZE_UOFFSET } else { 0 };
576 // for the file identifier (a string that is not zero-terminated):
577 let c = if file_identifier.is_some() {
578 FILE_IDENTIFIER_LENGTH
579 } else {
580 0
581 };
582 a + b + c
583 };
584
585 {
586 let ma = PushAlignment::new(self.min_align);
587 self.align(to_align, ma);
588 }
589
590 if let Some(ident) = file_identifier {
591 debug_assert_eq!(ident.len(), FILE_IDENTIFIER_LENGTH);
592 self.push_bytes_unprefixed(ident.as_bytes());
593 }
594
595 self.push(root);
596
597 if size_prefixed {
598 let sz = self.used_space() as UOffsetT;
599 self.push::<UOffsetT>(sz);
600 }
601 self.finished = true;
602 }
603
604 #[inline]
align(&mut self, len: usize, alignment: PushAlignment)605 fn align(&mut self, len: usize, alignment: PushAlignment) {
606 self.track_min_align(alignment.value());
607 let s = self.used_space() as usize;
608 self.make_space(padding_bytes(s + len, alignment.value()));
609 }
610
611 #[inline]
track_min_align(&mut self, alignment: usize)612 fn track_min_align(&mut self, alignment: usize) {
613 self.min_align = max(self.min_align, alignment);
614 }
615
616 #[inline]
push_bytes_unprefixed(&mut self, x: &[u8]) -> UOffsetT617 fn push_bytes_unprefixed(&mut self, x: &[u8]) -> UOffsetT {
618 let n = self.make_space(x.len());
619 self.owned_buf[n..n + x.len()].copy_from_slice(x);
620
621 n as UOffsetT
622 }
623
624 #[inline]
make_space(&mut self, want: usize) -> usize625 fn make_space(&mut self, want: usize) -> usize {
626 self.ensure_capacity(want);
627 self.head -= want;
628 self.head
629 }
630
631 #[inline]
ensure_capacity(&mut self, want: usize) -> usize632 fn ensure_capacity(&mut self, want: usize) -> usize {
633 if self.unused_ready_space() >= want {
634 return want;
635 }
636 assert!(
637 want <= FLATBUFFERS_MAX_BUFFER_SIZE,
638 "cannot grow buffer beyond 2 gigabytes"
639 );
640
641 while self.unused_ready_space() < want {
642 self.grow_owned_buf();
643 }
644 want
645 }
646 #[inline]
unused_ready_space(&self) -> usize647 fn unused_ready_space(&self) -> usize {
648 self.head
649 }
650 #[inline]
assert_nested(&self, fn_name: &'static str)651 fn assert_nested(&self, fn_name: &'static str) {
652 // we don't assert that self.field_locs.len() >0 because the vtable
653 // could be empty (e.g. for empty tables, or for all-default values).
654 debug_assert!(
655 self.nested,
656 format!(
657 "incorrect FlatBufferBuilder usage: {} must be called while in a nested state",
658 fn_name
659 )
660 );
661 }
662 #[inline]
assert_not_nested(&self, msg: &'static str)663 fn assert_not_nested(&self, msg: &'static str) {
664 debug_assert!(!self.nested, msg);
665 }
666 #[inline]
assert_finished(&self, msg: &'static str)667 fn assert_finished(&self, msg: &'static str) {
668 debug_assert!(self.finished, msg);
669 }
670 #[inline]
assert_not_finished(&self, msg: &'static str)671 fn assert_not_finished(&self, msg: &'static str) {
672 debug_assert!(!self.finished, msg);
673 }
674 }
675
676 /// Compute the length of the vtable needed to represent the provided FieldLocs.
677 /// If there are no FieldLocs, then provide the minimum number of bytes
678 /// required: enough to write the VTable header.
679 #[inline]
get_vtable_byte_len(field_locs: &[FieldLoc]) -> usize680 fn get_vtable_byte_len(field_locs: &[FieldLoc]) -> usize {
681 let max_voffset = field_locs.iter().map(|fl| fl.id).max();
682 match max_voffset {
683 None => field_index_to_field_offset(0) as usize,
684 Some(mv) => mv as usize + SIZE_VOFFSET,
685 }
686 }
687
688 #[inline]
padding_bytes(buf_size: usize, scalar_size: usize) -> usize689 fn padding_bytes(buf_size: usize, scalar_size: usize) -> usize {
690 // ((!buf_size) + 1) & (scalar_size - 1)
691 (!buf_size).wrapping_add(1) & (scalar_size.wrapping_sub(1))
692 }
693
694 impl<'fbb> Default for FlatBufferBuilder<'fbb> {
default() -> Self695 fn default() -> Self {
696 Self::new_with_capacity(0)
697 }
698 }
699