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 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> 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 passed 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 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 { AddGoogle.FlatBuffers.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 PrepGoogle.FlatBuffers.FlatBufferBuilder.__anon6356 Prep(size, size * (x.Count - 1)); PutGoogle.FlatBuffers.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 PrepGoogle.FlatBuffers.FlatBufferBuilder.__anon7398 Prep(size, sizeInBytes - size); PutGoogle.FlatBuffers.FlatBufferBuilder.__anon7399 Put<T>(ptr, sizeInBytes); 400 } 401 402 #if ENABLE_SPAN_T && UNSAFE_BYTEBUFFER 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 PrepGoogle.FlatBuffers.FlatBufferBuilder.__anon8419 Prep(size, size * (x.Length - 1)); PutGoogle.FlatBuffers.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 if (off != 0) 442 off = Offset - off + sizeof(int); 443 PutInt(off); 444 } 445 446 /// @cond FLATBUFFERS_INTERNAL StartVector(int elemSize, int count, int alignment)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> EndVector()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 { NotNestedGoogle.FlatBuffers.FlatBufferBuilder.__anon9471 NotNested(); sizeofGoogle.FlatBuffers.FlatBufferBuilder.__anon9472 StartVector(sizeof(int), offsets.Length, sizeof(int)); AddOffsetGoogle.FlatBuffers.FlatBufferBuilder.__anon9473 for (int i = offsets.Length - 1; i >= 0; i--) AddOffset(offsets[i].Value); 474 return EndVector(); 475 } 476 477 /// @cond FLATBUFFERS_INTENRAL Nested(int obj)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 NotNested()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 StartTable(int numfields)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. Slot(int voffset)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> AddBool(int o, bool x, bool d)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> AddBool(int o, bool? x)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> AddSbyte(int o, sbyte x, sbyte d)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> AddSbyte(int o, sbyte? x)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> AddByte(int o, byte x, byte d)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> AddByte(int o, byte? x)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> AddShort(int o, short x, int d)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> AddShort(int o, short? x)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> AddUshort(int o, ushort x, ushort d)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> AddUshort(int o, ushort? x)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> AddInt(int o, int x, int d)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> AddInt(int o, int? x)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> AddUint(int o, uint x, uint d)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> AddUint(int o, uint? x)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> AddLong(int o, long x, long d)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> AddLong(int o, long? x)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> AddUlong(int o, ulong x, ulong d)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> AddUlong(int o, ulong? x)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> AddFloat(int o, float x, double d)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> AddFloat(int o, float? x)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> AddDouble(int o, double x, double d)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> AddDouble(int o, double? x)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> AddOffset(int o, int x, int d)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> CreateString(string s)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 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> CreateUTF8String(Span<byte> chars)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> CreateSharedString(string s)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. AddStruct(int voffset, int x, int d)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 EndTable()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. Required(int table, int field)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> Finish(int rootTable, bool sizePrefix)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> Finish(int rootTable)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> FinishSizePrefixed(int rootTable)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> SizedByteArray()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> Finish(int rootTable, string fileIdentifier, bool sizePrefix)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> Finish(int rootTable, string fileIdentifier)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> FinishSizePrefixed(int rootTable, string fileIdentifier)1016 public void FinishSizePrefixed(int rootTable, string fileIdentifier) 1017 { 1018 Finish(rootTable, fileIdentifier, true); 1019 } 1020 } 1021 } 1022 1023 /// @} 1024