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.Collections.Generic;
20 using System.Text;
21 
22 /// @file
23 /// @addtogroup flatbuffers_csharp_api
24 /// @{
25 
26 namespace 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>
FlatBufferBuilder(int initialSize)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>
FlatBufferBuilder(ByteBuffer buffer)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>
Clear()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 
Pad(int size)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).
GrowBuffer()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.
Prep(int size, int additionalBytes)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 
PutBool(bool x)147         public void PutBool(bool x)
148         {
149           _bb.PutByte(_space -= sizeof(byte), (byte)(x ? 1 : 0));
150         }
151 
PutSbyte(sbyte x)152         public void PutSbyte(sbyte x)
153         {
154           _bb.PutSbyte(_space -= sizeof(sbyte), x);
155         }
156 
PutByte(byte x)157         public void PutByte(byte x)
158         {
159             _bb.PutByte(_space -= sizeof(byte), x);
160         }
161 
PutShort(short x)162         public void PutShort(short x)
163         {
164             _bb.PutShort(_space -= sizeof(short), x);
165         }
166 
PutUshort(ushort x)167         public void PutUshort(ushort x)
168         {
169           _bb.PutUshort(_space -= sizeof(ushort), x);
170         }
171 
PutInt(int x)172         public void PutInt(int x)
173         {
174             _bb.PutInt(_space -= sizeof(int), x);
175         }
176 
PutUint(uint x)177         public void PutUint(uint x)
178         {
179           _bb.PutUint(_space -= sizeof(uint), x);
180         }
181 
PutLong(long x)182         public void PutLong(long x)
183         {
184             _bb.PutLong(_space -= sizeof(long), x);
185         }
186 
PutUlong(ulong x)187         public void PutUlong(ulong x)
188         {
189           _bb.PutUlong(_space -= sizeof(ulong), x);
190         }
191 
PutFloat(float x)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 
PutDouble(double x)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>
AddBool(bool x)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>
AddSbyte(sbyte x)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>
AddByte(byte x)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>
AddShort(short x)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>
AddUshort(ushort x)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>
AddInt(int x)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>
AddUint(uint x)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>
AddLong(long x)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>
AddUlong(ulong x)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>
AddFloat(float x)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
PrepFlatBuffers.FlatBufferBuilder.__anon3316             Prep(size, size * (x.Length - 1));
PutFlatBuffers.FlatBufferBuilder.__anon3317             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
PrepFlatBuffers.FlatBufferBuilder.__anon4337             Prep(size, size * (x.Length - 1));
PutFlatBuffers.FlatBufferBuilder.__anon4338             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>
AddDouble(double x)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>
AddOffset(int off)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
StartVector(int elemSize, int count, int alignment)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>
EndVector()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         {
NotNestedFlatBuffers.FlatBufferBuilder.__anon5388             NotNested();
sizeofFlatBuffers.FlatBufferBuilder.__anon5389             StartVector(sizeof(int), offsets.Length, sizeof(int));
AddOffsetFlatBuffers.FlatBufferBuilder.__anon5390             for (int i = offsets.Length - 1; i >= 0; i--) AddOffset(offsets[i].Value);
391             return EndVector();
392         }
393 
394         /// @cond FLATBUFFERS_INTENRAL
Nested(int obj)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 
NotNested()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 
StartTable(int numfields)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.
Slot(int voffset)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>
AddBool(int o, bool x, bool d)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>
AddSbyte(int o, sbyte x, sbyte d)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>
AddByte(int o, byte x, byte d)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>
AddShort(int o, short x, int d)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>
AddUshort(int o, ushort x, ushort d)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>
AddInt(int o, int x, int d)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>
AddUint(int o, uint x, uint d)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>
AddLong(int o, long x, long d)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>
AddUlong(int o, ulong x, ulong d)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>
AddFloat(int o, float x, double d)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>
AddDouble(int o, double x, double d)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>
AddOffset(int o, int x, int d)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>
CreateString(string s)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>
CreateUTF8String(Span<byte> chars)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>
CreateSharedString(string s)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.
AddStruct(int voffset, int x, int d)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 
EndTable()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.
Required(int table, int field)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>
Finish(int rootTable, bool sizePrefix)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>
Finish(int rootTable)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>
FinishSizePrefixed(int rootTable)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>
SizedByteArray()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>
Finish(int rootTable, string fileIdentifier, bool sizePrefix)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>
Finish(int rootTable, string fileIdentifier)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>
FinishSizePrefixed(int rootTable, string fileIdentifier)835         public void FinishSizePrefixed(int rootTable, string fileIdentifier)
836         {
837             Finish(rootTable, fileIdentifier, true);
838         }
839     }
840 }
841 
842 /// @}
843