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
18using System;
19using System.Collections.Generic;
20using System.Text;
21
22/// @file
23/// @addtogroup flatbuffers_csharp_api
24/// @{
25
26namespace Google.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;
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
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
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
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 {
326 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
356 Prep(size, size * (x.Count - 1));
357 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
398 Prep(size, sizeInBytes - size);
399 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
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 if (off != 0)
442 off = Offset - off + sizeof(int);
443 PutInt(off);
444 }
445
446 /// @cond FLATBUFFERS_INTERNAL
447 public void StartVector(int elemSize, int count, int alignment)
448 {
449 NotNested();
450 _vectorNumElems = count;
451 Prep(sizeof(int), elemSize * count);
452 Prep(alignment, elemSize * count); // Just in case alignment > int.
453 }
454 /// @endcond
455
456 /// <summary>
457 /// Writes data necessary to finish a vector construction.
458 /// </summary>
459 public VectorOffset EndVector()
460 {
461 PutInt(_vectorNumElems);
462 return new VectorOffset(Offset);
463 }
464
465 /// <summary>
466 /// Creates a vector of tables.
467 /// </summary>
468 /// <param name="offsets">Offsets of the tables.</param>
469 public VectorOffset CreateVectorOfTables<T>(Offset<T>[] offsets) where T : struct
470 {
471 NotNested();
472 StartVector(sizeof(int), offsets.Length, sizeof(int));
473 for (int i = offsets.Length - 1; i >= 0; i--) AddOffset(offsets[i].Value);
474 return EndVector();
475 }
476
477 /// @cond FLATBUFFERS_INTENRAL
478 public void Nested(int obj)
479 {
480 // Structs are always stored inline, so need to be created right
481 // where they are used. You'll get this assert if you created it
482 // elsewhere.
483 if (obj != Offset)
484 throw new Exception(
485 "FlatBuffers: struct must be serialized inline.");
486 }
487
488 public void NotNested()
489 {
490 // You should not be creating any other objects or strings/vectors
491 // while an object is being constructed
492 if (_vtableSize >= 0)
493 throw new Exception(
494 "FlatBuffers: object serialization must not be nested.");
495 }
496
497 public void StartTable(int numfields)
498 {
499 if (numfields < 0)
500 throw new ArgumentOutOfRangeException("Flatbuffers: invalid numfields");
501
502 NotNested();
503
504 if (_vtable.Length < numfields)
505 _vtable = new int[numfields];
506
507 _vtableSize = numfields;
508 _objectStart = Offset;
509 }
510
511
512 // Set the current vtable at `voffset` to the current location in the
513 // buffer.
514 public void Slot(int voffset)
515 {
516 if (voffset >= _vtableSize)
517 throw new IndexOutOfRangeException("Flatbuffers: invalid voffset");
518
519 _vtable[voffset] = Offset;
520 }
521
522 /// <summary>
523 /// Adds a Boolean to the Table at index `o` in its vtable using the value `x` and default `d`
524 /// </summary>
525 /// <param name="o">The index into the vtable</param>
526 /// <param name="x">The value to put into the buffer. If the value is equal to the default
527 /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param>
528 /// <param name="d">The default value to compare the value against</param>
529 public void AddBool(int o, bool x, bool d) { if (ForceDefaults || x != d) { AddBool(x); Slot(o); } }
530
531 /// <summary>
532 /// Adds a Boolean to the Table at index `o` in its vtable using the nullable value `x`
533 /// </summary>
534 /// <param name="o">The index into the vtable</param>
535 /// <param name="x">The nullable boolean value to put into the buffer. If it doesn't have a value
536 /// it will skip writing to the buffer.</param>
537 public void AddBool(int o, bool? x) { if (x.HasValue) { AddBool(x.Value); Slot(o); } }
538
539
540 /// <summary>
541 /// Adds a SByte to the Table at index `o` in its vtable using the value `x` and default `d`
542 /// </summary>
543 /// <param name="o">The index into the vtable</param>
544 /// <param name="x">The value to put into the buffer. If the value is equal to the default
545 /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param>
546 /// <param name="d">The default value to compare the value against</param>
547 public void AddSbyte(int o, sbyte x, sbyte d) { if (ForceDefaults || x != d) { AddSbyte(x); Slot(o); } }
548
549 /// <summary>
550 /// Adds a SByte to the Table at index `o` in its vtable using the nullable value `x`
551 /// </summary>
552 /// <param name="o">The index into the vtable</param>
553 /// <param name="x">The nullable sbyte value to put into the buffer. If it doesn't have a value
554 /// it will skip writing to the buffer.</param>
555 public void AddSbyte(int o, sbyte? x) { if (x.HasValue) { AddSbyte(x.Value); Slot(o); } }
556
557 /// <summary>
558 /// Adds a Byte to the Table at index `o` in its vtable using the value `x` and default `d`
559 /// </summary>
560 /// <param name="o">The index into the vtable</param>
561 /// <param name="x">The value to put into the buffer. If the value is equal to the default
562 /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param>
563 /// <param name="d">The default value to compare the value against</param>
564 public void AddByte(int o, byte x, byte d) { if (ForceDefaults || x != d) { AddByte(x); Slot(o); } }
565
566 /// <summary>
567 /// Adds a Byte to the Table at index `o` in its vtable using the nullable value `x`
568 /// </summary>
569 /// <param name="o">The index into the vtable</param>
570 /// <param name="x">The nullable byte value to put into the buffer. If it doesn't have a value
571 /// it will skip writing to the buffer.</param>
572 public void AddByte(int o, byte? x) { if (x.HasValue) { AddByte(x.Value); Slot(o); } }
573
574 /// <summary>
575 /// Adds a Int16 to the Table at index `o` in its vtable using the value `x` and default `d`
576 /// </summary>
577 /// <param name="o">The index into the vtable</param>
578 /// <param name="x">The value to put into the buffer. If the value is equal to the default
579 /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param>
580 /// <param name="d">The default value to compare the value against</param>
581 public void AddShort(int o, short x, int d) { if (ForceDefaults || x != d) { AddShort(x); Slot(o); } }
582
583 /// <summary>
584 /// Adds a Int16 to the Table at index `o` in its vtable using the nullable value `x`
585 /// </summary>
586 /// <param name="o">The index into the vtable</param>
587 /// <param name="x">The nullable int16 value to put into the buffer. If it doesn't have a value
588 /// it will skip writing to the buffer.</param>
589 public void AddShort(int o, short? x) { if (x.HasValue) { AddShort(x.Value); Slot(o); } }
590
591 /// <summary>
592 /// Adds a UInt16 to the Table at index `o` in its vtable using the value `x` and default `d`
593 /// </summary>
594 /// <param name="o">The index into the vtable</param>
595 /// <param name="x">The value to put into the buffer. If the value is equal to the default
596 /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param>
597 /// <param name="d">The default value to compare the value against</param>
598 public void AddUshort(int o, ushort x, ushort d) { if (ForceDefaults || x != d) { AddUshort(x); Slot(o); } }
599
600 /// <summary>
601 /// Adds a Uint16 to the Table at index `o` in its vtable using the nullable value `x`
602 /// </summary>
603 /// <param name="o">The index into the vtable</param>
604 /// <param name="x">The nullable uint16 value to put into the buffer. If it doesn't have a value
605 /// it will skip writing to the buffer.</param>
606 public void AddUshort(int o, ushort? x) { if (x.HasValue) { AddUshort(x.Value); Slot(o); } }
607
608 /// <summary>
609 /// Adds an Int32 to the Table at index `o` in its vtable using the value `x` and default `d`
610 /// </summary>
611 /// <param name="o">The index into the vtable</param>
612 /// <param name="x">The value to put into the buffer. If the value is equal to the default
613 /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param>
614 /// <param name="d">The default value to compare the value against</param>
615 public void AddInt(int o, int x, int d) { if (ForceDefaults || x != d) { AddInt(x); Slot(o); } }
616
617 /// <summary>
618 /// Adds a Int32 to the Table at index `o` in its vtable using the nullable value `x`
619 /// </summary>
620 /// <param name="o">The index into the vtable</param>
621 /// <param name="x">The nullable int32 value to put into the buffer. If it doesn't have a value
622 /// it will skip writing to the buffer.</param>
623 public void AddInt(int o, int? x) { if (x.HasValue) { AddInt(x.Value); Slot(o); } }
624
625 /// <summary>
626 /// Adds a UInt32 to the Table at index `o` in its vtable using the value `x` and default `d`
627 /// </summary>
628 /// <param name="o">The index into the vtable</param>
629 /// <param name="x">The value to put into the buffer. If the value is equal to the default
630 /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param>
631 /// <param name="d">The default value to compare the value against</param>
632 public void AddUint(int o, uint x, uint d) { if (ForceDefaults || x != d) { AddUint(x); Slot(o); } }
633
634 /// <summary>
635 /// Adds a UInt32 to the Table at index `o` in its vtable using the nullable value `x`
636 /// </summary>
637 /// <param name="o">The index into the vtable</param>
638 /// <param name="x">The nullable uint32 value to put into the buffer. If it doesn't have a value
639 /// it will skip writing to the buffer.</param>
640 public void AddUint(int o, uint? x) { if (x.HasValue) { AddUint(x.Value); Slot(o); } }
641
642 /// <summary>
643 /// Adds an Int64 to the Table at index `o` in its vtable using the value `x` and default `d`
644 /// </summary>
645 /// <param name="o">The index into the vtable</param>
646 /// <param name="x">The value to put into the buffer. If the value is equal to the default
647 /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param>
648 /// <param name="d">The default value to compare the value against</param>
649 public void AddLong(int o, long x, long d) { if (ForceDefaults || x != d) { AddLong(x); Slot(o); } }
650
651 /// <summary>
652 /// Adds a Int64 to the Table at index `o` in its vtable using the nullable value `x`
653 /// </summary>
654 /// <param name="o">The index into the vtable</param>
655 /// <param name="x">The nullable int64 value to put into the buffer. If it doesn't have a value
656 /// it will skip writing to the buffer.</param>
657 public void AddLong(int o, long? x) { if (x.HasValue) { AddLong(x.Value); Slot(o); } }
658
659 /// <summary>
660 /// Adds a UInt64 to the Table at index `o` in its vtable using the value `x` and default `d`
661 /// </summary>
662 /// <param name="o">The index into the vtable</param>
663 /// <param name="x">The value to put into the buffer. If the value is equal to the default
664 /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param>
665 /// <param name="d">The default value to compare the value against</param>
666 public void AddUlong(int o, ulong x, ulong d) { if (ForceDefaults || x != d) { AddUlong(x); Slot(o); } }
667
668 /// <summary>
669 /// Adds a UInt64 to the Table at index `o` in its vtable using the nullable value `x`
670 /// </summary>
671 /// <param name="o">The index into the vtable</param>
672 /// <param name="x">The nullable int64 value to put into the buffer. If it doesn't have a value
673 /// it will skip writing to the buffer.</param>
674 public void AddUlong(int o, ulong? x) { if (x.HasValue) { AddUlong(x.Value); Slot(o); } }
675
676 /// <summary>
677 /// Adds a Single to the Table at index `o` in its vtable using the value `x` and default `d`
678 /// </summary>
679 /// <param name="o">The index into the vtable</param>
680 /// <param name="x">The value to put into the buffer. If the value is equal to the default
681 /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param>
682 /// <param name="d">The default value to compare the value against</param>
683 public void AddFloat(int o, float x, double d) { if (ForceDefaults || x != d) { AddFloat(x); Slot(o); } }
684
685 /// <summary>
686 /// Adds a Single to the Table at index `o` in its vtable using the nullable value `x`
687 /// </summary>
688 /// <param name="o">The index into the vtable</param>
689 /// <param name="x">The nullable single value to put into the buffer. If it doesn't have a value
690 /// it will skip writing to the buffer.</param>
691 public void AddFloat(int o, float? x) { if (x.HasValue) { AddFloat(x.Value); Slot(o); } }
692
693 /// <summary>
694 /// Adds a Double to the Table at index `o` in its vtable using the value `x` and default `d`
695 /// </summary>
696 /// <param name="o">The index into the vtable</param>
697 /// <param name="x">The value to put into the buffer. If the value is equal to the default
698 /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param>
699 /// <param name="d">The default value to compare the value against</param>
700 public void AddDouble(int o, double x, double d) { if (ForceDefaults || x != d) { AddDouble(x); Slot(o); } }
701
702 /// <summary>
703 /// Adds a Double to the Table at index `o` in its vtable using the nullable value `x`
704 /// </summary>
705 /// <param name="o">The index into the vtable</param>
706 /// <param name="x">The nullable double value to put into the buffer. If it doesn't have a value
707 /// it will skip writing to the buffer.</param>
708 public void AddDouble(int o, double? x) { if (x.HasValue) { AddDouble(x.Value); Slot(o); } }
709
710 /// <summary>
711 /// Adds a buffer offset to the Table at index `o` in its vtable using the value `x` and default `d`
712 /// </summary>
713 /// <param name="o">The index into the vtable</param>
714 /// <param name="x">The value to put into the buffer. If the value is equal to the default
715 /// the value will be skipped.</param>
716 /// <param name="d">The default value to compare the value against</param>
717 public void AddOffset(int o, int x, int d) { if (x != d) { AddOffset(x); Slot(o); } }
718 /// @endcond
719
720 /// <summary>
721 /// Encode the string `s` in the buffer using UTF-8.
722 /// </summary>
723 /// <param name="s">The string to encode.</param>
724 /// <returns>
725 /// The offset in the buffer where the encoded string starts.
726 /// </returns>
727 public StringOffset CreateString(string s)
728 {
729 if (s == null)
730 {
731 return new StringOffset(0);
732 }
733 NotNested();
734 AddByte(0);
735 var utf8StringLen = Encoding.UTF8.GetByteCount(s);
736 StartVector(1, utf8StringLen, 1);
737 _bb.PutStringUTF8(_space -= utf8StringLen, s);
738 return new StringOffset(EndVector().Value);
739 }
740
741
742#if ENABLE_SPAN_T && (UNSAFE_BYTEBUFFER || NETSTANDARD2_1)
743 /// <summary>
744 /// Creates a string in the buffer from a Span containing
745 /// a UTF8 string.
746 /// </summary>
747 /// <param name="chars">the UTF8 string to add to the buffer</param>
748 /// <returns>
749 /// The offset in the buffer where the encoded string starts.
750 /// </returns>
751 public StringOffset CreateUTF8String(Span<byte> chars)
752 {
753 NotNested();
754 AddByte(0);
755 var utf8StringLen = chars.Length;
756 StartVector(1, utf8StringLen, 1);
757 _space = _bb.Put(_space, chars);
758 return new StringOffset(EndVector().Value);
759 }
760#endif
761
762 /// <summary>
763 /// Store a string in the buffer, which can contain any binary data.
764 /// If a string with this exact contents has already been serialized before,
765 /// instead simply returns the offset of the existing string.
766 /// </summary>
767 /// <param name="s">The string to encode.</param>
768 /// <returns>
769 /// The offset in the buffer where the encoded string starts.
770 /// </returns>
771 public StringOffset CreateSharedString(string s)
772 {
773 if (s == null)
774 {
775 return new StringOffset(0);
776 }
777
778 if (_sharedStringMap == null)
779 {
780 _sharedStringMap = new Dictionary<string, StringOffset>();
781 }
782
783 if (_sharedStringMap.ContainsKey(s))
784 {
785 return _sharedStringMap[s];
786 }
787
788 var stringOffset = CreateString(s);
789 _sharedStringMap.Add(s, stringOffset);
790 return stringOffset;
791 }
792
793 /// @cond FLATBUFFERS_INTERNAL
794 // Structs are stored inline, so nothing additional is being added.
795 // `d` is always 0.
796 public void AddStruct(int voffset, int x, int d)
797 {
798 if (x != d)
799 {
800 Nested(x);
801 Slot(voffset);
802 }
803 }
804
805 public int EndTable()
806 {
807 if (_vtableSize < 0)
808 throw new InvalidOperationException(
809 "Flatbuffers: calling EndTable without a StartTable");
810
811 AddInt((int)0);
812 var vtableloc = Offset;
813 // Write out the current vtable.
814 int i = _vtableSize - 1;
815 // Trim trailing zeroes.
816 for (; i >= 0 && _vtable[i] == 0; i--) {}
817 int trimmedSize = i + 1;
818 for (; i >= 0 ; i--) {
819 // Offset relative to the start of the table.
820 short off = (short)(_vtable[i] != 0
821 ? vtableloc - _vtable[i]
822 : 0);
823 AddShort(off);
824
825 // clear out written entry
826 _vtable[i] = 0;
827 }
828
829 const int standardFields = 2; // The fields below:
830 AddShort((short)(vtableloc - _objectStart));
831 AddShort((short)((trimmedSize + standardFields) *
832 sizeof(short)));
833
834 // Search for an existing vtable that matches the current one.
835 int existingVtable = 0;
836 for (i = 0; i < _numVtables; i++) {
837 int vt1 = _bb.Length - _vtables[i];
838 int vt2 = _space;
839 short len = _bb.GetShort(vt1);
840 if (len == _bb.GetShort(vt2)) {
841 for (int j = sizeof(short); j < len; j += sizeof(short)) {
842 if (_bb.GetShort(vt1 + j) != _bb.GetShort(vt2 + j)) {
843 goto endLoop;
844 }
845 }
846 existingVtable = _vtables[i];
847 break;
848 }
849
850 endLoop: { }
851 }
852
853 if (existingVtable != 0) {
854 // Found a match:
855 // Remove the current vtable.
856 _space = _bb.Length - vtableloc;
857 // Point table to existing vtable.
858 _bb.PutInt(_space, existingVtable - vtableloc);
859 } else {
860 // No match:
861 // Add the location of the current vtable to the list of
862 // vtables.
863 if (_numVtables == _vtables.Length)
864 {
865 // Arrays.CopyOf(vtables num_vtables * 2);
866 var newvtables = new int[ _numVtables * 2];
867 Array.Copy(_vtables, newvtables, _vtables.Length);
868
869 _vtables = newvtables;
870 };
871 _vtables[_numVtables++] = Offset;
872 // Point table to current vtable.
873 _bb.PutInt(_bb.Length - vtableloc, Offset - vtableloc);
874 }
875
876 _vtableSize = -1;
877 return vtableloc;
878 }
879
880 // This checks a required field has been set in a given table that has
881 // just been constructed.
882 public void Required(int table, int field)
883 {
884 int table_start = _bb.Length - table;
885 int vtable_start = table_start - _bb.GetInt(table_start);
886 bool ok = _bb.GetShort(vtable_start + field) != 0;
887 // If this fails, the caller will show what field needs to be set.
888 if (!ok)
889 throw new InvalidOperationException("FlatBuffers: field " + field +
890 " must be set");
891 }
892 /// @endcond
893
894 /// <summary>
895 /// Finalize a buffer, pointing to the given `root_table`.
896 /// </summary>
897 /// <param name="rootTable">
898 /// An offset to be added to the buffer.
899 /// </param>
900 /// <param name="sizePrefix">
901 /// Whether to prefix the size to the buffer.
902 /// </param>
903 protected void Finish(int rootTable, bool sizePrefix)
904 {
905 Prep(_minAlign, sizeof(int) + (sizePrefix ? sizeof(int) : 0));
906 AddOffset(rootTable);
907 if (sizePrefix) {
908 AddInt(_bb.Length - _space);
909 }
910 _bb.Position = _space;
911 }
912
913 /// <summary>
914 /// Finalize a buffer, pointing to the given `root_table`.
915 /// </summary>
916 /// <param name="rootTable">
917 /// An offset to be added to the buffer.
918 /// </param>
919 public void Finish(int rootTable)
920 {
921 Finish(rootTable, false);
922 }
923
924 /// <summary>
925 /// Finalize a buffer, pointing to the given `root_table`, with the size prefixed.
926 /// </summary>
927 /// <param name="rootTable">
928 /// An offset to be added to the buffer.
929 /// </param>
930 public void FinishSizePrefixed(int rootTable)
931 {
932 Finish(rootTable, true);
933 }
934
935 /// <summary>
936 /// Get the ByteBuffer representing the FlatBuffer.
937 /// </summary>
938 /// <remarks>
939 /// This is typically only called after you call `Finish()`.
940 /// The actual data starts at the ByteBuffer's current position,
941 /// not necessarily at `0`.
942 /// </remarks>
943 /// <returns>
944 /// Returns the ByteBuffer for this FlatBuffer.
945 /// </returns>
946 public ByteBuffer DataBuffer { get { return _bb; } }
947
948 /// <summary>
949 /// A utility function to copy and return the ByteBuffer data as a
950 /// `byte[]`.
951 /// </summary>
952 /// <returns>
953 /// A full copy of the FlatBuffer data.
954 /// </returns>
955 public byte[] SizedByteArray()
956 {
957 return _bb.ToSizedArray();
958 }
959
960 /// <summary>
961 /// Finalize a buffer, pointing to the given `rootTable`.
962 /// </summary>
963 /// <param name="rootTable">
964 /// An offset to be added to the buffer.
965 /// </param>
966 /// <param name="fileIdentifier">
967 /// A FlatBuffer file identifier to be added to the buffer before
968 /// `root_table`.
969 /// </param>
970 /// <param name="sizePrefix">
971 /// Whether to prefix the size to the buffer.
972 /// </param>
973 protected void Finish(int rootTable, string fileIdentifier, bool sizePrefix)
974 {
975 Prep(_minAlign, sizeof(int) + (sizePrefix ? sizeof(int) : 0) +
976 FlatBufferConstants.FileIdentifierLength);
977 if (fileIdentifier.Length !=
978 FlatBufferConstants.FileIdentifierLength)
979 throw new ArgumentException(
980 "FlatBuffers: file identifier must be length " +
981 FlatBufferConstants.FileIdentifierLength,
982 "fileIdentifier");
983 for (int i = FlatBufferConstants.FileIdentifierLength - 1; i >= 0;
984 i--)
985 {
986 AddByte((byte)fileIdentifier[i]);
987 }
988 Finish(rootTable, sizePrefix);
989 }
990
991 /// <summary>
992 /// Finalize a buffer, pointing to the given `rootTable`.
993 /// </summary>
994 /// <param name="rootTable">
995 /// An offset to be added to the buffer.
996 /// </param>
997 /// <param name="fileIdentifier">
998 /// A FlatBuffer file identifier to be added to the buffer before
999 /// `root_table`.
1000 /// </param>
1001 public void Finish(int rootTable, string fileIdentifier)
1002 {
1003 Finish(rootTable, fileIdentifier, false);
1004 }
1005
1006 /// <summary>
1007 /// Finalize a buffer, pointing to the given `rootTable`, with the size prefixed.
1008 /// </summary>
1009 /// <param name="rootTable">
1010 /// An offset to be added to the buffer.
1011 /// </param>
1012 /// <param name="fileIdentifier">
1013 /// A FlatBuffer file identifier to be added to the buffer before
1014 /// `root_table`.
1015 /// </param>
1016 public void FinishSizePrefixed(int rootTable, string fileIdentifier)
1017 {
1018 Finish(rootTable, fileIdentifier, true);
1019 }
1020 }
1021}
1022
1023/// @}
View as plain text