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