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