xref: /aosp_15_r20/external/flatbuffers/net/FlatBuffers/FlatBufferBuilder.cs (revision 890232f25432b36107d06881e0a25aaa6b473652)
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         /// <summary>
214         /// Puts an array of type T into this builder at the
215         /// current offset
216         /// </summary>
217         /// <typeparam name="T">The type of the input data </typeparam>
218         /// <param name="x">The array segment to copy data from</param>
219         public void Put<T>(ArraySegment<T> x)
220             where T : struct
221         {
222             _space = _bb.Put(_space, x);
223         }
224 
225         /// <summary>
226         /// Puts data of type T into this builder at the
227         /// current offset
228         /// </summary>
229         /// <typeparam name="T">The type of the input data </typeparam>
230         /// <param name="ptr">The pointer to copy data from</param>
231         /// <param name="sizeInBytes">The length of the data in bytes</param>
232         public void Put<T>(IntPtr ptr, int sizeInBytes)
233             where T : struct
234         {
235             _space = _bb.Put<T>(_space, ptr, sizeInBytes);
236         }
237 
238 #if ENABLE_SPAN_T && (UNSAFE_BYTEBUFFER || NETSTANDARD2_1)
239         /// <summary>
240         /// Puts a span of type T into this builder at the
241         /// current offset
242         /// </summary>
243         /// <typeparam name="T">The type of the input data </typeparam>
244         /// <param name="x">The span to copy data from</param>
245         public void Put<T>(Span<T> x)
246             where T : struct
247         {
248             _space = _bb.Put(_space, x);
249         }
250 #endif
251 
PutDouble(double x)252         public void PutDouble(double x)
253         {
254             _bb.PutDouble(_space -= sizeof(double), x);
255         }
256         /// @endcond
257 
258         /// <summary>
259         /// Add a `bool` to the buffer (aligns the data and grows if necessary).
260         /// </summary>
261         /// <param name="x">The `bool` to add to the buffer.</param>
AddBool(bool x)262         public void AddBool(bool x) { Prep(sizeof(byte), 0); PutBool(x); }
263 
264         /// <summary>
265         /// Add a `sbyte` to the buffer (aligns the data and grows if necessary).
266         /// </summary>
267         /// <param name="x">The `sbyte` to add to the buffer.</param>
AddSbyte(sbyte x)268         public void AddSbyte(sbyte x) { Prep(sizeof(sbyte), 0); PutSbyte(x); }
269 
270         /// <summary>
271         /// Add a `byte` to the buffer (aligns the data and grows if necessary).
272         /// </summary>
273         /// <param name="x">The `byte` to add to the buffer.</param>
AddByte(byte x)274         public void AddByte(byte x) { Prep(sizeof(byte), 0); PutByte(x); }
275 
276         /// <summary>
277         /// Add a `short` to the buffer (aligns the data and grows if necessary).
278         /// </summary>
279         /// <param name="x">The `short` to add to the buffer.</param>
AddShort(short x)280         public void AddShort(short x) { Prep(sizeof(short), 0); PutShort(x); }
281 
282         /// <summary>
283         /// Add an `ushort` to the buffer (aligns the data and grows if necessary).
284         /// </summary>
285         /// <param name="x">The `ushort` to add to the buffer.</param>
AddUshort(ushort x)286         public void AddUshort(ushort x) { Prep(sizeof(ushort), 0); PutUshort(x); }
287 
288         /// <summary>
289         /// Add an `int` to the buffer (aligns the data and grows if necessary).
290         /// </summary>
291         /// <param name="x">The `int` to add to the buffer.</param>
AddInt(int x)292         public void AddInt(int x) { Prep(sizeof(int), 0); PutInt(x); }
293 
294         /// <summary>
295         /// Add an `uint` to the buffer (aligns the data and grows if necessary).
296         /// </summary>
297         /// <param name="x">The `uint` to add to the buffer.</param>
AddUint(uint x)298         public void AddUint(uint x) { Prep(sizeof(uint), 0); PutUint(x); }
299 
300         /// <summary>
301         /// Add a `long` to the buffer (aligns the data and grows if necessary).
302         /// </summary>
303         /// <param name="x">The `long` to add to the buffer.</param>
AddLong(long x)304         public void AddLong(long x) { Prep(sizeof(long), 0); PutLong(x); }
305 
306         /// <summary>
307         /// Add an `ulong` to the buffer (aligns the data and grows if necessary).
308         /// </summary>
309         /// <param name="x">The `ulong` to add to the buffer.</param>
AddUlong(ulong x)310         public void AddUlong(ulong x) { Prep(sizeof(ulong), 0); PutUlong(x); }
311 
312         /// <summary>
313         /// Add a `float` to the buffer (aligns the data and grows if necessary).
314         /// </summary>
315         /// <param name="x">The `float` to add to the buffer.</param>
AddFloat(float x)316         public void AddFloat(float x) { Prep(sizeof(float), 0); PutFloat(x); }
317 
318         /// <summary>
319         /// Add an array of type T to the buffer (aligns the data and grows if necessary).
320         /// </summary>
321         /// <typeparam name="T">The type of the input data</typeparam>
322         /// <param name="x">The array to copy data from</param>
323         public void Add<T>(T[] x)
324             where T : struct
325         {
AddFlatBuffers.FlatBufferBuilder.__anon5326             Add(new ArraySegment<T>(x));
327         }
328 
329         /// <summary>
330         /// Add an array of type T to the buffer (aligns the data and grows if necessary).
331         /// </summary>
332         /// <typeparam name="T">The type of the input data</typeparam>
333         /// <param name="x">The array segment to copy data from</param>
334         public void Add<T>(ArraySegment<T> x)
335             where T : struct
336         {
337             if (x == null)
338             {
339                 throw new ArgumentNullException("Cannot add a null array");
340             }
341 
342             if( x.Count == 0)
343             {
344                 // don't do anything if the array is empty
345                 return;
346             }
347 
348             if(!ByteBuffer.IsSupportedType<T>())
349             {
350                 throw new ArgumentException("Cannot add this Type array to the builder");
351             }
352 
353             int size = ByteBuffer.SizeOf<T>();
354             // Need to prep on size (for data alignment) and then we pass the
355             // rest of the length (minus 1) as additional bytes
PrepFlatBuffers.FlatBufferBuilder.__anon6356             Prep(size, size * (x.Count - 1));
PutFlatBuffers.FlatBufferBuilder.__anon6357             Put(x);
358         }
359 
360         /// <summary>
361         /// Adds the data of type T pointed to by the given pointer to the buffer (aligns the data and grows if necessary).
362         /// </summary>
363         /// <typeparam name="T">The type of the input data</typeparam>
364         /// <param name="ptr">The pointer to copy data from</param>
365         /// <param name="sizeInBytes">The data size in bytes</param>
366         public void Add<T>(IntPtr ptr, int sizeInBytes)
367             where T : struct
368         {
369             if(sizeInBytes == 0)
370             {
371                 // don't do anything if the array is empty
372                 return;
373             }
374 
375             if (ptr == IntPtr.Zero)
376             {
377                 throw new ArgumentNullException("Cannot add a null pointer");
378             }
379 
380             if(sizeInBytes < 0)
381             {
382                 throw new ArgumentOutOfRangeException("sizeInBytes", "sizeInBytes cannot be negative");
383             }
384 
385             if(!ByteBuffer.IsSupportedType<T>())
386             {
387                 throw new ArgumentException("Cannot add this Type array to the builder");
388             }
389 
390             int size = ByteBuffer.SizeOf<T>();
391             if((sizeInBytes % size) != 0)
392             {
393                 throw new ArgumentException("The given size in bytes " + sizeInBytes + " doesn't match the element size of T ( " + size + ")", "sizeInBytes");
394             }
395 
396             // Need to prep on size (for data alignment) and then we pass the
397             // rest of the length (minus 1) as additional bytes
PrepFlatBuffers.FlatBufferBuilder.__anon7398             Prep(size, sizeInBytes - size);
PutFlatBuffers.FlatBufferBuilder.__anon7399             Put<T>(ptr, sizeInBytes);
400         }
401 
402 #if ENABLE_SPAN_T && (UNSAFE_BYTEBUFFER || NETSTANDARD2_1)
403         /// <summary>
404         /// Add a span of type T to the buffer (aligns the data and grows if necessary).
405         /// </summary>
406         /// <typeparam name="T">The type of the input data</typeparam>
407         /// <param name="x">The span to copy data from</param>
408         public void Add<T>(Span<T> x)
409             where T : struct
410         {
411             if (!ByteBuffer.IsSupportedType<T>())
412             {
413                 throw new ArgumentException("Cannot add this Type array to the builder");
414             }
415 
416             int size = ByteBuffer.SizeOf<T>();
417             // Need to prep on size (for data alignment) and then we pass the
418             // rest of the length (minus 1) as additional bytes
PrepFlatBuffers.FlatBufferBuilder.__anon8419             Prep(size, size * (x.Length - 1));
PutFlatBuffers.FlatBufferBuilder.__anon8420             Put(x);
421         }
422 #endif
423 
424         /// <summary>
425         /// Add a `double` to the buffer (aligns the data and grows if necessary).
426         /// </summary>
427         /// <param name="x">The `double` to add to the buffer.</param>
AddDouble(double x)428         public void AddDouble(double x) { Prep(sizeof(double), 0);
429                                           PutDouble(x); }
430 
431         /// <summary>
432         /// Adds an offset, relative to where it will be written.
433         /// </summary>
434         /// <param name="off">The offset to add to the buffer.</param>
AddOffset(int off)435         public void AddOffset(int off)
436         {
437             Prep(sizeof(int), 0);  // Ensure alignment is already done.
438             if (off > Offset)
439                 throw new ArgumentException();
440 
441             off = Offset - off + sizeof(int);
442             PutInt(off);
443         }
444 
445         /// @cond FLATBUFFERS_INTERNAL
StartVector(int elemSize, int count, int alignment)446         public void StartVector(int elemSize, int count, int alignment)
447         {
448             NotNested();
449             _vectorNumElems = count;
450             Prep(sizeof(int), elemSize * count);
451             Prep(alignment, elemSize * count); // Just in case alignment > int.
452         }
453         /// @endcond
454 
455         /// <summary>
456         /// Writes data necessary to finish a vector construction.
457         /// </summary>
EndVector()458         public VectorOffset EndVector()
459         {
460             PutInt(_vectorNumElems);
461             return new VectorOffset(Offset);
462         }
463 
464         /// <summary>
465         /// Creates a vector of tables.
466         /// </summary>
467         /// <param name="offsets">Offsets of the tables.</param>
468         public VectorOffset CreateVectorOfTables<T>(Offset<T>[] offsets) where T : struct
469         {
NotNestedFlatBuffers.FlatBufferBuilder.__anon9470             NotNested();
sizeofFlatBuffers.FlatBufferBuilder.__anon9471             StartVector(sizeof(int), offsets.Length, sizeof(int));
AddOffsetFlatBuffers.FlatBufferBuilder.__anon9472             for (int i = offsets.Length - 1; i >= 0; i--) AddOffset(offsets[i].Value);
473             return EndVector();
474         }
475 
476         /// @cond FLATBUFFERS_INTENRAL
Nested(int obj)477         public void Nested(int obj)
478         {
479             // Structs are always stored inline, so need to be created right
480             // where they are used. You'll get this assert if you created it
481             // elsewhere.
482             if (obj != Offset)
483                 throw new Exception(
484                     "FlatBuffers: struct must be serialized inline.");
485         }
486 
NotNested()487         public void NotNested()
488         {
489             // You should not be creating any other objects or strings/vectors
490             // while an object is being constructed
491             if (_vtableSize >= 0)
492                 throw new Exception(
493                     "FlatBuffers: object serialization must not be nested.");
494         }
495 
StartTable(int numfields)496         public void StartTable(int numfields)
497         {
498             if (numfields < 0)
499                 throw new ArgumentOutOfRangeException("Flatbuffers: invalid numfields");
500 
501             NotNested();
502 
503             if (_vtable.Length < numfields)
504                 _vtable = new int[numfields];
505 
506             _vtableSize = numfields;
507             _objectStart = Offset;
508         }
509 
510 
511         // Set the current vtable at `voffset` to the current location in the
512         // buffer.
Slot(int voffset)513         public void Slot(int voffset)
514         {
515             if (voffset >= _vtableSize)
516                 throw new IndexOutOfRangeException("Flatbuffers: invalid voffset");
517 
518             _vtable[voffset] = Offset;
519         }
520 
521         /// <summary>
522         /// Adds a Boolean to the Table at index `o` in its vtable using the value `x` and default `d`
523         /// </summary>
524         /// <param name="o">The index into the vtable</param>
525         /// <param name="x">The value to put into the buffer. If the value is equal to the default
526         /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param>
527         /// <param name="d">The default value to compare the value against</param>
AddBool(int o, bool x, bool d)528         public void AddBool(int o, bool x, bool d) { if (ForceDefaults || x != d) { AddBool(x); Slot(o); } }
529 
530         /// <summary>
531         /// Adds a Boolean to the Table at index `o` in its vtable using the nullable value `x`
532         /// </summary>
533         /// <param name="o">The index into the vtable</param>
534         /// <param name="x">The nullable boolean value to put into the buffer. If it doesn't have a value
535         /// it will skip writing to the buffer.</param>
AddBool(int o, bool? x)536         public void AddBool(int o, bool? x) { if (x.HasValue) { AddBool(x.Value); Slot(o); } }
537 
538 
539         /// <summary>
540         /// Adds a SByte to the Table at index `o` in its vtable using the value `x` and default `d`
541         /// </summary>
542         /// <param name="o">The index into the vtable</param>
543         /// <param name="x">The value to put into the buffer. If the value is equal to the default
544         /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param>
545         /// <param name="d">The default value to compare the value against</param>
AddSbyte(int o, sbyte x, sbyte d)546         public void AddSbyte(int o, sbyte x, sbyte d) { if (ForceDefaults || x != d) { AddSbyte(x); Slot(o); } }
547 
548         /// <summary>
549         /// Adds a SByte to the Table at index `o` in its vtable using the nullable value `x`
550         /// </summary>
551         /// <param name="o">The index into the vtable</param>
552         /// <param name="x">The nullable sbyte value to put into the buffer. If it doesn't have a value
553         /// it will skip writing to the buffer.</param>
AddSbyte(int o, sbyte? x)554         public void AddSbyte(int o, sbyte? x) { if (x.HasValue) { AddSbyte(x.Value); Slot(o); } }
555 
556         /// <summary>
557         /// Adds a Byte to the Table at index `o` in its vtable using the value `x` and default `d`
558         /// </summary>
559         /// <param name="o">The index into the vtable</param>
560         /// <param name="x">The value to put into the buffer. If the value is equal to the default
561         /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param>
562         /// <param name="d">The default value to compare the value against</param>
AddByte(int o, byte x, byte d)563         public void AddByte(int o, byte x, byte d) { if (ForceDefaults || x != d) { AddByte(x); Slot(o); } }
564 
565         /// <summary>
566         /// Adds a Byte to the Table at index `o` in its vtable using the nullable value `x`
567         /// </summary>
568         /// <param name="o">The index into the vtable</param>
569         /// <param name="x">The nullable byte value to put into the buffer. If it doesn't have a value
570         /// it will skip writing to the buffer.</param>
AddByte(int o, byte? x)571         public void AddByte(int o, byte? x) { if (x.HasValue) { AddByte(x.Value); Slot(o); } }
572 
573         /// <summary>
574         /// Adds a Int16 to the Table at index `o` in its vtable using the value `x` and default `d`
575         /// </summary>
576         /// <param name="o">The index into the vtable</param>
577         /// <param name="x">The value to put into the buffer. If the value is equal to the default
578         /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param>
579         /// <param name="d">The default value to compare the value against</param>
AddShort(int o, short x, int d)580         public void AddShort(int o, short x, int d) { if (ForceDefaults || x != d) { AddShort(x); Slot(o); } }
581 
582         /// <summary>
583         /// Adds a Int16 to the Table at index `o` in its vtable using the nullable value `x`
584         /// </summary>
585         /// <param name="o">The index into the vtable</param>
586         /// <param name="x">The nullable int16 value to put into the buffer. If it doesn't have a value
587         /// it will skip writing to the buffer.</param>
AddShort(int o, short? x)588         public void AddShort(int o, short? x) { if (x.HasValue) { AddShort(x.Value); Slot(o); } }
589 
590         /// <summary>
591         /// Adds a UInt16 to the Table at index `o` in its vtable using the value `x` and default `d`
592         /// </summary>
593         /// <param name="o">The index into the vtable</param>
594         /// <param name="x">The value to put into the buffer. If the value is equal to the default
595         /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param>
596         /// <param name="d">The default value to compare the value against</param>
AddUshort(int o, ushort x, ushort d)597         public void AddUshort(int o, ushort x, ushort d) { if (ForceDefaults || x != d) { AddUshort(x); Slot(o); } }
598 
599         /// <summary>
600         /// Adds a Uint16 to the Table at index `o` in its vtable using the nullable value `x`
601         /// </summary>
602         /// <param name="o">The index into the vtable</param>
603         /// <param name="x">The nullable uint16 value to put into the buffer. If it doesn't have a value
604         /// it will skip writing to the buffer.</param>
AddUshort(int o, ushort? x)605         public void AddUshort(int o, ushort? x) { if (x.HasValue) { AddUshort(x.Value); Slot(o); } }
606 
607         /// <summary>
608         /// Adds an Int32 to the Table at index `o` in its vtable using the value `x` and default `d`
609         /// </summary>
610         /// <param name="o">The index into the vtable</param>
611         /// <param name="x">The value to put into the buffer. If the value is equal to the default
612         /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param>
613         /// <param name="d">The default value to compare the value against</param>
AddInt(int o, int x, int d)614         public void AddInt(int o, int x, int d) { if (ForceDefaults || x != d) { AddInt(x); Slot(o); } }
615 
616         /// <summary>
617         /// Adds a Int32 to the Table at index `o` in its vtable using the nullable value `x`
618         /// </summary>
619         /// <param name="o">The index into the vtable</param>
620         /// <param name="x">The nullable int32 value to put into the buffer. If it doesn't have a value
621         /// it will skip writing to the buffer.</param>
AddInt(int o, int? x)622         public void AddInt(int o, int? x) { if (x.HasValue) { AddInt(x.Value); Slot(o); } }
623 
624         /// <summary>
625         /// Adds a UInt32 to the Table at index `o` in its vtable using the value `x` and default `d`
626         /// </summary>
627         /// <param name="o">The index into the vtable</param>
628         /// <param name="x">The value to put into the buffer. If the value is equal to the default
629         /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param>
630         /// <param name="d">The default value to compare the value against</param>
AddUint(int o, uint x, uint d)631         public void AddUint(int o, uint x, uint d) { if (ForceDefaults || x != d) { AddUint(x); Slot(o); } }
632 
633         /// <summary>
634         /// Adds a UInt32 to the Table at index `o` in its vtable using the nullable value `x`
635         /// </summary>
636         /// <param name="o">The index into the vtable</param>
637         /// <param name="x">The nullable uint32 value to put into the buffer. If it doesn't have a value
638         /// it will skip writing to the buffer.</param>
AddUint(int o, uint? x)639         public void AddUint(int o, uint? x) { if (x.HasValue) { AddUint(x.Value); Slot(o); } }
640 
641         /// <summary>
642         /// Adds an Int64 to the Table at index `o` in its vtable using the value `x` and default `d`
643         /// </summary>
644         /// <param name="o">The index into the vtable</param>
645         /// <param name="x">The value to put into the buffer. If the value is equal to the default
646         /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param>
647         /// <param name="d">The default value to compare the value against</param>
AddLong(int o, long x, long d)648         public void AddLong(int o, long x, long d) { if (ForceDefaults || x != d) { AddLong(x); Slot(o); } }
649 
650         /// <summary>
651         /// Adds a Int64 to the Table at index `o` in its vtable using the nullable value `x`
652         /// </summary>
653         /// <param name="o">The index into the vtable</param>
654         /// <param name="x">The nullable int64 value to put into the buffer. If it doesn't have a value
655         /// it will skip writing to the buffer.</param>
AddLong(int o, long? x)656         public void AddLong(int o, long? x) { if (x.HasValue) { AddLong(x.Value); Slot(o); } }
657 
658         /// <summary>
659         /// Adds a UInt64 to the Table at index `o` in its vtable using the value `x` and default `d`
660         /// </summary>
661         /// <param name="o">The index into the vtable</param>
662         /// <param name="x">The value to put into the buffer. If the value is equal to the default
663         /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param>
664         /// <param name="d">The default value to compare the value against</param>
AddUlong(int o, ulong x, ulong d)665         public void AddUlong(int o, ulong x, ulong d) { if (ForceDefaults || x != d) { AddUlong(x); Slot(o); } }
666 
667         /// <summary>
668         /// Adds a UInt64 to the Table at index `o` in its vtable using the nullable value `x`
669         /// </summary>
670         /// <param name="o">The index into the vtable</param>
671         /// <param name="x">The nullable int64 value to put into the buffer. If it doesn't have a value
672         /// it will skip writing to the buffer.</param>
AddUlong(int o, ulong? x)673         public void AddUlong(int o, ulong? x) { if (x.HasValue) { AddUlong(x.Value); Slot(o); } }
674 
675         /// <summary>
676         /// Adds a Single to the Table at index `o` in its vtable using the value `x` and default `d`
677         /// </summary>
678         /// <param name="o">The index into the vtable</param>
679         /// <param name="x">The value to put into the buffer. If the value is equal to the default
680         /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param>
681         /// <param name="d">The default value to compare the value against</param>
AddFloat(int o, float x, double d)682         public void AddFloat(int o, float x, double d) { if (ForceDefaults || x != d) { AddFloat(x); Slot(o); } }
683 
684         /// <summary>
685         /// Adds a Single to the Table at index `o` in its vtable using the nullable value `x`
686         /// </summary>
687         /// <param name="o">The index into the vtable</param>
688         /// <param name="x">The nullable single value to put into the buffer. If it doesn't have a value
689         /// it will skip writing to the buffer.</param>
AddFloat(int o, float? x)690         public void AddFloat(int o, float? x) { if (x.HasValue) { AddFloat(x.Value); Slot(o); } }
691 
692         /// <summary>
693         /// Adds a Double to the Table at index `o` in its vtable using the value `x` and default `d`
694         /// </summary>
695         /// <param name="o">The index into the vtable</param>
696         /// <param name="x">The value to put into the buffer. If the value is equal to the default
697         /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param>
698         /// <param name="d">The default value to compare the value against</param>
AddDouble(int o, double x, double d)699         public void AddDouble(int o, double x, double d) { if (ForceDefaults || x != d) { AddDouble(x); Slot(o); } }
700 
701         /// <summary>
702         /// Adds a Double to the Table at index `o` in its vtable using the nullable value `x`
703         /// </summary>
704         /// <param name="o">The index into the vtable</param>
705         /// <param name="x">The nullable double value to put into the buffer. If it doesn't have a value
706         /// it will skip writing to the buffer.</param>
AddDouble(int o, double? x)707         public void AddDouble(int o, double? x) { if (x.HasValue) { AddDouble(x.Value); Slot(o); } }
708 
709         /// <summary>
710         /// Adds a buffer offset to the Table at index `o` in its vtable using the value `x` and default `d`
711         /// </summary>
712         /// <param name="o">The index into the vtable</param>
713         /// <param name="x">The value to put into the buffer. If the value is equal to the default
714         /// the value will be skipped.</param>
715         /// <param name="d">The default value to compare the value against</param>
AddOffset(int o, int x, int d)716         public void AddOffset(int o, int x, int d) { if (x != d) { AddOffset(x); Slot(o); } }
717         /// @endcond
718 
719         /// <summary>
720         /// Encode the string `s` in the buffer using UTF-8.
721         /// </summary>
722         /// <param name="s">The string to encode.</param>
723         /// <returns>
724         /// The offset in the buffer where the encoded string starts.
725         /// </returns>
CreateString(string s)726         public StringOffset CreateString(string s)
727         {
728             if (s == null)
729             {
730                 return new StringOffset(0);
731             }
732             NotNested();
733             AddByte(0);
734             var utf8StringLen = Encoding.UTF8.GetByteCount(s);
735             StartVector(1, utf8StringLen, 1);
736             _bb.PutStringUTF8(_space -= utf8StringLen, s);
737             return new StringOffset(EndVector().Value);
738         }
739 
740 
741 #if ENABLE_SPAN_T && (UNSAFE_BYTEBUFFER || NETSTANDARD2_1)
742         /// <summary>
743         /// Creates a string in the buffer from a Span containing
744         /// a UTF8 string.
745         /// </summary>
746         /// <param name="chars">the UTF8 string to add to the buffer</param>
747         /// <returns>
748         /// The offset in the buffer where the encoded string starts.
749         /// </returns>
CreateUTF8String(Span<byte> chars)750         public StringOffset CreateUTF8String(Span<byte> chars)
751         {
752             NotNested();
753             AddByte(0);
754             var utf8StringLen = chars.Length;
755             StartVector(1, utf8StringLen, 1);
756             _space = _bb.Put(_space, chars);
757             return new StringOffset(EndVector().Value);
758         }
759 #endif
760 
761         /// <summary>
762         /// Store a string in the buffer, which can contain any binary data.
763         /// If a string with this exact contents has already been serialized before,
764         /// instead simply returns the offset of the existing string.
765         /// </summary>
766         /// <param name="s">The string to encode.</param>
767         /// <returns>
768         /// The offset in the buffer where the encoded string starts.
769         /// </returns>
CreateSharedString(string s)770         public StringOffset CreateSharedString(string s)
771         {
772             if (s == null)
773             {
774               return new StringOffset(0);
775             }
776 
777             if (_sharedStringMap == null)
778             {
779                 _sharedStringMap = new Dictionary<string, StringOffset>();
780             }
781 
782             if (_sharedStringMap.ContainsKey(s))
783             {
784                 return _sharedStringMap[s];
785             }
786 
787             var stringOffset = CreateString(s);
788             _sharedStringMap.Add(s, stringOffset);
789             return stringOffset;
790         }
791 
792         /// @cond FLATBUFFERS_INTERNAL
793         // Structs are stored inline, so nothing additional is being added.
794         // `d` is always 0.
AddStruct(int voffset, int x, int d)795         public void AddStruct(int voffset, int x, int d)
796         {
797             if (x != d)
798             {
799                 Nested(x);
800                 Slot(voffset);
801             }
802         }
803 
EndTable()804         public int EndTable()
805         {
806             if (_vtableSize < 0)
807                 throw new InvalidOperationException(
808                   "Flatbuffers: calling EndTable without a StartTable");
809 
810             AddInt((int)0);
811             var vtableloc = Offset;
812             // Write out the current vtable.
813             int i = _vtableSize - 1;
814             // Trim trailing zeroes.
815             for (; i >= 0 && _vtable[i] == 0; i--) {}
816             int trimmedSize = i + 1;
817             for (; i >= 0 ; i--) {
818                 // Offset relative to the start of the table.
819                 short off = (short)(_vtable[i] != 0
820                                         ? vtableloc - _vtable[i]
821                                         : 0);
822                 AddShort(off);
823 
824                 // clear out written entry
825                 _vtable[i] = 0;
826             }
827 
828             const int standardFields = 2; // The fields below:
829             AddShort((short)(vtableloc - _objectStart));
830             AddShort((short)((trimmedSize + standardFields) *
831                              sizeof(short)));
832 
833             // Search for an existing vtable that matches the current one.
834             int existingVtable = 0;
835             for (i = 0; i < _numVtables; i++) {
836                 int vt1 = _bb.Length - _vtables[i];
837                 int vt2 = _space;
838                 short len = _bb.GetShort(vt1);
839                 if (len == _bb.GetShort(vt2)) {
840                     for (int j = sizeof(short); j < len; j += sizeof(short)) {
841                         if (_bb.GetShort(vt1 + j) != _bb.GetShort(vt2 + j)) {
842                             goto endLoop;
843                         }
844                     }
845                     existingVtable = _vtables[i];
846                     break;
847                 }
848 
849                 endLoop: { }
850             }
851 
852             if (existingVtable != 0) {
853                 // Found a match:
854                 // Remove the current vtable.
855                 _space = _bb.Length - vtableloc;
856                 // Point table to existing vtable.
857                 _bb.PutInt(_space, existingVtable - vtableloc);
858             } else {
859                 // No match:
860                 // Add the location of the current vtable to the list of
861                 // vtables.
862                 if (_numVtables == _vtables.Length)
863                 {
864                     // Arrays.CopyOf(vtables num_vtables * 2);
865                     var newvtables = new int[ _numVtables * 2];
866                     Array.Copy(_vtables, newvtables, _vtables.Length);
867 
868                     _vtables = newvtables;
869                 };
870                 _vtables[_numVtables++] = Offset;
871                 // Point table to current vtable.
872                 _bb.PutInt(_bb.Length - vtableloc, Offset - vtableloc);
873             }
874 
875             _vtableSize = -1;
876             return vtableloc;
877         }
878 
879         // This checks a required field has been set in a given table that has
880         // just been constructed.
Required(int table, int field)881         public void Required(int table, int field)
882         {
883           int table_start = _bb.Length - table;
884           int vtable_start = table_start - _bb.GetInt(table_start);
885           bool ok = _bb.GetShort(vtable_start + field) != 0;
886           // If this fails, the caller will show what field needs to be set.
887           if (!ok)
888             throw new InvalidOperationException("FlatBuffers: field " + field +
889                                                 " must be set");
890         }
891         /// @endcond
892 
893         /// <summary>
894         /// Finalize a buffer, pointing to the given `root_table`.
895         /// </summary>
896         /// <param name="rootTable">
897         /// An offset to be added to the buffer.
898         /// </param>
899         /// <param name="sizePrefix">
900         /// Whether to prefix the size to the buffer.
901         /// </param>
Finish(int rootTable, bool sizePrefix)902         protected void Finish(int rootTable, bool sizePrefix)
903         {
904             Prep(_minAlign, sizeof(int) + (sizePrefix ? sizeof(int) : 0));
905             AddOffset(rootTable);
906             if (sizePrefix) {
907                 AddInt(_bb.Length - _space);
908             }
909             _bb.Position = _space;
910         }
911 
912         /// <summary>
913         /// Finalize a buffer, pointing to the given `root_table`.
914         /// </summary>
915         /// <param name="rootTable">
916         /// An offset to be added to the buffer.
917         /// </param>
Finish(int rootTable)918         public void Finish(int rootTable)
919         {
920             Finish(rootTable, false);
921         }
922 
923         /// <summary>
924         /// Finalize a buffer, pointing to the given `root_table`, with the size prefixed.
925         /// </summary>
926         /// <param name="rootTable">
927         /// An offset to be added to the buffer.
928         /// </param>
FinishSizePrefixed(int rootTable)929         public void FinishSizePrefixed(int rootTable)
930         {
931             Finish(rootTable, true);
932         }
933 
934         /// <summary>
935         /// Get the ByteBuffer representing the FlatBuffer.
936         /// </summary>
937         /// <remarks>
938         /// This is typically only called after you call `Finish()`.
939         /// The actual data starts at the ByteBuffer's current position,
940         /// not necessarily at `0`.
941         /// </remarks>
942         /// <returns>
943         /// Returns the ByteBuffer for this FlatBuffer.
944         /// </returns>
945         public ByteBuffer DataBuffer { get { return _bb; } }
946 
947         /// <summary>
948         /// A utility function to copy and return the ByteBuffer data as a
949         /// `byte[]`.
950         /// </summary>
951         /// <returns>
952         /// A full copy of the FlatBuffer data.
953         /// </returns>
SizedByteArray()954         public byte[] SizedByteArray()
955         {
956             return _bb.ToSizedArray();
957         }
958 
959         /// <summary>
960         /// Finalize a buffer, pointing to the given `rootTable`.
961         /// </summary>
962         /// <param name="rootTable">
963         /// An offset to be added to the buffer.
964         /// </param>
965         /// <param name="fileIdentifier">
966         /// A FlatBuffer file identifier to be added to the buffer before
967         /// `root_table`.
968         /// </param>
969         /// <param name="sizePrefix">
970         /// Whether to prefix the size to the buffer.
971         /// </param>
Finish(int rootTable, string fileIdentifier, bool sizePrefix)972         protected void Finish(int rootTable, string fileIdentifier, bool sizePrefix)
973         {
974             Prep(_minAlign, sizeof(int) + (sizePrefix ? sizeof(int) : 0) +
975                             FlatBufferConstants.FileIdentifierLength);
976             if (fileIdentifier.Length !=
977                 FlatBufferConstants.FileIdentifierLength)
978                 throw new ArgumentException(
979                     "FlatBuffers: file identifier must be length " +
980                     FlatBufferConstants.FileIdentifierLength,
981                     "fileIdentifier");
982             for (int i = FlatBufferConstants.FileIdentifierLength - 1; i >= 0;
983                  i--)
984             {
985                AddByte((byte)fileIdentifier[i]);
986             }
987             Finish(rootTable, sizePrefix);
988         }
989 
990         /// <summary>
991         /// Finalize a buffer, pointing to the given `rootTable`.
992         /// </summary>
993         /// <param name="rootTable">
994         /// An offset to be added to the buffer.
995         /// </param>
996         /// <param name="fileIdentifier">
997         /// A FlatBuffer file identifier to be added to the buffer before
998         /// `root_table`.
999         /// </param>
Finish(int rootTable, string fileIdentifier)1000         public void Finish(int rootTable, string fileIdentifier)
1001         {
1002             Finish(rootTable, fileIdentifier, false);
1003         }
1004 
1005         /// <summary>
1006         /// Finalize a buffer, pointing to the given `rootTable`, with the size prefixed.
1007         /// </summary>
1008         /// <param name="rootTable">
1009         /// An offset to be added to the buffer.
1010         /// </param>
1011         /// <param name="fileIdentifier">
1012         /// A FlatBuffer file identifier to be added to the buffer before
1013         /// `root_table`.
1014         /// </param>
FinishSizePrefixed(int rootTable, string fileIdentifier)1015         public void FinishSizePrefixed(int rootTable, string fileIdentifier)
1016         {
1017             Finish(rootTable, fileIdentifier, true);
1018         }
1019     }
1020 }
1021 
1022 /// @}
1023