• 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(new byte[initialSize]);
63         }
64 
65         /// <summary>
66         /// Reset the FlatBufferBuilder by purging all data that it holds.
67         /// </summary>
Clear()68         public void Clear()
69         {
70             _space = _bb.Length;
71             _bb.Reset();
72             _minAlign = 1;
73             while (_vtableSize > 0) _vtable[--_vtableSize] = 0;
74             _vtableSize = -1;
75             _objectStart = 0;
76             _numVtables = 0;
77             _vectorNumElems = 0;
78         }
79 
80         /// <summary>
81         /// Gets and sets a Boolean to disable the optimization when serializing
82         /// default values to a Table.
83         ///
84         /// In order to save space, fields that are set to their default value
85         /// don't get serialized into the buffer.
86         /// </summary>
87         public bool ForceDefaults { get; set; }
88 
89         /// @cond FLATBUFFERS_INTERNAL
90 
91         public int Offset { get { return _bb.Length - _space; } }
92 
Pad(int size)93         public void Pad(int size)
94         {
95              _bb.PutByte(_space -= size, 0, size);
96         }
97 
98         // Doubles the size of the ByteBuffer, and copies the old data towards
99         // the end of the new buffer (since we build the buffer backwards).
GrowBuffer()100         void GrowBuffer()
101         {
102             var oldBuf = _bb.Data;
103             var oldBufSize = oldBuf.Length;
104             if ((oldBufSize & 0xC0000000) != 0)
105                 throw new Exception(
106                     "FlatBuffers: cannot grow buffer beyond 2 gigabytes.");
107 
108             var newBufSize = oldBufSize << 1;
109             var newBuf = new byte[newBufSize];
110 
111             Buffer.BlockCopy(oldBuf, 0, newBuf, newBufSize - oldBufSize,
112                              oldBufSize);
113             _bb = new ByteBuffer(newBuf, newBufSize);
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 
PutDouble(double x)193         public void PutDouble(double x)
194         {
195             _bb.PutDouble(_space -= sizeof(double), x);
196         }
197         /// @endcond
198 
199         /// <summary>
200         /// Add a `bool` to the buffer (aligns the data and grows if necessary).
201         /// </summary>
202         /// <param name="x">The `bool` to add to the buffer.</param>
AddBool(bool x)203         public void AddBool(bool x) { Prep(sizeof(byte), 0); PutBool(x); }
204 
205         /// <summary>
206         /// Add a `sbyte` to the buffer (aligns the data and grows if necessary).
207         /// </summary>
208         /// <param name="x">The `sbyte` to add to the buffer.</param>
AddSbyte(sbyte x)209         public void AddSbyte(sbyte x) { Prep(sizeof(sbyte), 0); PutSbyte(x); }
210 
211         /// <summary>
212         /// Add a `byte` to the buffer (aligns the data and grows if necessary).
213         /// </summary>
214         /// <param name="x">The `byte` to add to the buffer.</param>
AddByte(byte x)215         public void AddByte(byte x) { Prep(sizeof(byte), 0); PutByte(x); }
216 
217         /// <summary>
218         /// Add a `short` to the buffer (aligns the data and grows if necessary).
219         /// </summary>
220         /// <param name="x">The `short` to add to the buffer.</param>
AddShort(short x)221         public void AddShort(short x) { Prep(sizeof(short), 0); PutShort(x); }
222 
223         /// <summary>
224         /// Add an `ushort` to the buffer (aligns the data and grows if necessary).
225         /// </summary>
226         /// <param name="x">The `ushort` to add to the buffer.</param>
AddUshort(ushort x)227         public void AddUshort(ushort x) { Prep(sizeof(ushort), 0); PutUshort(x); }
228 
229         /// <summary>
230         /// Add an `int` to the buffer (aligns the data and grows if necessary).
231         /// </summary>
232         /// <param name="x">The `int` to add to the buffer.</param>
AddInt(int x)233         public void AddInt(int x) { Prep(sizeof(int), 0); PutInt(x); }
234 
235         /// <summary>
236         /// Add an `uint` to the buffer (aligns the data and grows if necessary).
237         /// </summary>
238         /// <param name="x">The `uint` to add to the buffer.</param>
AddUint(uint x)239         public void AddUint(uint x) { Prep(sizeof(uint), 0); PutUint(x); }
240 
241         /// <summary>
242         /// Add a `long` to the buffer (aligns the data and grows if necessary).
243         /// </summary>
244         /// <param name="x">The `long` to add to the buffer.</param>
AddLong(long x)245         public void AddLong(long x) { Prep(sizeof(long), 0); PutLong(x); }
246 
247         /// <summary>
248         /// Add an `ulong` to the buffer (aligns the data and grows if necessary).
249         /// </summary>
250         /// <param name="x">The `ulong` to add to the buffer.</param>
AddUlong(ulong x)251         public void AddUlong(ulong x) { Prep(sizeof(ulong), 0); PutUlong(x); }
252 
253         /// <summary>
254         /// Add a `float` to the buffer (aligns the data and grows if necessary).
255         /// </summary>
256         /// <param name="x">The `float` to add to the buffer.</param>
AddFloat(float x)257         public void AddFloat(float x) { Prep(sizeof(float), 0); PutFloat(x); }
258 
259         /// <summary>
260         /// Add a `double` to the buffer (aligns the data and grows if necessary).
261         /// </summary>
262         /// <param name="x">The `double` to add to the buffer.</param>
AddDouble(double x)263         public void AddDouble(double x) { Prep(sizeof(double), 0);
264                                           PutDouble(x); }
265 
266         /// <summary>
267         /// Adds an offset, relative to where it will be written.
268         /// </summary>
269         /// <param name="off">The offset to add to the buffer.</param>
AddOffset(int off)270         public void AddOffset(int off)
271         {
272             Prep(sizeof(int), 0);  // Ensure alignment is already done.
273             if (off > Offset)
274                 throw new ArgumentException();
275 
276             off = Offset - off + sizeof(int);
277             PutInt(off);
278         }
279 
280         /// @cond FLATBUFFERS_INTERNAL
StartVector(int elemSize, int count, int alignment)281         public void StartVector(int elemSize, int count, int alignment)
282         {
283             NotNested();
284             _vectorNumElems = count;
285             Prep(sizeof(int), elemSize * count);
286             Prep(alignment, elemSize * count); // Just in case alignment > int.
287         }
288         /// @endcond
289 
290         /// <summary>
291         /// Writes data necessary to finish a vector construction.
292         /// </summary>
EndVector()293         public VectorOffset EndVector()
294         {
295             PutInt(_vectorNumElems);
296             return new VectorOffset(Offset);
297         }
298 
299         /// <summary>
300         /// Creates a vector of tables.
301         /// </summary>
302         /// <param name="offsets">Offsets of the tables.</param>
303         public VectorOffset CreateVectorOfTables<T>(Offset<T>[] offsets) where T : struct
304         {
NotNestedFlatBuffers.FlatBufferBuilder.__anon1305             NotNested();
sizeofFlatBuffers.FlatBufferBuilder.__anon1306             StartVector(sizeof(int), offsets.Length, sizeof(int));
AddOffsetFlatBuffers.FlatBufferBuilder.__anon1307             for (int i = offsets.Length - 1; i >= 0; i--) AddOffset(offsets[i].Value);
308             return EndVector();
309         }
310 
311         /// @cond FLATBUFFERS_INTENRAL
Nested(int obj)312         public void Nested(int obj)
313         {
314             // Structs are always stored inline, so need to be created right
315             // where they are used. You'll get this assert if you created it
316             // elsewhere.
317             if (obj != Offset)
318                 throw new Exception(
319                     "FlatBuffers: struct must be serialized inline.");
320         }
321 
NotNested()322         public void NotNested()
323         {
324             // You should not be creating any other objects or strings/vectors
325             // while an object is being constructed
326             if (_vtableSize >= 0)
327                 throw new Exception(
328                     "FlatBuffers: object serialization must not be nested.");
329         }
330 
StartObject(int numfields)331         public void StartObject(int numfields)
332         {
333             if (numfields < 0)
334                 throw new ArgumentOutOfRangeException("Flatbuffers: invalid numfields");
335 
336             NotNested();
337 
338             if (_vtable.Length < numfields)
339                 _vtable = new int[numfields];
340 
341             _vtableSize = numfields;
342             _objectStart = Offset;
343         }
344 
345 
346         // Set the current vtable at `voffset` to the current location in the
347         // buffer.
Slot(int voffset)348         public void Slot(int voffset)
349         {
350             if (voffset >= _vtableSize)
351                 throw new IndexOutOfRangeException("Flatbuffers: invalid voffset");
352 
353             _vtable[voffset] = Offset;
354         }
355 
356         /// <summary>
357         /// Adds a Boolean to the Table at index `o` in its vtable using the value `x` and default `d`
358         /// </summary>
359         /// <param name="o">The index into the vtable</param>
360         /// <param name="x">The value to put into the buffer. If the value is equal to the default
361         /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param>
362         /// <param name="d">The default value to compare the value against</param>
AddBool(int o, bool x, bool d)363         public void AddBool(int o, bool x, bool d) { if (ForceDefaults || x != d) { AddBool(x); Slot(o); } }
364 
365         /// <summary>
366         /// Adds a SByte to the Table at index `o` in its vtable using the value `x` and default `d`
367         /// </summary>
368         /// <param name="o">The index into the vtable</param>
369         /// <param name="x">The value to put into the buffer. If the value is equal to the default
370         /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param>
371         /// <param name="d">The default value to compare the value against</param>
AddSbyte(int o, sbyte x, sbyte d)372         public void AddSbyte(int o, sbyte x, sbyte d) { if (ForceDefaults || x != d) { AddSbyte(x); Slot(o); } }
373 
374         /// <summary>
375         /// Adds a Byte to the Table at index `o` in its vtable using the value `x` and default `d`
376         /// </summary>
377         /// <param name="o">The index into the vtable</param>
378         /// <param name="x">The value to put into the buffer. If the value is equal to the default
379         /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param>
380         /// <param name="d">The default value to compare the value against</param>
AddByte(int o, byte x, byte d)381         public void AddByte(int o, byte x, byte d) { if (ForceDefaults || x != d) { AddByte(x); Slot(o); } }
382 
383         /// <summary>
384         /// Adds a Int16 to the Table at index `o` in its vtable using the value `x` and default `d`
385         /// </summary>
386         /// <param name="o">The index into the vtable</param>
387         /// <param name="x">The value to put into the buffer. If the value is equal to the default
388         /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param>
389         /// <param name="d">The default value to compare the value against</param>
AddShort(int o, short x, int d)390         public void AddShort(int o, short x, int d) { if (ForceDefaults || x != d) { AddShort(x); Slot(o); } }
391 
392         /// <summary>
393         /// Adds a UInt16 to the Table at index `o` in its vtable using the value `x` and default `d`
394         /// </summary>
395         /// <param name="o">The index into the vtable</param>
396         /// <param name="x">The value to put into the buffer. If the value is equal to the default
397         /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param>
398         /// <param name="d">The default value to compare the value against</param>
AddUshort(int o, ushort x, ushort d)399         public void AddUshort(int o, ushort x, ushort d) { if (ForceDefaults || x != d) { AddUshort(x); Slot(o); } }
400 
401         /// <summary>
402         /// Adds an Int32 to the Table at index `o` in its vtable using the value `x` and default `d`
403         /// </summary>
404         /// <param name="o">The index into the vtable</param>
405         /// <param name="x">The value to put into the buffer. If the value is equal to the default
406         /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param>
407         /// <param name="d">The default value to compare the value against</param>
AddInt(int o, int x, int d)408         public void AddInt(int o, int x, int d) { if (ForceDefaults || x != d) { AddInt(x); Slot(o); } }
409 
410         /// <summary>
411         /// Adds a UInt32 to the Table at index `o` in its vtable using the value `x` and default `d`
412         /// </summary>
413         /// <param name="o">The index into the vtable</param>
414         /// <param name="x">The value to put into the buffer. If the value is equal to the default
415         /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param>
416         /// <param name="d">The default value to compare the value against</param>
AddUint(int o, uint x, uint d)417         public void AddUint(int o, uint x, uint d) { if (ForceDefaults || x != d) { AddUint(x); Slot(o); } }
418 
419         /// <summary>
420         /// Adds an Int64 to the Table at index `o` in its vtable using the value `x` and default `d`
421         /// </summary>
422         /// <param name="o">The index into the vtable</param>
423         /// <param name="x">The value to put into the buffer. If the value is equal to the default
424         /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param>
425         /// <param name="d">The default value to compare the value against</param>
AddLong(int o, long x, long d)426         public void AddLong(int o, long x, long d) { if (ForceDefaults || x != d) { AddLong(x); Slot(o); } }
427 
428         /// <summary>
429         /// Adds a UInt64 to the Table at index `o` in its vtable using the value `x` and default `d`
430         /// </summary>
431         /// <param name="o">The index into the vtable</param>
432         /// <param name="x">The value to put into the buffer. If the value is equal to the default
433         /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param>
434         /// <param name="d">The default value to compare the value against</param>
AddUlong(int o, ulong x, ulong d)435         public void AddUlong(int o, ulong x, ulong d) { if (ForceDefaults || x != d) { AddUlong(x); Slot(o); } }
436 
437         /// <summary>
438         /// Adds a Single to the Table at index `o` in its vtable using the value `x` and default `d`
439         /// </summary>
440         /// <param name="o">The index into the vtable</param>
441         /// <param name="x">The value to put into the buffer. If the value is equal to the default
442         /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param>
443         /// <param name="d">The default value to compare the value against</param>
AddFloat(int o, float x, double d)444         public void AddFloat(int o, float x, double d) { if (ForceDefaults || x != d) { AddFloat(x); Slot(o); } }
445 
446         /// <summary>
447         /// Adds a Double to the Table at index `o` in its vtable using the value `x` and default `d`
448         /// </summary>
449         /// <param name="o">The index into the vtable</param>
450         /// <param name="x">The value to put into the buffer. If the value is equal to the default
451         /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param>
452         /// <param name="d">The default value to compare the value against</param>
AddDouble(int o, double x, double d)453         public void AddDouble(int o, double x, double d) { if (ForceDefaults || x != d) { AddDouble(x); Slot(o); } }
454 
455         /// <summary>
456         /// Adds a buffer offset to the Table at index `o` in its vtable using the value `x` and default `d`
457         /// </summary>
458         /// <param name="o">The index into the vtable</param>
459         /// <param name="x">The value to put into the buffer. If the value is equal to the default
460         /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param>
461         /// <param name="d">The default value to compare the value against</param>
AddOffset(int o, int x, int d)462         public void AddOffset(int o, int x, int d) { if (ForceDefaults || x != d) { AddOffset(x); Slot(o); } }
463         /// @endcond
464 
465         /// <summary>
466         /// Encode the string `s` in the buffer using UTF-8.
467         /// </summary>
468         /// <param name="s">The string to encode.</param>
469         /// <returns>
470         /// The offset in the buffer where the encoded string starts.
471         /// </returns>
CreateString(string s)472         public StringOffset CreateString(string s)
473         {
474             NotNested();
475             AddByte(0);
476             var utf8StringLen = Encoding.UTF8.GetByteCount(s);
477             StartVector(1, utf8StringLen, 1);
478             Encoding.UTF8.GetBytes(s, 0, s.Length, _bb.Data, _space -= utf8StringLen);
479             return new StringOffset(EndVector().Value);
480         }
481 
482         /// @cond FLATBUFFERS_INTERNAL
483         // Structs are stored inline, so nothing additional is being added.
484         // `d` is always 0.
AddStruct(int voffset, int x, int d)485         public void AddStruct(int voffset, int x, int d)
486         {
487             if (x != d)
488             {
489                 Nested(x);
490                 Slot(voffset);
491             }
492         }
493 
EndObject()494         public int EndObject()
495         {
496             if (_vtableSize < 0)
497                 throw new InvalidOperationException(
498                   "Flatbuffers: calling endObject without a startObject");
499 
500             AddInt((int)0);
501             var vtableloc = Offset;
502             // Write out the current vtable.
503             for (int i = _vtableSize - 1; i >= 0 ; i--) {
504                 // Offset relative to the start of the table.
505                 short off = (short)(_vtable[i] != 0
506                                         ? vtableloc - _vtable[i]
507                                         : 0);
508                 AddShort(off);
509 
510                 // clear out written entry
511                 _vtable[i] = 0;
512             }
513 
514             const int standardFields = 2; // The fields below:
515             AddShort((short)(vtableloc - _objectStart));
516             AddShort((short)((_vtableSize + standardFields) *
517                              sizeof(short)));
518 
519             // Search for an existing vtable that matches the current one.
520             int existingVtable = 0;
521             for (int i = 0; i < _numVtables; i++) {
522                 int vt1 = _bb.Length - _vtables[i];
523                 int vt2 = _space;
524                 short len = _bb.GetShort(vt1);
525                 if (len == _bb.GetShort(vt2)) {
526                     for (int j = sizeof(short); j < len; j += sizeof(short)) {
527                         if (_bb.GetShort(vt1 + j) != _bb.GetShort(vt2 + j)) {
528                             goto endLoop;
529                         }
530                     }
531                     existingVtable = _vtables[i];
532                     break;
533                 }
534 
535             endLoop: { }
536             }
537 
538             if (existingVtable != 0) {
539                 // Found a match:
540                 // Remove the current vtable.
541                 _space = _bb.Length - vtableloc;
542                 // Point table to existing vtable.
543                 _bb.PutInt(_space, existingVtable - vtableloc);
544             } else {
545                 // No match:
546                 // Add the location of the current vtable to the list of
547                 // vtables.
548                 if (_numVtables == _vtables.Length)
549                 {
550                     // Arrays.CopyOf(vtables num_vtables * 2);
551                     var newvtables = new int[ _numVtables * 2];
552                     Array.Copy(_vtables, newvtables, _vtables.Length);
553 
554                     _vtables = newvtables;
555                 };
556                 _vtables[_numVtables++] = Offset;
557                 // Point table to current vtable.
558                 _bb.PutInt(_bb.Length - vtableloc, Offset - vtableloc);
559             }
560 
561             _vtableSize = -1;
562             return vtableloc;
563         }
564 
565         // This checks a required field has been set in a given table that has
566         // just been constructed.
Required(int table, int field)567         public void Required(int table, int field)
568         {
569           int table_start = _bb.Length - table;
570           int vtable_start = table_start - _bb.GetInt(table_start);
571           bool ok = _bb.GetShort(vtable_start + field) != 0;
572           // If this fails, the caller will show what field needs to be set.
573           if (!ok)
574             throw new InvalidOperationException("FlatBuffers: field " + field +
575                                                 " must be set");
576         }
577         /// @endcond
578 
579         /// <summary>
580         /// Finalize a buffer, pointing to the given `root_table`.
581         /// </summary>
582         /// <param name="rootTable">
583         /// An offset to be added to the buffer.
584         /// </param>
Finish(int rootTable)585         public void Finish(int rootTable)
586         {
587             Prep(_minAlign, sizeof(int));
588             AddOffset(rootTable);
589             _bb.Position = _space;
590         }
591 
592         /// <summary>
593         /// Get the ByteBuffer representing the FlatBuffer.
594         /// </summary>
595         /// <remarks>
596         /// This is typically only called after you call `Finish()`.
597         /// The actual data starts at the ByteBuffer's current position,
598         /// not necessarily at `0`.
599         /// </remarks>
600         /// <returns>
601         /// Returns the ByteBuffer for this FlatBuffer.
602         /// </returns>
603         public ByteBuffer DataBuffer { get { return _bb; } }
604 
605         /// <summary>
606         /// A utility function to copy and return the ByteBuffer data as a
607         /// `byte[]`.
608         /// </summary>
609         /// <returns>
610         /// A full copy of the FlatBuffer data.
611         /// </returns>
SizedByteArray()612         public byte[] SizedByteArray()
613         {
614             var newArray = new byte[_bb.Data.Length - _bb.Position];
615             Buffer.BlockCopy(_bb.Data, _bb.Position, newArray, 0,
616                              _bb.Data.Length - _bb.Position);
617             return newArray;
618         }
619 
620          /// <summary>
621          /// Finalize a buffer, pointing to the given `rootTable`.
622          /// </summary>
623          /// <param name="rootTable">
624          /// An offset to be added to the buffer.
625          /// </param>
626          /// <param name="fileIdentifier">
627          /// A FlatBuffer file identifier to be added to the buffer before
628          /// `root_table`.
629          /// </param>
Finish(int rootTable, string fileIdentifier)630          public void Finish(int rootTable, string fileIdentifier)
631          {
632              Prep(_minAlign, sizeof(int) +
633                              FlatBufferConstants.FileIdentifierLength);
634              if (fileIdentifier.Length !=
635                  FlatBufferConstants.FileIdentifierLength)
636                  throw new ArgumentException(
637                      "FlatBuffers: file identifier must be length " +
638                      FlatBufferConstants.FileIdentifierLength,
639                      "fileIdentifier");
640              for (int i = FlatBufferConstants.FileIdentifierLength - 1; i >= 0;
641                   i--)
642              {
643                 AddByte((byte)fileIdentifier[i]);
644              }
645              Finish(rootTable);
646         }
647 
648 
649     }
650 }
651 
652 /// @}
653