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.Text; 20 21 /// @file 22 /// @addtogroup flatbuffers_csharp_api 23 /// @{ 24 25 namespace FlatBuffers 26 { 27 /// <summary> 28 /// Responsible for building up and accessing a FlatBuffer formatted byte 29 /// array (via ByteBuffer). 30 /// </summary> 31 public class FlatBufferBuilder 32 { 33 private int _space; 34 private ByteBuffer _bb; 35 private int _minAlign = 1; 36 37 // The vtable for the current table (if _vtableSize >= 0) 38 private int[] _vtable = new int[16]; 39 // The size of the vtable. -1 indicates no vtable 40 private int _vtableSize = -1; 41 // Starting offset of the current struct/table. 42 private int _objectStart; 43 // List of offsets of all vtables. 44 private int[] _vtables = new int[16]; 45 // Number of entries in `vtables` in use. 46 private int _numVtables = 0; 47 // For the current vector being built. 48 private int _vectorNumElems = 0; 49 50 /// <summary> 51 /// Create a FlatBufferBuilder with a given initial size. 52 /// </summary> 53 /// <param name="initialSize"> 54 /// The initial size to use for the internal buffer. 55 /// </param> FlatBufferBuilder(int initialSize)56 public FlatBufferBuilder(int initialSize) 57 { 58 if (initialSize <= 0) 59 throw new ArgumentOutOfRangeException("initialSize", 60 initialSize, "Must be greater than zero"); 61 _space = initialSize; 62 _bb = new ByteBuffer(initialSize); 63 } 64 65 /// <summary> 66 /// Create a FlatBufferBuilder backed by the pased in ByteBuffer 67 /// </summary> 68 /// <param name="buffer">The ByteBuffer to write to</param> FlatBufferBuilder(ByteBuffer buffer)69 public FlatBufferBuilder(ByteBuffer buffer) 70 { 71 _bb = buffer; 72 _space = buffer.Length; 73 buffer.Reset(); 74 } 75 76 /// <summary> 77 /// Reset the FlatBufferBuilder by purging all data that it holds. 78 /// </summary> Clear()79 public void Clear() 80 { 81 _space = _bb.Length; 82 _bb.Reset(); 83 _minAlign = 1; 84 while (_vtableSize > 0) _vtable[--_vtableSize] = 0; 85 _vtableSize = -1; 86 _objectStart = 0; 87 _numVtables = 0; 88 _vectorNumElems = 0; 89 } 90 91 /// <summary> 92 /// Gets and sets a Boolean to disable the optimization when serializing 93 /// default values to a Table. 94 /// 95 /// In order to save space, fields that are set to their default value 96 /// don't get serialized into the buffer. 97 /// </summary> 98 public bool ForceDefaults { get; set; } 99 100 /// @cond FLATBUFFERS_INTERNAL 101 102 public int Offset { get { return _bb.Length - _space; } } 103 Pad(int size)104 public void Pad(int size) 105 { 106 _bb.PutByte(_space -= size, 0, size); 107 } 108 109 // Doubles the size of the ByteBuffer, and copies the old data towards 110 // the end of the new buffer (since we build the buffer backwards). GrowBuffer()111 void GrowBuffer() 112 { 113 _bb.GrowFront(_bb.Length << 1); 114 } 115 116 // Prepare to write an element of `size` after `additional_bytes` 117 // have been written, e.g. if you write a string, you need to align 118 // such the int length field is aligned to SIZEOF_INT, and the string 119 // data follows it directly. 120 // If all you need to do is align, `additional_bytes` will be 0. Prep(int size, int additionalBytes)121 public void Prep(int size, int additionalBytes) 122 { 123 // Track the biggest thing we've ever aligned to. 124 if (size > _minAlign) 125 _minAlign = size; 126 // Find the amount of alignment needed such that `size` is properly 127 // aligned after `additional_bytes` 128 var alignSize = 129 ((~((int)_bb.Length - _space + additionalBytes)) + 1) & 130 (size - 1); 131 // Reallocate the buffer if needed. 132 while (_space < alignSize + size + additionalBytes) 133 { 134 var oldBufSize = (int)_bb.Length; 135 GrowBuffer(); 136 _space += (int)_bb.Length - oldBufSize; 137 138 } 139 if (alignSize > 0) 140 Pad(alignSize); 141 } 142 PutBool(bool x)143 public void PutBool(bool x) 144 { 145 _bb.PutByte(_space -= sizeof(byte), (byte)(x ? 1 : 0)); 146 } 147 PutSbyte(sbyte x)148 public void PutSbyte(sbyte x) 149 { 150 _bb.PutSbyte(_space -= sizeof(sbyte), x); 151 } 152 PutByte(byte x)153 public void PutByte(byte x) 154 { 155 _bb.PutByte(_space -= sizeof(byte), x); 156 } 157 PutShort(short x)158 public void PutShort(short x) 159 { 160 _bb.PutShort(_space -= sizeof(short), x); 161 } 162 PutUshort(ushort x)163 public void PutUshort(ushort x) 164 { 165 _bb.PutUshort(_space -= sizeof(ushort), x); 166 } 167 PutInt(int x)168 public void PutInt(int x) 169 { 170 _bb.PutInt(_space -= sizeof(int), x); 171 } 172 PutUint(uint x)173 public void PutUint(uint x) 174 { 175 _bb.PutUint(_space -= sizeof(uint), x); 176 } 177 PutLong(long x)178 public void PutLong(long x) 179 { 180 _bb.PutLong(_space -= sizeof(long), x); 181 } 182 PutUlong(ulong x)183 public void PutUlong(ulong x) 184 { 185 _bb.PutUlong(_space -= sizeof(ulong), x); 186 } 187 PutFloat(float x)188 public void PutFloat(float x) 189 { 190 _bb.PutFloat(_space -= sizeof(float), x); 191 } 192 193 /// <summary> 194 /// Puts an array of type T into this builder at the 195 /// current offset 196 /// </summary> 197 /// <typeparam name="T">The type of the input data </typeparam> 198 /// <param name="x">The array to copy data from</param> 199 public void Put<T>(T[] x) 200 where T : struct 201 { 202 _space = _bb.Put(_space, x); 203 } 204 205 #if ENABLE_SPAN_T 206 /// <summary> 207 /// Puts a span of type T into this builder at the 208 /// current offset 209 /// </summary> 210 /// <typeparam name="T">The type of the input data </typeparam> 211 /// <param name="x">The span to copy data from</param> 212 public void Put<T>(Span<T> x) 213 where T : struct 214 { 215 _space = _bb.Put(_space, x); 216 } 217 #endif 218 PutDouble(double x)219 public void PutDouble(double x) 220 { 221 _bb.PutDouble(_space -= sizeof(double), x); 222 } 223 /// @endcond 224 225 /// <summary> 226 /// Add a `bool` to the buffer (aligns the data and grows if necessary). 227 /// </summary> 228 /// <param name="x">The `bool` to add to the buffer.</param> AddBool(bool x)229 public void AddBool(bool x) { Prep(sizeof(byte), 0); PutBool(x); } 230 231 /// <summary> 232 /// Add a `sbyte` to the buffer (aligns the data and grows if necessary). 233 /// </summary> 234 /// <param name="x">The `sbyte` to add to the buffer.</param> AddSbyte(sbyte x)235 public void AddSbyte(sbyte x) { Prep(sizeof(sbyte), 0); PutSbyte(x); } 236 237 /// <summary> 238 /// Add a `byte` to the buffer (aligns the data and grows if necessary). 239 /// </summary> 240 /// <param name="x">The `byte` to add to the buffer.</param> AddByte(byte x)241 public void AddByte(byte x) { Prep(sizeof(byte), 0); PutByte(x); } 242 243 /// <summary> 244 /// Add a `short` to the buffer (aligns the data and grows if necessary). 245 /// </summary> 246 /// <param name="x">The `short` to add to the buffer.</param> AddShort(short x)247 public void AddShort(short x) { Prep(sizeof(short), 0); PutShort(x); } 248 249 /// <summary> 250 /// Add an `ushort` to the buffer (aligns the data and grows if necessary). 251 /// </summary> 252 /// <param name="x">The `ushort` to add to the buffer.</param> AddUshort(ushort x)253 public void AddUshort(ushort x) { Prep(sizeof(ushort), 0); PutUshort(x); } 254 255 /// <summary> 256 /// Add an `int` to the buffer (aligns the data and grows if necessary). 257 /// </summary> 258 /// <param name="x">The `int` to add to the buffer.</param> AddInt(int x)259 public void AddInt(int x) { Prep(sizeof(int), 0); PutInt(x); } 260 261 /// <summary> 262 /// Add an `uint` to the buffer (aligns the data and grows if necessary). 263 /// </summary> 264 /// <param name="x">The `uint` to add to the buffer.</param> AddUint(uint x)265 public void AddUint(uint x) { Prep(sizeof(uint), 0); PutUint(x); } 266 267 /// <summary> 268 /// Add a `long` to the buffer (aligns the data and grows if necessary). 269 /// </summary> 270 /// <param name="x">The `long` to add to the buffer.</param> AddLong(long x)271 public void AddLong(long x) { Prep(sizeof(long), 0); PutLong(x); } 272 273 /// <summary> 274 /// Add an `ulong` to the buffer (aligns the data and grows if necessary). 275 /// </summary> 276 /// <param name="x">The `ulong` to add to the buffer.</param> AddUlong(ulong x)277 public void AddUlong(ulong x) { Prep(sizeof(ulong), 0); PutUlong(x); } 278 279 /// <summary> 280 /// Add a `float` to the buffer (aligns the data and grows if necessary). 281 /// </summary> 282 /// <param name="x">The `float` to add to the buffer.</param> AddFloat(float x)283 public void AddFloat(float x) { Prep(sizeof(float), 0); PutFloat(x); } 284 285 /// <summary> 286 /// Add an array of type T to the buffer (aligns the data and grows if necessary). 287 /// </summary> 288 /// <typeparam name="T">The type of the input data</typeparam> 289 /// <param name="x">The array to copy data from</param> 290 public void Add<T>(T[] x) 291 where T : struct 292 { 293 if (x == null) 294 { 295 throw new ArgumentNullException("Cannot add a null array"); 296 } 297 298 if( x.Length == 0) 299 { 300 // don't do anything if the array is empty 301 return; 302 } 303 304 if(!ByteBuffer.IsSupportedType<T>()) 305 { 306 throw new ArgumentException("Cannot add this Type array to the builder"); 307 } 308 309 int size = ByteBuffer.SizeOf<T>(); 310 // Need to prep on size (for data alignment) and then we pass the 311 // rest of the length (minus 1) as additional bytes PrepFlatBuffers.FlatBufferBuilder.__anon3312 Prep(size, size * (x.Length - 1)); PutFlatBuffers.FlatBufferBuilder.__anon3313 Put(x); 314 } 315 316 #if ENABLE_SPAN_T 317 /// <summary> 318 /// Add a span of type T to the buffer (aligns the data and grows if necessary). 319 /// </summary> 320 /// <typeparam name="T">The type of the input data</typeparam> 321 /// <param name="x">The span to copy data from</param> 322 public void Add<T>(Span<T> x) 323 where T : struct 324 { 325 if (!ByteBuffer.IsSupportedType<T>()) 326 { 327 throw new ArgumentException("Cannot add this Type array to the builder"); 328 } 329 330 int size = ByteBuffer.SizeOf<T>(); 331 // Need to prep on size (for data alignment) and then we pass the 332 // rest of the length (minus 1) as additional bytes PrepFlatBuffers.FlatBufferBuilder.__anon4333 Prep(size, size * (x.Length - 1)); PutFlatBuffers.FlatBufferBuilder.__anon4334 Put(x); 335 } 336 #endif 337 338 /// <summary> 339 /// Add a `double` to the buffer (aligns the data and grows if necessary). 340 /// </summary> 341 /// <param name="x">The `double` to add to the buffer.</param> AddDouble(double x)342 public void AddDouble(double x) { Prep(sizeof(double), 0); 343 PutDouble(x); } 344 345 /// <summary> 346 /// Adds an offset, relative to where it will be written. 347 /// </summary> 348 /// <param name="off">The offset to add to the buffer.</param> AddOffset(int off)349 public void AddOffset(int off) 350 { 351 Prep(sizeof(int), 0); // Ensure alignment is already done. 352 if (off > Offset) 353 throw new ArgumentException(); 354 355 off = Offset - off + sizeof(int); 356 PutInt(off); 357 } 358 359 /// @cond FLATBUFFERS_INTERNAL StartVector(int elemSize, int count, int alignment)360 public void StartVector(int elemSize, int count, int alignment) 361 { 362 NotNested(); 363 _vectorNumElems = count; 364 Prep(sizeof(int), elemSize * count); 365 Prep(alignment, elemSize * count); // Just in case alignment > int. 366 } 367 /// @endcond 368 369 /// <summary> 370 /// Writes data necessary to finish a vector construction. 371 /// </summary> EndVector()372 public VectorOffset EndVector() 373 { 374 PutInt(_vectorNumElems); 375 return new VectorOffset(Offset); 376 } 377 378 /// <summary> 379 /// Creates a vector of tables. 380 /// </summary> 381 /// <param name="offsets">Offsets of the tables.</param> 382 public VectorOffset CreateVectorOfTables<T>(Offset<T>[] offsets) where T : struct 383 { NotNestedFlatBuffers.FlatBufferBuilder.__anon5384 NotNested(); sizeofFlatBuffers.FlatBufferBuilder.__anon5385 StartVector(sizeof(int), offsets.Length, sizeof(int)); AddOffsetFlatBuffers.FlatBufferBuilder.__anon5386 for (int i = offsets.Length - 1; i >= 0; i--) AddOffset(offsets[i].Value); 387 return EndVector(); 388 } 389 390 /// @cond FLATBUFFERS_INTENRAL Nested(int obj)391 public void Nested(int obj) 392 { 393 // Structs are always stored inline, so need to be created right 394 // where they are used. You'll get this assert if you created it 395 // elsewhere. 396 if (obj != Offset) 397 throw new Exception( 398 "FlatBuffers: struct must be serialized inline."); 399 } 400 NotNested()401 public void NotNested() 402 { 403 // You should not be creating any other objects or strings/vectors 404 // while an object is being constructed 405 if (_vtableSize >= 0) 406 throw new Exception( 407 "FlatBuffers: object serialization must not be nested."); 408 } 409 StartObject(int numfields)410 public void StartObject(int numfields) 411 { 412 if (numfields < 0) 413 throw new ArgumentOutOfRangeException("Flatbuffers: invalid numfields"); 414 415 NotNested(); 416 417 if (_vtable.Length < numfields) 418 _vtable = new int[numfields]; 419 420 _vtableSize = numfields; 421 _objectStart = Offset; 422 } 423 424 425 // Set the current vtable at `voffset` to the current location in the 426 // buffer. Slot(int voffset)427 public void Slot(int voffset) 428 { 429 if (voffset >= _vtableSize) 430 throw new IndexOutOfRangeException("Flatbuffers: invalid voffset"); 431 432 _vtable[voffset] = Offset; 433 } 434 435 /// <summary> 436 /// Adds a Boolean to the Table at index `o` in its vtable using the value `x` and default `d` 437 /// </summary> 438 /// <param name="o">The index into the vtable</param> 439 /// <param name="x">The value to put into the buffer. If the value is equal to the default 440 /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param> 441 /// <param name="d">The default value to compare the value against</param> AddBool(int o, bool x, bool d)442 public void AddBool(int o, bool x, bool d) { if (ForceDefaults || x != d) { AddBool(x); Slot(o); } } 443 444 /// <summary> 445 /// Adds a SByte to the Table at index `o` in its vtable using the value `x` and default `d` 446 /// </summary> 447 /// <param name="o">The index into the vtable</param> 448 /// <param name="x">The value to put into the buffer. If the value is equal to the default 449 /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param> 450 /// <param name="d">The default value to compare the value against</param> AddSbyte(int o, sbyte x, sbyte d)451 public void AddSbyte(int o, sbyte x, sbyte d) { if (ForceDefaults || x != d) { AddSbyte(x); Slot(o); } } 452 453 /// <summary> 454 /// Adds a Byte to the Table at index `o` in its vtable using the value `x` and default `d` 455 /// </summary> 456 /// <param name="o">The index into the vtable</param> 457 /// <param name="x">The value to put into the buffer. If the value is equal to the default 458 /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param> 459 /// <param name="d">The default value to compare the value against</param> AddByte(int o, byte x, byte d)460 public void AddByte(int o, byte x, byte d) { if (ForceDefaults || x != d) { AddByte(x); Slot(o); } } 461 462 /// <summary> 463 /// Adds a Int16 to the Table at index `o` in its vtable using the value `x` and default `d` 464 /// </summary> 465 /// <param name="o">The index into the vtable</param> 466 /// <param name="x">The value to put into the buffer. If the value is equal to the default 467 /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param> 468 /// <param name="d">The default value to compare the value against</param> AddShort(int o, short x, int d)469 public void AddShort(int o, short x, int d) { if (ForceDefaults || x != d) { AddShort(x); Slot(o); } } 470 471 /// <summary> 472 /// Adds a UInt16 to the Table at index `o` in its vtable using the value `x` and default `d` 473 /// </summary> 474 /// <param name="o">The index into the vtable</param> 475 /// <param name="x">The value to put into the buffer. If the value is equal to the default 476 /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param> 477 /// <param name="d">The default value to compare the value against</param> AddUshort(int o, ushort x, ushort d)478 public void AddUshort(int o, ushort x, ushort d) { if (ForceDefaults || x != d) { AddUshort(x); Slot(o); } } 479 480 /// <summary> 481 /// Adds an Int32 to the Table at index `o` in its vtable using the value `x` and default `d` 482 /// </summary> 483 /// <param name="o">The index into the vtable</param> 484 /// <param name="x">The value to put into the buffer. If the value is equal to the default 485 /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param> 486 /// <param name="d">The default value to compare the value against</param> AddInt(int o, int x, int d)487 public void AddInt(int o, int x, int d) { if (ForceDefaults || x != d) { AddInt(x); Slot(o); } } 488 489 /// <summary> 490 /// Adds a UInt32 to the Table at index `o` in its vtable using the value `x` and default `d` 491 /// </summary> 492 /// <param name="o">The index into the vtable</param> 493 /// <param name="x">The value to put into the buffer. If the value is equal to the default 494 /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param> 495 /// <param name="d">The default value to compare the value against</param> AddUint(int o, uint x, uint d)496 public void AddUint(int o, uint x, uint d) { if (ForceDefaults || x != d) { AddUint(x); Slot(o); } } 497 498 /// <summary> 499 /// Adds an Int64 to the Table at index `o` in its vtable using the value `x` and default `d` 500 /// </summary> 501 /// <param name="o">The index into the vtable</param> 502 /// <param name="x">The value to put into the buffer. If the value is equal to the default 503 /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param> 504 /// <param name="d">The default value to compare the value against</param> AddLong(int o, long x, long d)505 public void AddLong(int o, long x, long d) { if (ForceDefaults || x != d) { AddLong(x); Slot(o); } } 506 507 /// <summary> 508 /// Adds a UInt64 to the Table at index `o` in its vtable using the value `x` and default `d` 509 /// </summary> 510 /// <param name="o">The index into the vtable</param> 511 /// <param name="x">The value to put into the buffer. If the value is equal to the default 512 /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param> 513 /// <param name="d">The default value to compare the value against</param> AddUlong(int o, ulong x, ulong d)514 public void AddUlong(int o, ulong x, ulong d) { if (ForceDefaults || x != d) { AddUlong(x); Slot(o); } } 515 516 /// <summary> 517 /// Adds a Single to the Table at index `o` in its vtable using the value `x` and default `d` 518 /// </summary> 519 /// <param name="o">The index into the vtable</param> 520 /// <param name="x">The value to put into the buffer. If the value is equal to the default 521 /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param> 522 /// <param name="d">The default value to compare the value against</param> AddFloat(int o, float x, double d)523 public void AddFloat(int o, float x, double d) { if (ForceDefaults || x != d) { AddFloat(x); Slot(o); } } 524 525 /// <summary> 526 /// Adds a Double to the Table at index `o` in its vtable using the value `x` and default `d` 527 /// </summary> 528 /// <param name="o">The index into the vtable</param> 529 /// <param name="x">The value to put into the buffer. If the value is equal to the default 530 /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param> 531 /// <param name="d">The default value to compare the value against</param> AddDouble(int o, double x, double d)532 public void AddDouble(int o, double x, double d) { if (ForceDefaults || x != d) { AddDouble(x); Slot(o); } } 533 534 /// <summary> 535 /// Adds a buffer offset to the Table at index `o` in its vtable using the value `x` and default `d` 536 /// </summary> 537 /// <param name="o">The index into the vtable</param> 538 /// <param name="x">The value to put into the buffer. If the value is equal to the default 539 /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param> 540 /// <param name="d">The default value to compare the value against</param> AddOffset(int o, int x, int d)541 public void AddOffset(int o, int x, int d) { if (ForceDefaults || x != d) { AddOffset(x); Slot(o); } } 542 /// @endcond 543 544 /// <summary> 545 /// Encode the string `s` in the buffer using UTF-8. 546 /// </summary> 547 /// <param name="s">The string to encode.</param> 548 /// <returns> 549 /// The offset in the buffer where the encoded string starts. 550 /// </returns> CreateString(string s)551 public StringOffset CreateString(string s) 552 { 553 NotNested(); 554 AddByte(0); 555 var utf8StringLen = Encoding.UTF8.GetByteCount(s); 556 StartVector(1, utf8StringLen, 1); 557 _bb.PutStringUTF8(_space -= utf8StringLen, s); 558 return new StringOffset(EndVector().Value); 559 } 560 561 562 #if ENABLE_SPAN_T 563 /// <summary> 564 /// Creates a string in the buffer from a Span containing 565 /// a UTF8 string. 566 /// </summary> 567 /// <param name="chars">the UTF8 string to add to the buffer</param> 568 /// <returns> 569 /// The offset in the buffer where the encoded string starts. 570 /// </returns> CreateUTF8String(Span<byte> chars)571 public StringOffset CreateUTF8String(Span<byte> chars) 572 { 573 NotNested(); 574 AddByte(0); 575 var utf8StringLen = chars.Length; 576 StartVector(1, utf8StringLen, 1); 577 _space = _bb.Put(_space, chars); 578 return new StringOffset(EndVector().Value); 579 } 580 #endif 581 582 /// @cond FLATBUFFERS_INTERNAL 583 // Structs are stored inline, so nothing additional is being added. 584 // `d` is always 0. AddStruct(int voffset, int x, int d)585 public void AddStruct(int voffset, int x, int d) 586 { 587 if (x != d) 588 { 589 Nested(x); 590 Slot(voffset); 591 } 592 } 593 EndObject()594 public int EndObject() 595 { 596 if (_vtableSize < 0) 597 throw new InvalidOperationException( 598 "Flatbuffers: calling endObject without a startObject"); 599 600 AddInt((int)0); 601 var vtableloc = Offset; 602 // Write out the current vtable. 603 int i = _vtableSize - 1; 604 // Trim trailing zeroes. 605 for (; i >= 0 && _vtable[i] == 0; i--) {} 606 int trimmedSize = i + 1; 607 for (; i >= 0 ; i--) { 608 // Offset relative to the start of the table. 609 short off = (short)(_vtable[i] != 0 610 ? vtableloc - _vtable[i] 611 : 0); 612 AddShort(off); 613 614 // clear out written entry 615 _vtable[i] = 0; 616 } 617 618 const int standardFields = 2; // The fields below: 619 AddShort((short)(vtableloc - _objectStart)); 620 AddShort((short)((trimmedSize + standardFields) * 621 sizeof(short))); 622 623 // Search for an existing vtable that matches the current one. 624 int existingVtable = 0; 625 for (i = 0; i < _numVtables; i++) { 626 int vt1 = _bb.Length - _vtables[i]; 627 int vt2 = _space; 628 short len = _bb.GetShort(vt1); 629 if (len == _bb.GetShort(vt2)) { 630 for (int j = sizeof(short); j < len; j += sizeof(short)) { 631 if (_bb.GetShort(vt1 + j) != _bb.GetShort(vt2 + j)) { 632 goto endLoop; 633 } 634 } 635 existingVtable = _vtables[i]; 636 break; 637 } 638 639 endLoop: { } 640 } 641 642 if (existingVtable != 0) { 643 // Found a match: 644 // Remove the current vtable. 645 _space = _bb.Length - vtableloc; 646 // Point table to existing vtable. 647 _bb.PutInt(_space, existingVtable - vtableloc); 648 } else { 649 // No match: 650 // Add the location of the current vtable to the list of 651 // vtables. 652 if (_numVtables == _vtables.Length) 653 { 654 // Arrays.CopyOf(vtables num_vtables * 2); 655 var newvtables = new int[ _numVtables * 2]; 656 Array.Copy(_vtables, newvtables, _vtables.Length); 657 658 _vtables = newvtables; 659 }; 660 _vtables[_numVtables++] = Offset; 661 // Point table to current vtable. 662 _bb.PutInt(_bb.Length - vtableloc, Offset - vtableloc); 663 } 664 665 _vtableSize = -1; 666 return vtableloc; 667 } 668 669 // This checks a required field has been set in a given table that has 670 // just been constructed. Required(int table, int field)671 public void Required(int table, int field) 672 { 673 int table_start = _bb.Length - table; 674 int vtable_start = table_start - _bb.GetInt(table_start); 675 bool ok = _bb.GetShort(vtable_start + field) != 0; 676 // If this fails, the caller will show what field needs to be set. 677 if (!ok) 678 throw new InvalidOperationException("FlatBuffers: field " + field + 679 " must be set"); 680 } 681 /// @endcond 682 683 /// <summary> 684 /// Finalize a buffer, pointing to the given `root_table`. 685 /// </summary> 686 /// <param name="rootTable"> 687 /// An offset to be added to the buffer. 688 /// </param> 689 /// <param name="sizePrefix"> 690 /// Whether to prefix the size to the buffer. 691 /// </param> Finish(int rootTable, bool sizePrefix)692 protected void Finish(int rootTable, bool sizePrefix) 693 { 694 Prep(_minAlign, sizeof(int) + (sizePrefix ? sizeof(int) : 0)); 695 AddOffset(rootTable); 696 if (sizePrefix) { 697 AddInt(_bb.Length - _space); 698 } 699 _bb.Position = _space; 700 } 701 702 /// <summary> 703 /// Finalize a buffer, pointing to the given `root_table`. 704 /// </summary> 705 /// <param name="rootTable"> 706 /// An offset to be added to the buffer. 707 /// </param> Finish(int rootTable)708 public void Finish(int rootTable) 709 { 710 Finish(rootTable, false); 711 } 712 713 /// <summary> 714 /// Finalize a buffer, pointing to the given `root_table`, with the size prefixed. 715 /// </summary> 716 /// <param name="rootTable"> 717 /// An offset to be added to the buffer. 718 /// </param> FinishSizePrefixed(int rootTable)719 public void FinishSizePrefixed(int rootTable) 720 { 721 Finish(rootTable, true); 722 } 723 724 /// <summary> 725 /// Get the ByteBuffer representing the FlatBuffer. 726 /// </summary> 727 /// <remarks> 728 /// This is typically only called after you call `Finish()`. 729 /// The actual data starts at the ByteBuffer's current position, 730 /// not necessarily at `0`. 731 /// </remarks> 732 /// <returns> 733 /// Returns the ByteBuffer for this FlatBuffer. 734 /// </returns> 735 public ByteBuffer DataBuffer { get { return _bb; } } 736 737 /// <summary> 738 /// A utility function to copy and return the ByteBuffer data as a 739 /// `byte[]`. 740 /// </summary> 741 /// <returns> 742 /// A full copy of the FlatBuffer data. 743 /// </returns> SizedByteArray()744 public byte[] SizedByteArray() 745 { 746 return _bb.ToSizedArray(); 747 } 748 749 /// <summary> 750 /// Finalize a buffer, pointing to the given `rootTable`. 751 /// </summary> 752 /// <param name="rootTable"> 753 /// An offset to be added to the buffer. 754 /// </param> 755 /// <param name="fileIdentifier"> 756 /// A FlatBuffer file identifier to be added to the buffer before 757 /// `root_table`. 758 /// </param> 759 /// <param name="sizePrefix"> 760 /// Whether to prefix the size to the buffer. 761 /// </param> Finish(int rootTable, string fileIdentifier, bool sizePrefix)762 protected void Finish(int rootTable, string fileIdentifier, bool sizePrefix) 763 { 764 Prep(_minAlign, sizeof(int) + (sizePrefix ? sizeof(int) : 0) + 765 FlatBufferConstants.FileIdentifierLength); 766 if (fileIdentifier.Length != 767 FlatBufferConstants.FileIdentifierLength) 768 throw new ArgumentException( 769 "FlatBuffers: file identifier must be length " + 770 FlatBufferConstants.FileIdentifierLength, 771 "fileIdentifier"); 772 for (int i = FlatBufferConstants.FileIdentifierLength - 1; i >= 0; 773 i--) 774 { 775 AddByte((byte)fileIdentifier[i]); 776 } 777 Finish(rootTable, sizePrefix); 778 } 779 780 /// <summary> 781 /// Finalize a buffer, pointing to the given `rootTable`. 782 /// </summary> 783 /// <param name="rootTable"> 784 /// An offset to be added to the buffer. 785 /// </param> 786 /// <param name="fileIdentifier"> 787 /// A FlatBuffer file identifier to be added to the buffer before 788 /// `root_table`. 789 /// </param> Finish(int rootTable, string fileIdentifier)790 public void Finish(int rootTable, string fileIdentifier) 791 { 792 Finish(rootTable, fileIdentifier, false); 793 } 794 795 /// <summary> 796 /// Finalize a buffer, pointing to the given `rootTable`, with the size prefixed. 797 /// </summary> 798 /// <param name="rootTable"> 799 /// An offset to be added to the buffer. 800 /// </param> 801 /// <param name="fileIdentifier"> 802 /// A FlatBuffer file identifier to be added to the buffer before 803 /// `root_table`. 804 /// </param> FinishSizePrefixed(int rootTable, string fileIdentifier)805 public void FinishSizePrefixed(int rootTable, string fileIdentifier) 806 { 807 Finish(rootTable, fileIdentifier, true); 808 } 809 } 810 } 811 812 /// @} 813