blob: c72b6248a1d79c78cc39d6c58aa65381c9d6598e [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
26namespace 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>
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 Schuh272c6132020-11-14 16:37:52 -0800213#if ENABLE_SPAN_T && (UNSAFE_BYTEBUFFER || NETSTANDARD2_1)
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700214 /// <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
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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
320 Prep(size, size * (x.Length - 1));
321 Put(x);
322 }
323
Austin Schuh272c6132020-11-14 16:37:52 -0800324#if ENABLE_SPAN_T && (UNSAFE_BYTEBUFFER || NETSTANDARD2_1)
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700325 /// <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
341 Prep(size, size * (x.Length - 1));
342 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>
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>
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
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>
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 {
392 NotNested();
393 StartVector(sizeof(int), offsets.Length, sizeof(int));
394 for (int i = offsets.Length - 1; i >= 0; i--) AddOffset(offsets[i].Value);
395 return EndVector();
396 }
397
398 /// @cond FLATBUFFERS_INTENRAL
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
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
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.
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>
450 public void AddBool(int o, bool x, bool d) { if (ForceDefaults || x != d) { AddBool(x); Slot(o); } }
451
452 /// <summary>
Austin Schuh272c6132020-11-14 16:37:52 -0800453 /// 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>
458 public void AddBool(int o, bool? x) { if (x.HasValue) { AddBool(x.Value); Slot(o); } }
459
460
461 /// <summary>
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700462 /// 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>
468 public void AddSbyte(int o, sbyte x, sbyte d) { if (ForceDefaults || x != d) { AddSbyte(x); Slot(o); } }
469
470 /// <summary>
Austin Schuh272c6132020-11-14 16:37:52 -0800471 /// 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>
476 public void AddSbyte(int o, sbyte? x) { if (x.HasValue) { AddSbyte(x.Value); Slot(o); } }
477
478 /// <summary>
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700479 /// 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>
485 public void AddByte(int o, byte x, byte d) { if (ForceDefaults || x != d) { AddByte(x); Slot(o); } }
486
487 /// <summary>
Austin Schuh272c6132020-11-14 16:37:52 -0800488 /// 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>
493 public void AddByte(int o, byte? x) { if (x.HasValue) { AddByte(x.Value); Slot(o); } }
494
495 /// <summary>
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700496 /// 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>
502 public void AddShort(int o, short x, int d) { if (ForceDefaults || x != d) { AddShort(x); Slot(o); } }
503
504 /// <summary>
Austin Schuh272c6132020-11-14 16:37:52 -0800505 /// 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>
510 public void AddShort(int o, short? x) { if (x.HasValue) { AddShort(x.Value); Slot(o); } }
511
512 /// <summary>
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700513 /// 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>
519 public void AddUshort(int o, ushort x, ushort d) { if (ForceDefaults || x != d) { AddUshort(x); Slot(o); } }
520
521 /// <summary>
Austin Schuh272c6132020-11-14 16:37:52 -0800522 /// 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>
527 public void AddUshort(int o, ushort? x) { if (x.HasValue) { AddUshort(x.Value); Slot(o); } }
528
529 /// <summary>
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700530 /// 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>
536 public void AddInt(int o, int x, int d) { if (ForceDefaults || x != d) { AddInt(x); Slot(o); } }
537
538 /// <summary>
Austin Schuh272c6132020-11-14 16:37:52 -0800539 /// 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>
544 public void AddInt(int o, int? x) { if (x.HasValue) { AddInt(x.Value); Slot(o); } }
545
546 /// <summary>
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700547 /// 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>
553 public void AddUint(int o, uint x, uint d) { if (ForceDefaults || x != d) { AddUint(x); Slot(o); } }
554
555 /// <summary>
Austin Schuh272c6132020-11-14 16:37:52 -0800556 /// 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>
561 public void AddUint(int o, uint? x) { if (x.HasValue) { AddUint(x.Value); Slot(o); } }
562
563 /// <summary>
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700564 /// 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>
570 public void AddLong(int o, long x, long d) { if (ForceDefaults || x != d) { AddLong(x); Slot(o); } }
571
572 /// <summary>
Austin Schuh272c6132020-11-14 16:37:52 -0800573 /// 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>
578 public void AddLong(int o, long? x) { if (x.HasValue) { AddLong(x.Value); Slot(o); } }
579
580 /// <summary>
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700581 /// 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>
587 public void AddUlong(int o, ulong x, ulong d) { if (ForceDefaults || x != d) { AddUlong(x); Slot(o); } }
588
589 /// <summary>
Austin Schuh272c6132020-11-14 16:37:52 -0800590 /// 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>
595 public void AddUlong(int o, ulong? x) { if (x.HasValue) { AddUlong(x.Value); Slot(o); } }
596
597 /// <summary>
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700598 /// 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>
604 public void AddFloat(int o, float x, double d) { if (ForceDefaults || x != d) { AddFloat(x); Slot(o); } }
605
606 /// <summary>
Austin Schuh272c6132020-11-14 16:37:52 -0800607 /// 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>
612 public void AddFloat(int o, float? x) { if (x.HasValue) { AddFloat(x.Value); Slot(o); } }
613
614 /// <summary>
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700615 /// 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>
621 public void AddDouble(int o, double x, double d) { if (ForceDefaults || x != d) { AddDouble(x); Slot(o); } }
622
623 /// <summary>
Austin Schuh272c6132020-11-14 16:37:52 -0800624 /// 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>
629 public void AddDouble(int o, double? x) { if (x.HasValue) { AddDouble(x.Value); Slot(o); } }
630
631 /// <summary>
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700632 /// 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>
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>
648 public StringOffset CreateString(string s)
649 {
Austin Schuh272c6132020-11-14 16:37:52 -0800650 if (s == null)
651 {
652 return new StringOffset(0);
653 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700654 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
Austin Schuh272c6132020-11-14 16:37:52 -0800663#if ENABLE_SPAN_T && (UNSAFE_BYTEBUFFER || NETSTANDARD2_1)
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700664 /// <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>
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>
692 public StringOffset CreateSharedString(string s)
693 {
Austin Schuh272c6132020-11-14 16:37:52 -0800694 if (s == null)
695 {
696 return new StringOffset(0);
697 }
698
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700699 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.
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
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.
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>
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>
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>
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>
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>
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>
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>
937 public void FinishSizePrefixed(int rootTable, string fileIdentifier)
938 {
939 Finish(rootTable, fileIdentifier, true);
940 }
941 }
942}
943
944/// @}