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