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(new byte[initialSize]); 63 } 64 65 /// <summary> 66 /// Reset the FlatBufferBuilder by purging all data that it holds. 67 /// </summary> Clear()68 public void Clear() 69 { 70 _space = _bb.Length; 71 _bb.Reset(); 72 _minAlign = 1; 73 while (_vtableSize > 0) _vtable[--_vtableSize] = 0; 74 _vtableSize = -1; 75 _objectStart = 0; 76 _numVtables = 0; 77 _vectorNumElems = 0; 78 } 79 80 /// <summary> 81 /// Gets and sets a Boolean to disable the optimization when serializing 82 /// default values to a Table. 83 /// 84 /// In order to save space, fields that are set to their default value 85 /// don't get serialized into the buffer. 86 /// </summary> 87 public bool ForceDefaults { get; set; } 88 89 /// @cond FLATBUFFERS_INTERNAL 90 91 public int Offset { get { return _bb.Length - _space; } } 92 Pad(int size)93 public void Pad(int size) 94 { 95 _bb.PutByte(_space -= size, 0, size); 96 } 97 98 // Doubles the size of the ByteBuffer, and copies the old data towards 99 // the end of the new buffer (since we build the buffer backwards). GrowBuffer()100 void GrowBuffer() 101 { 102 var oldBuf = _bb.Data; 103 var oldBufSize = oldBuf.Length; 104 if ((oldBufSize & 0xC0000000) != 0) 105 throw new Exception( 106 "FlatBuffers: cannot grow buffer beyond 2 gigabytes."); 107 108 var newBufSize = oldBufSize << 1; 109 var newBuf = new byte[newBufSize]; 110 111 Buffer.BlockCopy(oldBuf, 0, newBuf, newBufSize - oldBufSize, 112 oldBufSize); 113 _bb = new ByteBuffer(newBuf, newBufSize); 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 PutDouble(double x)193 public void PutDouble(double x) 194 { 195 _bb.PutDouble(_space -= sizeof(double), x); 196 } 197 /// @endcond 198 199 /// <summary> 200 /// Add a `bool` to the buffer (aligns the data and grows if necessary). 201 /// </summary> 202 /// <param name="x">The `bool` to add to the buffer.</param> AddBool(bool x)203 public void AddBool(bool x) { Prep(sizeof(byte), 0); PutBool(x); } 204 205 /// <summary> 206 /// Add a `sbyte` to the buffer (aligns the data and grows if necessary). 207 /// </summary> 208 /// <param name="x">The `sbyte` to add to the buffer.</param> AddSbyte(sbyte x)209 public void AddSbyte(sbyte x) { Prep(sizeof(sbyte), 0); PutSbyte(x); } 210 211 /// <summary> 212 /// Add a `byte` to the buffer (aligns the data and grows if necessary). 213 /// </summary> 214 /// <param name="x">The `byte` to add to the buffer.</param> AddByte(byte x)215 public void AddByte(byte x) { Prep(sizeof(byte), 0); PutByte(x); } 216 217 /// <summary> 218 /// Add a `short` to the buffer (aligns the data and grows if necessary). 219 /// </summary> 220 /// <param name="x">The `short` to add to the buffer.</param> AddShort(short x)221 public void AddShort(short x) { Prep(sizeof(short), 0); PutShort(x); } 222 223 /// <summary> 224 /// Add an `ushort` to the buffer (aligns the data and grows if necessary). 225 /// </summary> 226 /// <param name="x">The `ushort` to add to the buffer.</param> AddUshort(ushort x)227 public void AddUshort(ushort x) { Prep(sizeof(ushort), 0); PutUshort(x); } 228 229 /// <summary> 230 /// Add an `int` to the buffer (aligns the data and grows if necessary). 231 /// </summary> 232 /// <param name="x">The `int` to add to the buffer.</param> AddInt(int x)233 public void AddInt(int x) { Prep(sizeof(int), 0); PutInt(x); } 234 235 /// <summary> 236 /// Add an `uint` to the buffer (aligns the data and grows if necessary). 237 /// </summary> 238 /// <param name="x">The `uint` to add to the buffer.</param> AddUint(uint x)239 public void AddUint(uint x) { Prep(sizeof(uint), 0); PutUint(x); } 240 241 /// <summary> 242 /// Add a `long` to the buffer (aligns the data and grows if necessary). 243 /// </summary> 244 /// <param name="x">The `long` to add to the buffer.</param> AddLong(long x)245 public void AddLong(long x) { Prep(sizeof(long), 0); PutLong(x); } 246 247 /// <summary> 248 /// Add an `ulong` to the buffer (aligns the data and grows if necessary). 249 /// </summary> 250 /// <param name="x">The `ulong` to add to the buffer.</param> AddUlong(ulong x)251 public void AddUlong(ulong x) { Prep(sizeof(ulong), 0); PutUlong(x); } 252 253 /// <summary> 254 /// Add a `float` to the buffer (aligns the data and grows if necessary). 255 /// </summary> 256 /// <param name="x">The `float` to add to the buffer.</param> AddFloat(float x)257 public void AddFloat(float x) { Prep(sizeof(float), 0); PutFloat(x); } 258 259 /// <summary> 260 /// Add a `double` to the buffer (aligns the data and grows if necessary). 261 /// </summary> 262 /// <param name="x">The `double` to add to the buffer.</param> AddDouble(double x)263 public void AddDouble(double x) { Prep(sizeof(double), 0); 264 PutDouble(x); } 265 266 /// <summary> 267 /// Adds an offset, relative to where it will be written. 268 /// </summary> 269 /// <param name="off">The offset to add to the buffer.</param> AddOffset(int off)270 public void AddOffset(int off) 271 { 272 Prep(sizeof(int), 0); // Ensure alignment is already done. 273 if (off > Offset) 274 throw new ArgumentException(); 275 276 off = Offset - off + sizeof(int); 277 PutInt(off); 278 } 279 280 /// @cond FLATBUFFERS_INTERNAL StartVector(int elemSize, int count, int alignment)281 public void StartVector(int elemSize, int count, int alignment) 282 { 283 NotNested(); 284 _vectorNumElems = count; 285 Prep(sizeof(int), elemSize * count); 286 Prep(alignment, elemSize * count); // Just in case alignment > int. 287 } 288 /// @endcond 289 290 /// <summary> 291 /// Writes data necessary to finish a vector construction. 292 /// </summary> EndVector()293 public VectorOffset EndVector() 294 { 295 PutInt(_vectorNumElems); 296 return new VectorOffset(Offset); 297 } 298 299 /// <summary> 300 /// Creates a vector of tables. 301 /// </summary> 302 /// <param name="offsets">Offsets of the tables.</param> 303 public VectorOffset CreateVectorOfTables<T>(Offset<T>[] offsets) where T : struct 304 { NotNestedFlatBuffers.FlatBufferBuilder.__anon1305 NotNested(); sizeofFlatBuffers.FlatBufferBuilder.__anon1306 StartVector(sizeof(int), offsets.Length, sizeof(int)); AddOffsetFlatBuffers.FlatBufferBuilder.__anon1307 for (int i = offsets.Length - 1; i >= 0; i--) AddOffset(offsets[i].Value); 308 return EndVector(); 309 } 310 311 /// @cond FLATBUFFERS_INTENRAL Nested(int obj)312 public void Nested(int obj) 313 { 314 // Structs are always stored inline, so need to be created right 315 // where they are used. You'll get this assert if you created it 316 // elsewhere. 317 if (obj != Offset) 318 throw new Exception( 319 "FlatBuffers: struct must be serialized inline."); 320 } 321 NotNested()322 public void NotNested() 323 { 324 // You should not be creating any other objects or strings/vectors 325 // while an object is being constructed 326 if (_vtableSize >= 0) 327 throw new Exception( 328 "FlatBuffers: object serialization must not be nested."); 329 } 330 StartObject(int numfields)331 public void StartObject(int numfields) 332 { 333 if (numfields < 0) 334 throw new ArgumentOutOfRangeException("Flatbuffers: invalid numfields"); 335 336 NotNested(); 337 338 if (_vtable.Length < numfields) 339 _vtable = new int[numfields]; 340 341 _vtableSize = numfields; 342 _objectStart = Offset; 343 } 344 345 346 // Set the current vtable at `voffset` to the current location in the 347 // buffer. Slot(int voffset)348 public void Slot(int voffset) 349 { 350 if (voffset >= _vtableSize) 351 throw new IndexOutOfRangeException("Flatbuffers: invalid voffset"); 352 353 _vtable[voffset] = Offset; 354 } 355 356 /// <summary> 357 /// Adds a Boolean to the Table at index `o` in its vtable using the value `x` and default `d` 358 /// </summary> 359 /// <param name="o">The index into the vtable</param> 360 /// <param name="x">The value to put into the buffer. If the value is equal to the default 361 /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param> 362 /// <param name="d">The default value to compare the value against</param> AddBool(int o, bool x, bool d)363 public void AddBool(int o, bool x, bool d) { if (ForceDefaults || x != d) { AddBool(x); Slot(o); } } 364 365 /// <summary> 366 /// Adds a SByte to the Table at index `o` in its vtable using the value `x` and default `d` 367 /// </summary> 368 /// <param name="o">The index into the vtable</param> 369 /// <param name="x">The value to put into the buffer. If the value is equal to the default 370 /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param> 371 /// <param name="d">The default value to compare the value against</param> AddSbyte(int o, sbyte x, sbyte d)372 public void AddSbyte(int o, sbyte x, sbyte d) { if (ForceDefaults || x != d) { AddSbyte(x); Slot(o); } } 373 374 /// <summary> 375 /// Adds a Byte to the Table at index `o` in its vtable using the value `x` and default `d` 376 /// </summary> 377 /// <param name="o">The index into the vtable</param> 378 /// <param name="x">The value to put into the buffer. If the value is equal to the default 379 /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param> 380 /// <param name="d">The default value to compare the value against</param> AddByte(int o, byte x, byte d)381 public void AddByte(int o, byte x, byte d) { if (ForceDefaults || x != d) { AddByte(x); Slot(o); } } 382 383 /// <summary> 384 /// Adds a Int16 to the Table at index `o` in its vtable using the value `x` and default `d` 385 /// </summary> 386 /// <param name="o">The index into the vtable</param> 387 /// <param name="x">The value to put into the buffer. If the value is equal to the default 388 /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param> 389 /// <param name="d">The default value to compare the value against</param> AddShort(int o, short x, int d)390 public void AddShort(int o, short x, int d) { if (ForceDefaults || x != d) { AddShort(x); Slot(o); } } 391 392 /// <summary> 393 /// Adds a UInt16 to the Table at index `o` in its vtable using the value `x` and default `d` 394 /// </summary> 395 /// <param name="o">The index into the vtable</param> 396 /// <param name="x">The value to put into the buffer. If the value is equal to the default 397 /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param> 398 /// <param name="d">The default value to compare the value against</param> AddUshort(int o, ushort x, ushort d)399 public void AddUshort(int o, ushort x, ushort d) { if (ForceDefaults || x != d) { AddUshort(x); Slot(o); } } 400 401 /// <summary> 402 /// Adds an Int32 to the Table at index `o` in its vtable using the value `x` and default `d` 403 /// </summary> 404 /// <param name="o">The index into the vtable</param> 405 /// <param name="x">The value to put into the buffer. If the value is equal to the default 406 /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param> 407 /// <param name="d">The default value to compare the value against</param> AddInt(int o, int x, int d)408 public void AddInt(int o, int x, int d) { if (ForceDefaults || x != d) { AddInt(x); Slot(o); } } 409 410 /// <summary> 411 /// Adds a UInt32 to the Table at index `o` in its vtable using the value `x` and default `d` 412 /// </summary> 413 /// <param name="o">The index into the vtable</param> 414 /// <param name="x">The value to put into the buffer. If the value is equal to the default 415 /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param> 416 /// <param name="d">The default value to compare the value against</param> AddUint(int o, uint x, uint d)417 public void AddUint(int o, uint x, uint d) { if (ForceDefaults || x != d) { AddUint(x); Slot(o); } } 418 419 /// <summary> 420 /// Adds an Int64 to the Table at index `o` in its vtable using the value `x` and default `d` 421 /// </summary> 422 /// <param name="o">The index into the vtable</param> 423 /// <param name="x">The value to put into the buffer. If the value is equal to the default 424 /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param> 425 /// <param name="d">The default value to compare the value against</param> AddLong(int o, long x, long d)426 public void AddLong(int o, long x, long d) { if (ForceDefaults || x != d) { AddLong(x); Slot(o); } } 427 428 /// <summary> 429 /// Adds a UInt64 to the Table at index `o` in its vtable using the value `x` and default `d` 430 /// </summary> 431 /// <param name="o">The index into the vtable</param> 432 /// <param name="x">The value to put into the buffer. If the value is equal to the default 433 /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param> 434 /// <param name="d">The default value to compare the value against</param> AddUlong(int o, ulong x, ulong d)435 public void AddUlong(int o, ulong x, ulong d) { if (ForceDefaults || x != d) { AddUlong(x); Slot(o); } } 436 437 /// <summary> 438 /// Adds a Single to the Table at index `o` in its vtable using the value `x` and default `d` 439 /// </summary> 440 /// <param name="o">The index into the vtable</param> 441 /// <param name="x">The value to put into the buffer. If the value is equal to the default 442 /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param> 443 /// <param name="d">The default value to compare the value against</param> AddFloat(int o, float x, double d)444 public void AddFloat(int o, float x, double d) { if (ForceDefaults || x != d) { AddFloat(x); Slot(o); } } 445 446 /// <summary> 447 /// Adds a Double to the Table at index `o` in its vtable using the value `x` and default `d` 448 /// </summary> 449 /// <param name="o">The index into the vtable</param> 450 /// <param name="x">The value to put into the buffer. If the value is equal to the default 451 /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param> 452 /// <param name="d">The default value to compare the value against</param> AddDouble(int o, double x, double d)453 public void AddDouble(int o, double x, double d) { if (ForceDefaults || x != d) { AddDouble(x); Slot(o); } } 454 455 /// <summary> 456 /// Adds a buffer offset to the Table at index `o` in its vtable using the value `x` and default `d` 457 /// </summary> 458 /// <param name="o">The index into the vtable</param> 459 /// <param name="x">The value to put into the buffer. If the value is equal to the default 460 /// and <see cref="ForceDefaults"/> is false, the value will be skipped.</param> 461 /// <param name="d">The default value to compare the value against</param> AddOffset(int o, int x, int d)462 public void AddOffset(int o, int x, int d) { if (ForceDefaults || x != d) { AddOffset(x); Slot(o); } } 463 /// @endcond 464 465 /// <summary> 466 /// Encode the string `s` in the buffer using UTF-8. 467 /// </summary> 468 /// <param name="s">The string to encode.</param> 469 /// <returns> 470 /// The offset in the buffer where the encoded string starts. 471 /// </returns> CreateString(string s)472 public StringOffset CreateString(string s) 473 { 474 NotNested(); 475 AddByte(0); 476 var utf8StringLen = Encoding.UTF8.GetByteCount(s); 477 StartVector(1, utf8StringLen, 1); 478 Encoding.UTF8.GetBytes(s, 0, s.Length, _bb.Data, _space -= utf8StringLen); 479 return new StringOffset(EndVector().Value); 480 } 481 482 /// @cond FLATBUFFERS_INTERNAL 483 // Structs are stored inline, so nothing additional is being added. 484 // `d` is always 0. AddStruct(int voffset, int x, int d)485 public void AddStruct(int voffset, int x, int d) 486 { 487 if (x != d) 488 { 489 Nested(x); 490 Slot(voffset); 491 } 492 } 493 EndObject()494 public int EndObject() 495 { 496 if (_vtableSize < 0) 497 throw new InvalidOperationException( 498 "Flatbuffers: calling endObject without a startObject"); 499 500 AddInt((int)0); 501 var vtableloc = Offset; 502 // Write out the current vtable. 503 int i = _vtableSize - 1; 504 // Trim trailing zeroes. 505 for (; i >= 0 && _vtable[i] == 0; i--) {} 506 int trimmedSize = i + 1; 507 for (; i >= 0 ; i--) { 508 // Offset relative to the start of the table. 509 short off = (short)(_vtable[i] != 0 510 ? vtableloc - _vtable[i] 511 : 0); 512 AddShort(off); 513 514 // clear out written entry 515 _vtable[i] = 0; 516 } 517 518 const int standardFields = 2; // The fields below: 519 AddShort((short)(vtableloc - _objectStart)); 520 AddShort((short)((trimmedSize + standardFields) * 521 sizeof(short))); 522 523 // Search for an existing vtable that matches the current one. 524 int existingVtable = 0; 525 for (i = 0; i < _numVtables; i++) { 526 int vt1 = _bb.Length - _vtables[i]; 527 int vt2 = _space; 528 short len = _bb.GetShort(vt1); 529 if (len == _bb.GetShort(vt2)) { 530 for (int j = sizeof(short); j < len; j += sizeof(short)) { 531 if (_bb.GetShort(vt1 + j) != _bb.GetShort(vt2 + j)) { 532 goto endLoop; 533 } 534 } 535 existingVtable = _vtables[i]; 536 break; 537 } 538 539 endLoop: { } 540 } 541 542 if (existingVtable != 0) { 543 // Found a match: 544 // Remove the current vtable. 545 _space = _bb.Length - vtableloc; 546 // Point table to existing vtable. 547 _bb.PutInt(_space, existingVtable - vtableloc); 548 } else { 549 // No match: 550 // Add the location of the current vtable to the list of 551 // vtables. 552 if (_numVtables == _vtables.Length) 553 { 554 // Arrays.CopyOf(vtables num_vtables * 2); 555 var newvtables = new int[ _numVtables * 2]; 556 Array.Copy(_vtables, newvtables, _vtables.Length); 557 558 _vtables = newvtables; 559 }; 560 _vtables[_numVtables++] = Offset; 561 // Point table to current vtable. 562 _bb.PutInt(_bb.Length - vtableloc, Offset - vtableloc); 563 } 564 565 _vtableSize = -1; 566 return vtableloc; 567 } 568 569 // This checks a required field has been set in a given table that has 570 // just been constructed. Required(int table, int field)571 public void Required(int table, int field) 572 { 573 int table_start = _bb.Length - table; 574 int vtable_start = table_start - _bb.GetInt(table_start); 575 bool ok = _bb.GetShort(vtable_start + field) != 0; 576 // If this fails, the caller will show what field needs to be set. 577 if (!ok) 578 throw new InvalidOperationException("FlatBuffers: field " + field + 579 " must be set"); 580 } 581 /// @endcond 582 583 /// <summary> 584 /// Finalize a buffer, pointing to the given `root_table`. 585 /// </summary> 586 /// <param name="rootTable"> 587 /// An offset to be added to the buffer. 588 /// </param> Finish(int rootTable)589 public void Finish(int rootTable) 590 { 591 Prep(_minAlign, sizeof(int)); 592 AddOffset(rootTable); 593 _bb.Position = _space; 594 } 595 596 /// <summary> 597 /// Get the ByteBuffer representing the FlatBuffer. 598 /// </summary> 599 /// <remarks> 600 /// This is typically only called after you call `Finish()`. 601 /// The actual data starts at the ByteBuffer's current position, 602 /// not necessarily at `0`. 603 /// </remarks> 604 /// <returns> 605 /// Returns the ByteBuffer for this FlatBuffer. 606 /// </returns> 607 public ByteBuffer DataBuffer { get { return _bb; } } 608 609 /// <summary> 610 /// A utility function to copy and return the ByteBuffer data as a 611 /// `byte[]`. 612 /// </summary> 613 /// <returns> 614 /// A full copy of the FlatBuffer data. 615 /// </returns> SizedByteArray()616 public byte[] SizedByteArray() 617 { 618 var newArray = new byte[_bb.Data.Length - _bb.Position]; 619 Buffer.BlockCopy(_bb.Data, _bb.Position, newArray, 0, 620 _bb.Data.Length - _bb.Position); 621 return newArray; 622 } 623 624 /// <summary> 625 /// Finalize a buffer, pointing to the given `rootTable`. 626 /// </summary> 627 /// <param name="rootTable"> 628 /// An offset to be added to the buffer. 629 /// </param> 630 /// <param name="fileIdentifier"> 631 /// A FlatBuffer file identifier to be added to the buffer before 632 /// `root_table`. 633 /// </param> Finish(int rootTable, string fileIdentifier)634 public void Finish(int rootTable, string fileIdentifier) 635 { 636 Prep(_minAlign, sizeof(int) + 637 FlatBufferConstants.FileIdentifierLength); 638 if (fileIdentifier.Length != 639 FlatBufferConstants.FileIdentifierLength) 640 throw new ArgumentException( 641 "FlatBuffers: file identifier must be length " + 642 FlatBufferConstants.FileIdentifierLength, 643 "fileIdentifier"); 644 for (int i = FlatBufferConstants.FileIdentifierLength - 1; i >= 0; 645 i--) 646 { 647 AddByte((byte)fileIdentifier[i]); 648 } 649 Finish(rootTable); 650 } 651 652 653 } 654 } 655 656 /// @} 657