blob: 27c16b360306fcafabc11f01ad70a74fe05e5301 [file] [log] [blame]
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001/*
2 * Copyright 2014 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
18using System;
19using System.Collections.Generic;
20using System.Text;
21
22/// @file
23/// @addtogroup flatbuffers_csharp_api
24/// @{
25
26namespace FlatBuffers
27{
28 /// <summary>
29 /// Responsible for building up and accessing a FlatBuffer formatted byte
30 /// array (via ByteBuffer).
31 /// </summary>
32 public class FlatBufferBuilder
33 {
34 private int _space;
35 private ByteBuffer _bb;
36 private int _minAlign = 1;
37
38 // The vtable for the current table (if _vtableSize >= 0)
39 private int[] _vtable = new int[16];
40 // The size of the vtable. -1 indicates no vtable
41 private int _vtableSize = -1;
42 // Starting offset of the current struct/table.
43 private int _objectStart;
44 // List of offsets of all vtables.
45 private int[] _vtables = new int[16];
46 // Number of entries in `vtables` in use.
47 private int _numVtables = 0;
48 // For the current vector being built.
49 private int _vectorNumElems = 0;
50
51 // For CreateSharedString
52 private Dictionary<string, StringOffset> _sharedStringMap = null;
53
54 /// <summary>
55 /// Create a FlatBufferBuilder with a given initial size.
56 /// </summary>
57 /// <param name="initialSize">
58 /// The initial size to use for the internal buffer.
59 /// </param>
60 public FlatBufferBuilder(int initialSize)
61 {
62 if (initialSize <= 0)
63 throw new ArgumentOutOfRangeException("initialSize",
64 initialSize, "Must be greater than zero");
65 _space = initialSize;
66 _bb = new ByteBuffer(initialSize);
67 }
68
69 /// <summary>
70 /// Create a FlatBufferBuilder backed by the pased in ByteBuffer
71 /// </summary>
72 /// <param name="buffer">The ByteBuffer to write to</param>
73 public FlatBufferBuilder(ByteBuffer buffer)
74 {
75 _bb = buffer;
76 _space = buffer.Length;
77 buffer.Reset();
78 }
79
80 /// <summary>
81 /// Reset the FlatBufferBuilder by purging all data that it holds.
82 /// </summary>
83 public void Clear()
84 {
85 _space = _bb.Length;
86 _bb.Reset();
87 _minAlign = 1;
88 while (_vtableSize > 0) _vtable[--_vtableSize] = 0;
89 _vtableSize = -1;
90 _objectStart = 0;
91 _numVtables = 0;
92 _vectorNumElems = 0;
93 }
94
95 /// <summary>
96 /// Gets and sets a Boolean to disable the optimization when serializing
97 /// default values to a Table.
98 ///
99 /// In order to save space, fields that are set to their default value
100 /// don't get serialized into the buffer.
101 /// </summary>
102 public bool ForceDefaults { get; set; }
103
104 /// @cond FLATBUFFERS_INTERNAL
105
106 public int Offset { get { return _bb.Length - _space; } }
107
108 public void Pad(int size)
109 {
110 _bb.PutByte(_space -= size, 0, size);
111 }
112
113 // Doubles the size of the ByteBuffer, and copies the old data towards
114 // the end of the new buffer (since we build the buffer backwards).
115 void GrowBuffer()
116 {
117 _bb.GrowFront(_bb.Length << 1);
118 }
119
120 // Prepare to write an element of `size` after `additional_bytes`
121 // have been written, e.g. if you write a string, you need to align
122 // such the int length field is aligned to SIZEOF_INT, and the string
123 // data follows it directly.
124 // If all you need to do is align, `additional_bytes` will be 0.
125 public void Prep(int size, int additionalBytes)
126 {
127 // Track the biggest thing we've ever aligned to.
128 if (size > _minAlign)
129 _minAlign = size;
130 // Find the amount of alignment needed such that `size` is properly
131 // aligned after `additional_bytes`
132 var alignSize =
133 ((~((int)_bb.Length - _space + additionalBytes)) + 1) &
134 (size - 1);
135 // Reallocate the buffer if needed.
136 while (_space < alignSize + size + additionalBytes)
137 {
138 var oldBufSize = (int)_bb.Length;
139 GrowBuffer();
140 _space += (int)_bb.Length - oldBufSize;
141
142 }
143 if (alignSize > 0)
144 Pad(alignSize);
145 }
146
147 public void PutBool(bool x)
148 {
149 _bb.PutByte(_space -= sizeof(byte), (byte)(x ? 1 : 0));
150 }
151
152 public void PutSbyte(sbyte x)
153 {
154 _bb.PutSbyte(_space -= sizeof(sbyte), x);
155 }
156
157 public void PutByte(byte x)
158 {
159 _bb.PutByte(_space -= sizeof(byte), x);
160 }
161
162 public void PutShort(short x)
163 {
164 _bb.PutShort(_space -= sizeof(short), x);
165 }
166
167 public void PutUshort(ushort x)
168 {
169 _bb.PutUshort(_space -= sizeof(ushort), x);
170 }
171
172 public void PutInt(int x)
173 {
174 _bb.PutInt(_space -= sizeof(int), x);
175 }
176
177 public void PutUint(uint x)
178 {
179 _bb.PutUint(_space -= sizeof(uint), x);
180 }
181
182 public void PutLong(long x)
183 {
184 _bb.PutLong(_space -= sizeof(long), x);
185 }
186
187 public void PutUlong(ulong x)
188 {
189 _bb.PutUlong(_space -= sizeof(ulong), x);
190 }
191
192 public void PutFloat(float x)
193 {
194 _bb.PutFloat(_space -= sizeof(float), x);
195 }
196
197 /// <summary>
198 /// Puts an array of type T into this builder at the
199 /// current offset
200 /// </summary>
201 /// <typeparam name="T">The type of the input data </typeparam>
202 /// <param name="x">The array to copy data from</param>
203 public void Put<T>(T[] x)
204 where T : struct
205 {
206 _space = _bb.Put(_space, x);
207 }
208
209#if ENABLE_SPAN_T
210 /// <summary>
211 /// Puts a span of type T into this builder at the
212 /// current offset
213 /// </summary>
214 /// <typeparam name="T">The type of the input data </typeparam>
215 /// <param name="x">The span to copy data from</param>
216 public void Put<T>(Span<T> x)
217 where T : struct
218 {
219 _space = _bb.Put(_space, x);
220 }
221#endif
222
223 public void PutDouble(double x)
224 {
225 _bb.PutDouble(_space -= sizeof(double), x);
226 }
227 /// @endcond
228
229 /// <summary>
230 /// Add a `bool` to the buffer (aligns the data and grows if necessary).
231 /// </summary>
232 /// <param name="x">The `bool` to add to the buffer.</param>
233 public void AddBool(bool x) { Prep(sizeof(byte), 0); PutBool(x); }
234
235 /// <summary>
236 /// Add a `sbyte` to the buffer (aligns the data and grows if necessary).
237 /// </summary>
238 /// <param name="x">The `sbyte` to add to the buffer.</param>
239 public void AddSbyte(sbyte x) { Prep(sizeof(sbyte), 0); PutSbyte(x); }
240
241 /// <summary>
242 /// Add a `byte` to the buffer (aligns the data and grows if necessary).
243 /// </summary>
244 /// <param name="x">The `byte` to add to the buffer.</param>
245 public void AddByte(byte x) { Prep(sizeof(byte), 0); PutByte(x); }
246
247 /// <summary>
248 /// Add a `short` to the buffer (aligns the data and grows if necessary).
249 /// </summary>
250 /// <param name="x">The `short` to add to the buffer.</param>
251 public void AddShort(short x) { Prep(sizeof(short), 0); PutShort(x); }
252
253 /// <summary>
254 /// Add an `ushort` to the buffer (aligns the data and grows if necessary).
255 /// </summary>
256 /// <param name="x">The `ushort` to add to the buffer.</param>
257 public void AddUshort(ushort x) { Prep(sizeof(ushort), 0); PutUshort(x); }
258
259 /// <summary>
260 /// Add an `int` to the buffer (aligns the data and grows if necessary).
261 /// </summary>
262 /// <param name="x">The `int` to add to the buffer.</param>
263 public void AddInt(int x) { Prep(sizeof(int), 0); PutInt(x); }
264
265 /// <summary>
266 /// Add an `uint` to the buffer (aligns the data and grows if necessary).
267 /// </summary>
268 /// <param name="x">The `uint` to add to the buffer.</param>
269 public void AddUint(uint x) { Prep(sizeof(uint), 0); PutUint(x); }
270
271 /// <summary>
272 /// Add a `long` to the buffer (aligns the data and grows if necessary).
273 /// </summary>
274 /// <param name="x">The `long` to add to the buffer.</param>
275 public void AddLong(long x) { Prep(sizeof(long), 0); PutLong(x); }
276
277 /// <summary>
278 /// Add an `ulong` to the buffer (aligns the data and grows if necessary).
279 /// </summary>
280 /// <param name="x">The `ulong` to add to the buffer.</param>
281 public void AddUlong(ulong x) { Prep(sizeof(ulong), 0); PutUlong(x); }
282
283 /// <summary>
284 /// Add a `float` to the buffer (aligns the data and grows if necessary).
285 /// </summary>
286 /// <param name="x">The `float` to add to the buffer.</param>
287 public void AddFloat(float x) { Prep(sizeof(float), 0); PutFloat(x); }
288
289 /// <summary>
290 /// Add an array of type T to the buffer (aligns the data and grows if necessary).
291 /// </summary>
292 /// <typeparam name="T">The type of the input data</typeparam>
293 /// <param name="x">The array to copy data from</param>
294 public void Add<T>(T[] x)
295 where T : struct
296 {
297 if (x == null)
298 {
299 throw new ArgumentNullException("Cannot add a null array");
300 }
301
302 if( x.Length == 0)
303 {
304 // don't do anything if the array is empty
305 return;
306 }
307
308 if(!ByteBuffer.IsSupportedType<T>())
309 {
310 throw new ArgumentException("Cannot add this Type array to the builder");
311 }
312
313 int size = ByteBuffer.SizeOf<T>();
314 // Need to prep on size (for data alignment) and then we pass the
315 // rest of the length (minus 1) as additional bytes
316 Prep(size, size * (x.Length - 1));
317 Put(x);
318 }
319
320#if ENABLE_SPAN_T
321 /// <summary>
322 /// Add a span of type T to the buffer (aligns the data and grows if necessary).
323 /// </summary>
324 /// <typeparam name="T">The type of the input data</typeparam>
325 /// <param name="x">The span to copy data from</param>
326 public void Add<T>(Span<T> x)
327 where T : struct
328 {
329 if (!ByteBuffer.IsSupportedType<T>())
330 {
331 throw new ArgumentException("Cannot add this Type array to the builder");
332 }
333
334 int size = ByteBuffer.SizeOf<T>();
335 // Need to prep on size (for data alignment) and then we pass the
336 // rest of the length (minus 1) as additional bytes
337 Prep(size, size * (x.Length - 1));
338 Put(x);
339 }
340#endif
341
342 /// <summary>
343 /// Add a `double` to the buffer (aligns the data and grows if necessary).
344 /// </summary>
345 /// <param name="x">The `double` to add to the buffer.</param>
346 public void AddDouble(double x) { Prep(sizeof(double), 0);
347 PutDouble(x); }
348
349 /// <summary>
350 /// Adds an offset, relative to where it will be written.
351 /// </summary>
352 /// <param name="off">The offset to add to the buffer.</param>
353 public void AddOffset(int off)
354 {
355 Prep(sizeof(int), 0); // Ensure alignment is already done.
356 if (off > Offset)
357 throw new ArgumentException();
358
359 off = Offset - off + sizeof(int);
360 PutInt(off);
361 }
362
363 /// @cond FLATBUFFERS_INTERNAL
364 public void StartVector(int elemSize, int count, int alignment)
365 {
366 NotNested();
367 _vectorNumElems = count;
368 Prep(sizeof(int), elemSize * count);
369 Prep(alignment, elemSize * count); // Just in case alignment > int.
370 }
371 /// @endcond
372
373 /// <summary>
374 /// Writes data necessary to finish a vector construction.
375 /// </summary>
376 public VectorOffset EndVector()
377 {
378 PutInt(_vectorNumElems);
379 return new VectorOffset(Offset);
380 }
381
382 /// <summary>
383 /// Creates a vector of tables.
384 /// </summary>
385 /// <param name="offsets">Offsets of the tables.</param>
386 public VectorOffset CreateVectorOfTables<T>(Offset<T>[] offsets) where T : struct
387 {
388 NotNested();
389 StartVector(sizeof(int), offsets.Length, sizeof(int));
390 for (int i = offsets.Length - 1; i >= 0; i--) AddOffset(offsets[i].Value);
391 return EndVector();
392 }
393
394 /// @cond FLATBUFFERS_INTENRAL
395 public void Nested(int obj)
396 {
397 // Structs are always stored inline, so need to be created right
398 // where they are used. You'll get this assert if you created it
399 // elsewhere.
400 if (obj != Offset)
401 throw new Exception(
402 "FlatBuffers: struct must be serialized inline.");
403 }
404
405 public void NotNested()
406 {
407 // You should not be creating any other objects or strings/vectors
408 // while an object is being constructed
409 if (_vtableSize >= 0)
410 throw new Exception(
411 "FlatBuffers: object serialization must not be nested.");
412 }
413
414 public void StartTable(int numfields)
415 {
416 if (numfields < 0)
417 throw new ArgumentOutOfRangeException("Flatbuffers: invalid numfields");
418
419 NotNested();
420
421 if (_vtable.Length < numfields)
422 _vtable = new int[numfields];
423
424 _vtableSize = numfields;
425 _objectStart = Offset;
426 }
427
428
429 // Set the current vtable at `voffset` to the current location in the
430 // buffer.
431 public void Slot(int voffset)
432 {
433 if (voffset >= _vtableSize)
434 throw new IndexOutOfRangeException("Flatbuffers: invalid voffset");
435
436 _vtable[voffset] = Offset;
437 }
438
439 /// <summary>
440 /// Adds a Boolean to the Table at index `o` in its vtable using the value `x` and default `d`
441 /// </summary>
442 /// <param name="o">The index into the vtable</param>
443 /// <param name="x">The value to put into the buffer. If the value is equal to the default
444 /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param>
445 /// <param name="d">The default value to compare the value against</param>
446 public void AddBool(int o, bool x, bool d) { if (ForceDefaults || x != d) { AddBool(x); Slot(o); } }
447
448 /// <summary>
449 /// Adds a SByte to the Table at index `o` in its vtable using the value `x` and default `d`
450 /// </summary>
451 /// <param name="o">The index into the vtable</param>
452 /// <param name="x">The value to put into the buffer. If the value is equal to the default
453 /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param>
454 /// <param name="d">The default value to compare the value against</param>
455 public void AddSbyte(int o, sbyte x, sbyte d) { if (ForceDefaults || x != d) { AddSbyte(x); Slot(o); } }
456
457 /// <summary>
458 /// Adds a Byte to the Table at index `o` in its vtable using the value `x` and default `d`
459 /// </summary>
460 /// <param name="o">The index into the vtable</param>
461 /// <param name="x">The value to put into the buffer. If the value is equal to the default
462 /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param>
463 /// <param name="d">The default value to compare the value against</param>
464 public void AddByte(int o, byte x, byte d) { if (ForceDefaults || x != d) { AddByte(x); Slot(o); } }
465
466 /// <summary>
467 /// Adds a Int16 to the Table at index `o` in its vtable using the value `x` and default `d`
468 /// </summary>
469 /// <param name="o">The index into the vtable</param>
470 /// <param name="x">The value to put into the buffer. If the value is equal to the default
471 /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param>
472 /// <param name="d">The default value to compare the value against</param>
473 public void AddShort(int o, short x, int d) { if (ForceDefaults || x != d) { AddShort(x); Slot(o); } }
474
475 /// <summary>
476 /// Adds a UInt16 to the Table at index `o` in its vtable using the value `x` and default `d`
477 /// </summary>
478 /// <param name="o">The index into the vtable</param>
479 /// <param name="x">The value to put into the buffer. If the value is equal to the default
480 /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param>
481 /// <param name="d">The default value to compare the value against</param>
482 public void AddUshort(int o, ushort x, ushort d) { if (ForceDefaults || x != d) { AddUshort(x); Slot(o); } }
483
484 /// <summary>
485 /// Adds an Int32 to the Table at index `o` in its vtable using the value `x` and default `d`
486 /// </summary>
487 /// <param name="o">The index into the vtable</param>
488 /// <param name="x">The value to put into the buffer. If the value is equal to the default
489 /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param>
490 /// <param name="d">The default value to compare the value against</param>
491 public void AddInt(int o, int x, int d) { if (ForceDefaults || x != d) { AddInt(x); Slot(o); } }
492
493 /// <summary>
494 /// Adds a UInt32 to the Table at index `o` in its vtable using the value `x` and default `d`
495 /// </summary>
496 /// <param name="o">The index into the vtable</param>
497 /// <param name="x">The value to put into the buffer. If the value is equal to the default
498 /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param>
499 /// <param name="d">The default value to compare the value against</param>
500 public void AddUint(int o, uint x, uint d) { if (ForceDefaults || x != d) { AddUint(x); Slot(o); } }
501
502 /// <summary>
503 /// Adds an Int64 to the Table at index `o` in its vtable using the value `x` and default `d`
504 /// </summary>
505 /// <param name="o">The index into the vtable</param>
506 /// <param name="x">The value to put into the buffer. If the value is equal to the default
507 /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param>
508 /// <param name="d">The default value to compare the value against</param>
509 public void AddLong(int o, long x, long d) { if (ForceDefaults || x != d) { AddLong(x); Slot(o); } }
510
511 /// <summary>
512 /// Adds a UInt64 to the Table at index `o` in its vtable using the value `x` and default `d`
513 /// </summary>
514 /// <param name="o">The index into the vtable</param>
515 /// <param name="x">The value to put into the buffer. If the value is equal to the default
516 /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param>
517 /// <param name="d">The default value to compare the value against</param>
518 public void AddUlong(int o, ulong x, ulong d) { if (ForceDefaults || x != d) { AddUlong(x); Slot(o); } }
519
520 /// <summary>
521 /// Adds a Single to the Table at index `o` in its vtable using the value `x` and default `d`
522 /// </summary>
523 /// <param name="o">The index into the vtable</param>
524 /// <param name="x">The value to put into the buffer. If the value is equal to the default
525 /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param>
526 /// <param name="d">The default value to compare the value against</param>
527 public void AddFloat(int o, float x, double d) { if (ForceDefaults || x != d) { AddFloat(x); Slot(o); } }
528
529 /// <summary>
530 /// Adds a Double to the Table at index `o` in its vtable using the value `x` and default `d`
531 /// </summary>
532 /// <param name="o">The index into the vtable</param>
533 /// <param name="x">The value to put into the buffer. If the value is equal to the default
534 /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param>
535 /// <param name="d">The default value to compare the value against</param>
536 public void AddDouble(int o, double x, double d) { if (ForceDefaults || x != d) { AddDouble(x); Slot(o); } }
537
538 /// <summary>
539 /// Adds a buffer offset to the Table at index `o` in its vtable using the value `x` and default `d`
540 /// </summary>
541 /// <param name="o">The index into the vtable</param>
542 /// <param name="x">The value to put into the buffer. If the value is equal to the default
543 /// the value will be skipped.</param>
544 /// <param name="d">The default value to compare the value against</param>
545 public void AddOffset(int o, int x, int d) { if (x != d) { AddOffset(x); Slot(o); } }
546 /// @endcond
547
548 /// <summary>
549 /// Encode the string `s` in the buffer using UTF-8.
550 /// </summary>
551 /// <param name="s">The string to encode.</param>
552 /// <returns>
553 /// The offset in the buffer where the encoded string starts.
554 /// </returns>
555 public StringOffset CreateString(string s)
556 {
557 NotNested();
558 AddByte(0);
559 var utf8StringLen = Encoding.UTF8.GetByteCount(s);
560 StartVector(1, utf8StringLen, 1);
561 _bb.PutStringUTF8(_space -= utf8StringLen, s);
562 return new StringOffset(EndVector().Value);
563 }
564
565
566#if ENABLE_SPAN_T
567 /// <summary>
568 /// Creates a string in the buffer from a Span containing
569 /// a UTF8 string.
570 /// </summary>
571 /// <param name="chars">the UTF8 string to add to the buffer</param>
572 /// <returns>
573 /// The offset in the buffer where the encoded string starts.
574 /// </returns>
575 public StringOffset CreateUTF8String(Span<byte> chars)
576 {
577 NotNested();
578 AddByte(0);
579 var utf8StringLen = chars.Length;
580 StartVector(1, utf8StringLen, 1);
581 _space = _bb.Put(_space, chars);
582 return new StringOffset(EndVector().Value);
583 }
584#endif
585
586 /// <summary>
587 /// Store a string in the buffer, which can contain any binary data.
588 /// If a string with this exact contents has already been serialized before,
589 /// instead simply returns the offset of the existing string.
590 /// </summary>
591 /// <param name="s">The string to encode.</param>
592 /// <returns>
593 /// The offset in the buffer where the encoded string starts.
594 /// </returns>
595 public StringOffset CreateSharedString(string s)
596 {
597 if (_sharedStringMap == null)
598 {
599 _sharedStringMap = new Dictionary<string, StringOffset>();
600 }
601
602 if (_sharedStringMap.ContainsKey(s))
603 {
604 return _sharedStringMap[s];
605 }
606
607 var stringOffset = CreateString(s);
608 _sharedStringMap.Add(s, stringOffset);
609 return stringOffset;
610 }
611
612 /// @cond FLATBUFFERS_INTERNAL
613 // Structs are stored inline, so nothing additional is being added.
614 // `d` is always 0.
615 public void AddStruct(int voffset, int x, int d)
616 {
617 if (x != d)
618 {
619 Nested(x);
620 Slot(voffset);
621 }
622 }
623
624 public int EndTable()
625 {
626 if (_vtableSize < 0)
627 throw new InvalidOperationException(
628 "Flatbuffers: calling EndTable without a StartTable");
629
630 AddInt((int)0);
631 var vtableloc = Offset;
632 // Write out the current vtable.
633 int i = _vtableSize - 1;
634 // Trim trailing zeroes.
635 for (; i >= 0 && _vtable[i] == 0; i--) {}
636 int trimmedSize = i + 1;
637 for (; i >= 0 ; i--) {
638 // Offset relative to the start of the table.
639 short off = (short)(_vtable[i] != 0
640 ? vtableloc - _vtable[i]
641 : 0);
642 AddShort(off);
643
644 // clear out written entry
645 _vtable[i] = 0;
646 }
647
648 const int standardFields = 2; // The fields below:
649 AddShort((short)(vtableloc - _objectStart));
650 AddShort((short)((trimmedSize + standardFields) *
651 sizeof(short)));
652
653 // Search for an existing vtable that matches the current one.
654 int existingVtable = 0;
655 for (i = 0; i < _numVtables; i++) {
656 int vt1 = _bb.Length - _vtables[i];
657 int vt2 = _space;
658 short len = _bb.GetShort(vt1);
659 if (len == _bb.GetShort(vt2)) {
660 for (int j = sizeof(short); j < len; j += sizeof(short)) {
661 if (_bb.GetShort(vt1 + j) != _bb.GetShort(vt2 + j)) {
662 goto endLoop;
663 }
664 }
665 existingVtable = _vtables[i];
666 break;
667 }
668
669 endLoop: { }
670 }
671
672 if (existingVtable != 0) {
673 // Found a match:
674 // Remove the current vtable.
675 _space = _bb.Length - vtableloc;
676 // Point table to existing vtable.
677 _bb.PutInt(_space, existingVtable - vtableloc);
678 } else {
679 // No match:
680 // Add the location of the current vtable to the list of
681 // vtables.
682 if (_numVtables == _vtables.Length)
683 {
684 // Arrays.CopyOf(vtables num_vtables * 2);
685 var newvtables = new int[ _numVtables * 2];
686 Array.Copy(_vtables, newvtables, _vtables.Length);
687
688 _vtables = newvtables;
689 };
690 _vtables[_numVtables++] = Offset;
691 // Point table to current vtable.
692 _bb.PutInt(_bb.Length - vtableloc, Offset - vtableloc);
693 }
694
695 _vtableSize = -1;
696 return vtableloc;
697 }
698
699 // This checks a required field has been set in a given table that has
700 // just been constructed.
701 public void Required(int table, int field)
702 {
703 int table_start = _bb.Length - table;
704 int vtable_start = table_start - _bb.GetInt(table_start);
705 bool ok = _bb.GetShort(vtable_start + field) != 0;
706 // If this fails, the caller will show what field needs to be set.
707 if (!ok)
708 throw new InvalidOperationException("FlatBuffers: field " + field +
709 " must be set");
710 }
711 /// @endcond
712
713 /// <summary>
714 /// Finalize a buffer, pointing to the given `root_table`.
715 /// </summary>
716 /// <param name="rootTable">
717 /// An offset to be added to the buffer.
718 /// </param>
719 /// <param name="sizePrefix">
720 /// Whether to prefix the size to the buffer.
721 /// </param>
722 protected void Finish(int rootTable, bool sizePrefix)
723 {
724 Prep(_minAlign, sizeof(int) + (sizePrefix ? sizeof(int) : 0));
725 AddOffset(rootTable);
726 if (sizePrefix) {
727 AddInt(_bb.Length - _space);
728 }
729 _bb.Position = _space;
730 }
731
732 /// <summary>
733 /// Finalize a buffer, pointing to the given `root_table`.
734 /// </summary>
735 /// <param name="rootTable">
736 /// An offset to be added to the buffer.
737 /// </param>
738 public void Finish(int rootTable)
739 {
740 Finish(rootTable, false);
741 }
742
743 /// <summary>
744 /// Finalize a buffer, pointing to the given `root_table`, with the size prefixed.
745 /// </summary>
746 /// <param name="rootTable">
747 /// An offset to be added to the buffer.
748 /// </param>
749 public void FinishSizePrefixed(int rootTable)
750 {
751 Finish(rootTable, true);
752 }
753
754 /// <summary>
755 /// Get the ByteBuffer representing the FlatBuffer.
756 /// </summary>
757 /// <remarks>
758 /// This is typically only called after you call `Finish()`.
759 /// The actual data starts at the ByteBuffer's current position,
760 /// not necessarily at `0`.
761 /// </remarks>
762 /// <returns>
763 /// Returns the ByteBuffer for this FlatBuffer.
764 /// </returns>
765 public ByteBuffer DataBuffer { get { return _bb; } }
766
767 /// <summary>
768 /// A utility function to copy and return the ByteBuffer data as a
769 /// `byte[]`.
770 /// </summary>
771 /// <returns>
772 /// A full copy of the FlatBuffer data.
773 /// </returns>
774 public byte[] SizedByteArray()
775 {
776 return _bb.ToSizedArray();
777 }
778
779 /// <summary>
780 /// Finalize a buffer, pointing to the given `rootTable`.
781 /// </summary>
782 /// <param name="rootTable">
783 /// An offset to be added to the buffer.
784 /// </param>
785 /// <param name="fileIdentifier">
786 /// A FlatBuffer file identifier to be added to the buffer before
787 /// `root_table`.
788 /// </param>
789 /// <param name="sizePrefix">
790 /// Whether to prefix the size to the buffer.
791 /// </param>
792 protected void Finish(int rootTable, string fileIdentifier, bool sizePrefix)
793 {
794 Prep(_minAlign, sizeof(int) + (sizePrefix ? sizeof(int) : 0) +
795 FlatBufferConstants.FileIdentifierLength);
796 if (fileIdentifier.Length !=
797 FlatBufferConstants.FileIdentifierLength)
798 throw new ArgumentException(
799 "FlatBuffers: file identifier must be length " +
800 FlatBufferConstants.FileIdentifierLength,
801 "fileIdentifier");
802 for (int i = FlatBufferConstants.FileIdentifierLength - 1; i >= 0;
803 i--)
804 {
805 AddByte((byte)fileIdentifier[i]);
806 }
807 Finish(rootTable, sizePrefix);
808 }
809
810 /// <summary>
811 /// Finalize a buffer, pointing to the given `rootTable`.
812 /// </summary>
813 /// <param name="rootTable">
814 /// An offset to be added to the buffer.
815 /// </param>
816 /// <param name="fileIdentifier">
817 /// A FlatBuffer file identifier to be added to the buffer before
818 /// `root_table`.
819 /// </param>
820 public void Finish(int rootTable, string fileIdentifier)
821 {
822 Finish(rootTable, fileIdentifier, false);
823 }
824
825 /// <summary>
826 /// Finalize a buffer, pointing to the given `rootTable`, with the size prefixed.
827 /// </summary>
828 /// <param name="rootTable">
829 /// An offset to be added to the buffer.
830 /// </param>
831 /// <param name="fileIdentifier">
832 /// A FlatBuffer file identifier to be added to the buffer before
833 /// `root_table`.
834 /// </param>
835 public void FinishSizePrefixed(int rootTable, string fileIdentifier)
836 {
837 Finish(rootTable, fileIdentifier, true);
838 }
839 }
840}
841
842/// @}