blob: e550f90f833d052a7cb4d40839e3cf567143dbde [file] [log] [blame]
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001/*
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
18using System;
19using System.Collections.Generic;
20using System.Text;
21
22/// @file
23/// @addtogroup flatbuffers_csharp_api
24/// @{
25
Austin Schuh2dd86a92022-09-14 21:19:23 -070026namespace Google.FlatBuffers
Austin Schuhe89fa2d2019-08-14 20:24:23 -070027{
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>
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>
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>
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;
Austin Schuh272c6132020-11-14 16:37:52 -080093 if (_sharedStringMap != null)
94 {
95 _sharedStringMap.Clear();
96 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -070097 }
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
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).
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.
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
151 public void PutBool(bool x)
152 {
153 _bb.PutByte(_space -= sizeof(byte), (byte)(x ? 1 : 0));
154 }
155
156 public void PutSbyte(sbyte x)
157 {
158 _bb.PutSbyte(_space -= sizeof(sbyte), x);
159 }
160
161 public void PutByte(byte x)
162 {
163 _bb.PutByte(_space -= sizeof(byte), x);
164 }
165
166 public void PutShort(short x)
167 {
168 _bb.PutShort(_space -= sizeof(short), x);
169 }
170
171 public void PutUshort(ushort x)
172 {
173 _bb.PutUshort(_space -= sizeof(ushort), x);
174 }
175
176 public void PutInt(int x)
177 {
178 _bb.PutInt(_space -= sizeof(int), x);
179 }
180
181 public void PutUint(uint x)
182 {
183 _bb.PutUint(_space -= sizeof(uint), x);
184 }
185
186 public void PutLong(long x)
187 {
188 _bb.PutLong(_space -= sizeof(long), x);
189 }
190
191 public void PutUlong(ulong x)
192 {
193 _bb.PutUlong(_space -= sizeof(ulong), x);
194 }
195
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
Austin Schuh2dd86a92022-09-14 21:19:23 -0700213 /// <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
Austin Schuh272c6132020-11-14 16:37:52 -0800238#if ENABLE_SPAN_T && (UNSAFE_BYTEBUFFER || NETSTANDARD2_1)
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700239 /// <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
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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 {
Austin Schuh2dd86a92022-09-14 21:19:23 -0700326 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 {
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700337 if (x == null)
338 {
339 throw new ArgumentNullException("Cannot add a null array");
340 }
341
Austin Schuh2dd86a92022-09-14 21:19:23 -0700342 if( x.Count == 0)
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700343 {
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
Austin Schuh2dd86a92022-09-14 21:19:23 -0700356 Prep(size, size * (x.Count - 1));
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700357 Put(x);
358 }
359
Austin Schuh2dd86a92022-09-14 21:19:23 -0700360 /// <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
398 Prep(size, sizeInBytes - size);
399 Put<T>(ptr, sizeInBytes);
400 }
401
Austin Schuh272c6132020-11-14 16:37:52 -0800402#if ENABLE_SPAN_T && (UNSAFE_BYTEBUFFER || NETSTANDARD2_1)
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700403 /// <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
419 Prep(size, size * (x.Length - 1));
420 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>
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>
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
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>
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 {
470 NotNested();
471 StartVector(sizeof(int), offsets.Length, sizeof(int));
472 for (int i = offsets.Length - 1; i >= 0; i--) AddOffset(offsets[i].Value);
473 return EndVector();
474 }
475
476 /// @cond FLATBUFFERS_INTENRAL
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
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
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.
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>
528 public void AddBool(int o, bool x, bool d) { if (ForceDefaults || x != d) { AddBool(x); Slot(o); } }
529
530 /// <summary>
Austin Schuh272c6132020-11-14 16:37:52 -0800531 /// 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>
536 public void AddBool(int o, bool? x) { if (x.HasValue) { AddBool(x.Value); Slot(o); } }
537
538
539 /// <summary>
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700540 /// 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>
546 public void AddSbyte(int o, sbyte x, sbyte d) { if (ForceDefaults || x != d) { AddSbyte(x); Slot(o); } }
547
548 /// <summary>
Austin Schuh272c6132020-11-14 16:37:52 -0800549 /// 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>
554 public void AddSbyte(int o, sbyte? x) { if (x.HasValue) { AddSbyte(x.Value); Slot(o); } }
555
556 /// <summary>
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700557 /// 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>
563 public void AddByte(int o, byte x, byte d) { if (ForceDefaults || x != d) { AddByte(x); Slot(o); } }
564
565 /// <summary>
Austin Schuh272c6132020-11-14 16:37:52 -0800566 /// 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>
571 public void AddByte(int o, byte? x) { if (x.HasValue) { AddByte(x.Value); Slot(o); } }
572
573 /// <summary>
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700574 /// 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>
580 public void AddShort(int o, short x, int d) { if (ForceDefaults || x != d) { AddShort(x); Slot(o); } }
581
582 /// <summary>
Austin Schuh272c6132020-11-14 16:37:52 -0800583 /// 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>
588 public void AddShort(int o, short? x) { if (x.HasValue) { AddShort(x.Value); Slot(o); } }
589
590 /// <summary>
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700591 /// 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>
597 public void AddUshort(int o, ushort x, ushort d) { if (ForceDefaults || x != d) { AddUshort(x); Slot(o); } }
598
599 /// <summary>
Austin Schuh272c6132020-11-14 16:37:52 -0800600 /// 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>
605 public void AddUshort(int o, ushort? x) { if (x.HasValue) { AddUshort(x.Value); Slot(o); } }
606
607 /// <summary>
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700608 /// 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>
614 public void AddInt(int o, int x, int d) { if (ForceDefaults || x != d) { AddInt(x); Slot(o); } }
615
616 /// <summary>
Austin Schuh272c6132020-11-14 16:37:52 -0800617 /// 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>
622 public void AddInt(int o, int? x) { if (x.HasValue) { AddInt(x.Value); Slot(o); } }
623
624 /// <summary>
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700625 /// 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>
631 public void AddUint(int o, uint x, uint d) { if (ForceDefaults || x != d) { AddUint(x); Slot(o); } }
632
633 /// <summary>
Austin Schuh272c6132020-11-14 16:37:52 -0800634 /// 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>
639 public void AddUint(int o, uint? x) { if (x.HasValue) { AddUint(x.Value); Slot(o); } }
640
641 /// <summary>
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700642 /// 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>
648 public void AddLong(int o, long x, long d) { if (ForceDefaults || x != d) { AddLong(x); Slot(o); } }
649
650 /// <summary>
Austin Schuh272c6132020-11-14 16:37:52 -0800651 /// 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>
656 public void AddLong(int o, long? x) { if (x.HasValue) { AddLong(x.Value); Slot(o); } }
657
658 /// <summary>
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700659 /// 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>
665 public void AddUlong(int o, ulong x, ulong d) { if (ForceDefaults || x != d) { AddUlong(x); Slot(o); } }
666
667 /// <summary>
Austin Schuh272c6132020-11-14 16:37:52 -0800668 /// 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>
673 public void AddUlong(int o, ulong? x) { if (x.HasValue) { AddUlong(x.Value); Slot(o); } }
674
675 /// <summary>
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700676 /// 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>
682 public void AddFloat(int o, float x, double d) { if (ForceDefaults || x != d) { AddFloat(x); Slot(o); } }
683
684 /// <summary>
Austin Schuh272c6132020-11-14 16:37:52 -0800685 /// 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>
690 public void AddFloat(int o, float? x) { if (x.HasValue) { AddFloat(x.Value); Slot(o); } }
691
692 /// <summary>
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700693 /// 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>
699 public void AddDouble(int o, double x, double d) { if (ForceDefaults || x != d) { AddDouble(x); Slot(o); } }
700
701 /// <summary>
Austin Schuh272c6132020-11-14 16:37:52 -0800702 /// 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>
707 public void AddDouble(int o, double? x) { if (x.HasValue) { AddDouble(x.Value); Slot(o); } }
708
709 /// <summary>
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700710 /// 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>
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>
726 public StringOffset CreateString(string s)
727 {
Austin Schuh272c6132020-11-14 16:37:52 -0800728 if (s == null)
729 {
730 return new StringOffset(0);
731 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700732 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
Austin Schuh272c6132020-11-14 16:37:52 -0800741#if ENABLE_SPAN_T && (UNSAFE_BYTEBUFFER || NETSTANDARD2_1)
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700742 /// <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>
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>
770 public StringOffset CreateSharedString(string s)
771 {
Austin Schuh272c6132020-11-14 16:37:52 -0800772 if (s == null)
773 {
774 return new StringOffset(0);
775 }
776
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700777 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.
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
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.
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>
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>
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>
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>
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>
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>
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>
1015 public void FinishSizePrefixed(int rootTable, string fileIdentifier)
1016 {
1017 Finish(rootTable, fileIdentifier, true);
1018 }
1019 }
1020}
1021
1022/// @}