Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 1 | package flatbuffers |
| 2 | |
| 3 | // Builder is a state machine for creating FlatBuffer objects. |
| 4 | // Use a Builder to construct object(s) starting from leaf nodes. |
| 5 | // |
| 6 | // A Builder constructs byte buffers in a last-first manner for simplicity and |
| 7 | // performance. |
| 8 | type Builder struct { |
| 9 | // `Bytes` gives raw access to the buffer. Most users will want to use |
| 10 | // FinishedBytes() instead. |
| 11 | Bytes []byte |
| 12 | |
| 13 | minalign int |
| 14 | vtable []UOffsetT |
| 15 | objectEnd UOffsetT |
| 16 | vtables []UOffsetT |
| 17 | head UOffsetT |
| 18 | nested bool |
| 19 | finished bool |
Austin Schuh | 272c613 | 2020-11-14 16:37:52 -0800 | [diff] [blame^] | 20 | |
| 21 | sharedStrings map[string]UOffsetT |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 22 | } |
| 23 | |
| 24 | const fileIdentifierLength = 4 |
Austin Schuh | 272c613 | 2020-11-14 16:37:52 -0800 | [diff] [blame^] | 25 | const sizePrefixLength = 4 |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 26 | |
| 27 | // NewBuilder initializes a Builder of size `initial_size`. |
| 28 | // The internal buffer is grown as needed. |
| 29 | func NewBuilder(initialSize int) *Builder { |
| 30 | if initialSize <= 0 { |
| 31 | initialSize = 0 |
| 32 | } |
| 33 | |
| 34 | b := &Builder{} |
| 35 | b.Bytes = make([]byte, initialSize) |
| 36 | b.head = UOffsetT(initialSize) |
| 37 | b.minalign = 1 |
| 38 | b.vtables = make([]UOffsetT, 0, 16) // sensible default capacity |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 39 | return b |
| 40 | } |
| 41 | |
| 42 | // Reset truncates the underlying Builder buffer, facilitating alloc-free |
| 43 | // reuse of a Builder. It also resets bookkeeping data. |
| 44 | func (b *Builder) Reset() { |
| 45 | if b.Bytes != nil { |
| 46 | b.Bytes = b.Bytes[:cap(b.Bytes)] |
| 47 | } |
| 48 | |
| 49 | if b.vtables != nil { |
| 50 | b.vtables = b.vtables[:0] |
| 51 | } |
| 52 | |
| 53 | if b.vtable != nil { |
| 54 | b.vtable = b.vtable[:0] |
| 55 | } |
| 56 | |
Austin Schuh | 272c613 | 2020-11-14 16:37:52 -0800 | [diff] [blame^] | 57 | if b.sharedStrings != nil { |
| 58 | for key := range b.sharedStrings { |
| 59 | delete(b.sharedStrings, key) |
| 60 | } |
| 61 | } |
| 62 | |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 63 | b.head = UOffsetT(len(b.Bytes)) |
| 64 | b.minalign = 1 |
| 65 | b.nested = false |
| 66 | b.finished = false |
| 67 | } |
| 68 | |
| 69 | // FinishedBytes returns a pointer to the written data in the byte buffer. |
| 70 | // Panics if the builder is not in a finished state (which is caused by calling |
| 71 | // `Finish()`). |
| 72 | func (b *Builder) FinishedBytes() []byte { |
| 73 | b.assertFinished() |
| 74 | return b.Bytes[b.Head():] |
| 75 | } |
| 76 | |
| 77 | // StartObject initializes bookkeeping for writing a new object. |
| 78 | func (b *Builder) StartObject(numfields int) { |
| 79 | b.assertNotNested() |
| 80 | b.nested = true |
| 81 | |
| 82 | // use 32-bit offsets so that arithmetic doesn't overflow. |
| 83 | if cap(b.vtable) < numfields || b.vtable == nil { |
| 84 | b.vtable = make([]UOffsetT, numfields) |
| 85 | } else { |
| 86 | b.vtable = b.vtable[:numfields] |
| 87 | for i := 0; i < len(b.vtable); i++ { |
| 88 | b.vtable[i] = 0 |
| 89 | } |
| 90 | } |
| 91 | |
| 92 | b.objectEnd = b.Offset() |
| 93 | } |
| 94 | |
| 95 | // WriteVtable serializes the vtable for the current object, if applicable. |
| 96 | // |
| 97 | // Before writing out the vtable, this checks pre-existing vtables for equality |
| 98 | // to this one. If an equal vtable is found, point the object to the existing |
| 99 | // vtable and return. |
| 100 | // |
| 101 | // Because vtable values are sensitive to alignment of object data, not all |
| 102 | // logically-equal vtables will be deduplicated. |
| 103 | // |
| 104 | // A vtable has the following format: |
| 105 | // <VOffsetT: size of the vtable in bytes, including this value> |
| 106 | // <VOffsetT: size of the object in bytes, including the vtable offset> |
| 107 | // <VOffsetT: offset for a field> * N, where N is the number of fields in |
| 108 | // the schema for this type. Includes deprecated fields. |
| 109 | // Thus, a vtable is made of 2 + N elements, each SizeVOffsetT bytes wide. |
| 110 | // |
| 111 | // An object has the following format: |
| 112 | // <SOffsetT: offset to this object's vtable (may be negative)> |
| 113 | // <byte: data>+ |
| 114 | func (b *Builder) WriteVtable() (n UOffsetT) { |
| 115 | // Prepend a zero scalar to the object. Later in this function we'll |
| 116 | // write an offset here that points to the object's vtable: |
| 117 | b.PrependSOffsetT(0) |
| 118 | |
| 119 | objectOffset := b.Offset() |
| 120 | existingVtable := UOffsetT(0) |
| 121 | |
| 122 | // Trim vtable of trailing zeroes. |
| 123 | i := len(b.vtable) - 1 |
| 124 | for ; i >= 0 && b.vtable[i] == 0; i-- { |
| 125 | } |
| 126 | b.vtable = b.vtable[:i+1] |
| 127 | |
| 128 | // Search backwards through existing vtables, because similar vtables |
| 129 | // are likely to have been recently appended. See |
| 130 | // BenchmarkVtableDeduplication for a case in which this heuristic |
| 131 | // saves about 30% of the time used in writing objects with duplicate |
| 132 | // tables. |
| 133 | for i := len(b.vtables) - 1; i >= 0; i-- { |
| 134 | // Find the other vtable, which is associated with `i`: |
| 135 | vt2Offset := b.vtables[i] |
| 136 | vt2Start := len(b.Bytes) - int(vt2Offset) |
| 137 | vt2Len := GetVOffsetT(b.Bytes[vt2Start:]) |
| 138 | |
| 139 | metadata := VtableMetadataFields * SizeVOffsetT |
| 140 | vt2End := vt2Start + int(vt2Len) |
| 141 | vt2 := b.Bytes[vt2Start+metadata : vt2End] |
| 142 | |
| 143 | // Compare the other vtable to the one under consideration. |
| 144 | // If they are equal, store the offset and break: |
| 145 | if vtableEqual(b.vtable, objectOffset, vt2) { |
| 146 | existingVtable = vt2Offset |
| 147 | break |
| 148 | } |
| 149 | } |
| 150 | |
| 151 | if existingVtable == 0 { |
| 152 | // Did not find a vtable, so write this one to the buffer. |
| 153 | |
| 154 | // Write out the current vtable in reverse , because |
| 155 | // serialization occurs in last-first order: |
| 156 | for i := len(b.vtable) - 1; i >= 0; i-- { |
| 157 | var off UOffsetT |
| 158 | if b.vtable[i] != 0 { |
| 159 | // Forward reference to field; |
| 160 | // use 32bit number to assert no overflow: |
| 161 | off = objectOffset - b.vtable[i] |
| 162 | } |
| 163 | |
| 164 | b.PrependVOffsetT(VOffsetT(off)) |
| 165 | } |
| 166 | |
| 167 | // The two metadata fields are written last. |
| 168 | |
| 169 | // First, store the object bytesize: |
| 170 | objectSize := objectOffset - b.objectEnd |
| 171 | b.PrependVOffsetT(VOffsetT(objectSize)) |
| 172 | |
| 173 | // Second, store the vtable bytesize: |
| 174 | vBytes := (len(b.vtable) + VtableMetadataFields) * SizeVOffsetT |
| 175 | b.PrependVOffsetT(VOffsetT(vBytes)) |
| 176 | |
| 177 | // Next, write the offset to the new vtable in the |
| 178 | // already-allocated SOffsetT at the beginning of this object: |
| 179 | objectStart := SOffsetT(len(b.Bytes)) - SOffsetT(objectOffset) |
| 180 | WriteSOffsetT(b.Bytes[objectStart:], |
| 181 | SOffsetT(b.Offset())-SOffsetT(objectOffset)) |
| 182 | |
| 183 | // Finally, store this vtable in memory for future |
| 184 | // deduplication: |
| 185 | b.vtables = append(b.vtables, b.Offset()) |
| 186 | } else { |
| 187 | // Found a duplicate vtable. |
| 188 | |
| 189 | objectStart := SOffsetT(len(b.Bytes)) - SOffsetT(objectOffset) |
| 190 | b.head = UOffsetT(objectStart) |
| 191 | |
| 192 | // Write the offset to the found vtable in the |
| 193 | // already-allocated SOffsetT at the beginning of this object: |
| 194 | WriteSOffsetT(b.Bytes[b.head:], |
| 195 | SOffsetT(existingVtable)-SOffsetT(objectOffset)) |
| 196 | } |
| 197 | |
| 198 | b.vtable = b.vtable[:0] |
| 199 | return objectOffset |
| 200 | } |
| 201 | |
| 202 | // EndObject writes data necessary to finish object construction. |
| 203 | func (b *Builder) EndObject() UOffsetT { |
| 204 | b.assertNested() |
| 205 | n := b.WriteVtable() |
| 206 | b.nested = false |
| 207 | return n |
| 208 | } |
| 209 | |
| 210 | // Doubles the size of the byteslice, and copies the old data towards the |
| 211 | // end of the new byteslice (since we build the buffer backwards). |
| 212 | func (b *Builder) growByteBuffer() { |
| 213 | if (int64(len(b.Bytes)) & int64(0xC0000000)) != 0 { |
| 214 | panic("cannot grow buffer beyond 2 gigabytes") |
| 215 | } |
| 216 | newLen := len(b.Bytes) * 2 |
| 217 | if newLen == 0 { |
| 218 | newLen = 1 |
| 219 | } |
| 220 | |
| 221 | if cap(b.Bytes) >= newLen { |
| 222 | b.Bytes = b.Bytes[:newLen] |
| 223 | } else { |
| 224 | extension := make([]byte, newLen-len(b.Bytes)) |
| 225 | b.Bytes = append(b.Bytes, extension...) |
| 226 | } |
| 227 | |
| 228 | middle := newLen / 2 |
| 229 | copy(b.Bytes[middle:], b.Bytes[:middle]) |
| 230 | } |
| 231 | |
| 232 | // Head gives the start of useful data in the underlying byte buffer. |
| 233 | // Note: unlike other functions, this value is interpreted as from the left. |
| 234 | func (b *Builder) Head() UOffsetT { |
| 235 | return b.head |
| 236 | } |
| 237 | |
| 238 | // Offset relative to the end of the buffer. |
| 239 | func (b *Builder) Offset() UOffsetT { |
| 240 | return UOffsetT(len(b.Bytes)) - b.head |
| 241 | } |
| 242 | |
| 243 | // Pad places zeros at the current offset. |
| 244 | func (b *Builder) Pad(n int) { |
| 245 | for i := 0; i < n; i++ { |
| 246 | b.PlaceByte(0) |
| 247 | } |
| 248 | } |
| 249 | |
| 250 | // Prep prepares to write an element of `size` after `additional_bytes` |
| 251 | // have been written, e.g. if you write a string, you need to align such |
| 252 | // the int length field is aligned to SizeInt32, and the string data follows it |
| 253 | // directly. |
| 254 | // If all you need to do is align, `additionalBytes` will be 0. |
| 255 | func (b *Builder) Prep(size, additionalBytes int) { |
| 256 | // Track the biggest thing we've ever aligned to. |
| 257 | if size > b.minalign { |
| 258 | b.minalign = size |
| 259 | } |
| 260 | // Find the amount of alignment needed such that `size` is properly |
| 261 | // aligned after `additionalBytes`: |
| 262 | alignSize := (^(len(b.Bytes) - int(b.Head()) + additionalBytes)) + 1 |
| 263 | alignSize &= (size - 1) |
| 264 | |
| 265 | // Reallocate the buffer if needed: |
| 266 | for int(b.head) <= alignSize+size+additionalBytes { |
| 267 | oldBufSize := len(b.Bytes) |
| 268 | b.growByteBuffer() |
| 269 | b.head += UOffsetT(len(b.Bytes) - oldBufSize) |
| 270 | } |
| 271 | b.Pad(alignSize) |
| 272 | } |
| 273 | |
| 274 | // PrependSOffsetT prepends an SOffsetT, relative to where it will be written. |
| 275 | func (b *Builder) PrependSOffsetT(off SOffsetT) { |
| 276 | b.Prep(SizeSOffsetT, 0) // Ensure alignment is already done. |
| 277 | if !(UOffsetT(off) <= b.Offset()) { |
| 278 | panic("unreachable: off <= b.Offset()") |
| 279 | } |
| 280 | off2 := SOffsetT(b.Offset()) - off + SOffsetT(SizeSOffsetT) |
| 281 | b.PlaceSOffsetT(off2) |
| 282 | } |
| 283 | |
| 284 | // PrependUOffsetT prepends an UOffsetT, relative to where it will be written. |
| 285 | func (b *Builder) PrependUOffsetT(off UOffsetT) { |
| 286 | b.Prep(SizeUOffsetT, 0) // Ensure alignment is already done. |
| 287 | if !(off <= b.Offset()) { |
| 288 | panic("unreachable: off <= b.Offset()") |
| 289 | } |
| 290 | off2 := b.Offset() - off + UOffsetT(SizeUOffsetT) |
| 291 | b.PlaceUOffsetT(off2) |
| 292 | } |
| 293 | |
| 294 | // StartVector initializes bookkeeping for writing a new vector. |
| 295 | // |
| 296 | // A vector has the following format: |
| 297 | // <UOffsetT: number of elements in this vector> |
| 298 | // <T: data>+, where T is the type of elements of this vector. |
| 299 | func (b *Builder) StartVector(elemSize, numElems, alignment int) UOffsetT { |
| 300 | b.assertNotNested() |
| 301 | b.nested = true |
| 302 | b.Prep(SizeUint32, elemSize*numElems) |
| 303 | b.Prep(alignment, elemSize*numElems) // Just in case alignment > int. |
| 304 | return b.Offset() |
| 305 | } |
| 306 | |
| 307 | // EndVector writes data necessary to finish vector construction. |
| 308 | func (b *Builder) EndVector(vectorNumElems int) UOffsetT { |
| 309 | b.assertNested() |
| 310 | |
| 311 | // we already made space for this, so write without PrependUint32 |
| 312 | b.PlaceUOffsetT(UOffsetT(vectorNumElems)) |
| 313 | |
| 314 | b.nested = false |
| 315 | return b.Offset() |
| 316 | } |
| 317 | |
Austin Schuh | 272c613 | 2020-11-14 16:37:52 -0800 | [diff] [blame^] | 318 | // CreateSharedString Checks if the string is already written |
| 319 | // to the buffer before calling CreateString |
| 320 | func (b *Builder) CreateSharedString(s string) UOffsetT { |
| 321 | if b.sharedStrings == nil { |
| 322 | b.sharedStrings = make(map[string]UOffsetT) |
| 323 | } |
| 324 | if v, ok := b.sharedStrings[s]; ok { |
| 325 | return v |
| 326 | } |
| 327 | off := b.CreateString(s) |
| 328 | b.sharedStrings[s] = off |
| 329 | return off |
| 330 | } |
| 331 | |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 332 | // CreateString writes a null-terminated string as a vector. |
| 333 | func (b *Builder) CreateString(s string) UOffsetT { |
| 334 | b.assertNotNested() |
| 335 | b.nested = true |
| 336 | |
| 337 | b.Prep(int(SizeUOffsetT), (len(s)+1)*SizeByte) |
| 338 | b.PlaceByte(0) |
| 339 | |
| 340 | l := UOffsetT(len(s)) |
| 341 | |
| 342 | b.head -= l |
| 343 | copy(b.Bytes[b.head:b.head+l], s) |
| 344 | |
| 345 | return b.EndVector(len(s)) |
| 346 | } |
| 347 | |
| 348 | // CreateByteString writes a byte slice as a string (null-terminated). |
| 349 | func (b *Builder) CreateByteString(s []byte) UOffsetT { |
| 350 | b.assertNotNested() |
| 351 | b.nested = true |
| 352 | |
| 353 | b.Prep(int(SizeUOffsetT), (len(s)+1)*SizeByte) |
| 354 | b.PlaceByte(0) |
| 355 | |
| 356 | l := UOffsetT(len(s)) |
| 357 | |
| 358 | b.head -= l |
| 359 | copy(b.Bytes[b.head:b.head+l], s) |
| 360 | |
| 361 | return b.EndVector(len(s)) |
| 362 | } |
| 363 | |
| 364 | // CreateByteVector writes a ubyte vector |
| 365 | func (b *Builder) CreateByteVector(v []byte) UOffsetT { |
| 366 | b.assertNotNested() |
| 367 | b.nested = true |
| 368 | |
| 369 | b.Prep(int(SizeUOffsetT), len(v)*SizeByte) |
| 370 | |
| 371 | l := UOffsetT(len(v)) |
| 372 | |
| 373 | b.head -= l |
| 374 | copy(b.Bytes[b.head:b.head+l], v) |
| 375 | |
| 376 | return b.EndVector(len(v)) |
| 377 | } |
| 378 | |
| 379 | func (b *Builder) assertNested() { |
| 380 | // If you get this assert, you're in an object while trying to write |
| 381 | // data that belongs outside of an object. |
| 382 | // To fix this, write non-inline data (like vectors) before creating |
| 383 | // objects. |
| 384 | if !b.nested { |
| 385 | panic("Incorrect creation order: must be inside object.") |
| 386 | } |
| 387 | } |
| 388 | |
| 389 | func (b *Builder) assertNotNested() { |
| 390 | // If you hit this, you're trying to construct a Table/Vector/String |
| 391 | // during the construction of its parent table (between the MyTableBuilder |
| 392 | // and builder.Finish()). |
| 393 | // Move the creation of these sub-objects to above the MyTableBuilder to |
| 394 | // not get this assert. |
| 395 | // Ignoring this assert may appear to work in simple cases, but the reason |
| 396 | // it is here is that storing objects in-line may cause vtable offsets |
| 397 | // to not fit anymore. It also leads to vtable duplication. |
| 398 | if b.nested { |
| 399 | panic("Incorrect creation order: object must not be nested.") |
| 400 | } |
| 401 | } |
| 402 | |
| 403 | func (b *Builder) assertFinished() { |
| 404 | // If you get this assert, you're attempting to get access a buffer |
| 405 | // which hasn't been finished yet. Be sure to call builder.Finish() |
| 406 | // with your root table. |
| 407 | // If you really need to access an unfinished buffer, use the Bytes |
| 408 | // buffer directly. |
| 409 | if !b.finished { |
| 410 | panic("Incorrect use of FinishedBytes(): must call 'Finish' first.") |
| 411 | } |
| 412 | } |
| 413 | |
| 414 | // PrependBoolSlot prepends a bool onto the object at vtable slot `o`. |
| 415 | // If value `x` equals default `d`, then the slot will be set to zero and no |
| 416 | // other data will be written. |
| 417 | func (b *Builder) PrependBoolSlot(o int, x, d bool) { |
| 418 | val := byte(0) |
| 419 | if x { |
| 420 | val = 1 |
| 421 | } |
| 422 | def := byte(0) |
| 423 | if d { |
| 424 | def = 1 |
| 425 | } |
| 426 | b.PrependByteSlot(o, val, def) |
| 427 | } |
| 428 | |
| 429 | // PrependByteSlot prepends a byte onto the object at vtable slot `o`. |
| 430 | // If value `x` equals default `d`, then the slot will be set to zero and no |
| 431 | // other data will be written. |
| 432 | func (b *Builder) PrependByteSlot(o int, x, d byte) { |
| 433 | if x != d { |
| 434 | b.PrependByte(x) |
| 435 | b.Slot(o) |
| 436 | } |
| 437 | } |
| 438 | |
| 439 | // PrependUint8Slot prepends a uint8 onto the object at vtable slot `o`. |
| 440 | // If value `x` equals default `d`, then the slot will be set to zero and no |
| 441 | // other data will be written. |
| 442 | func (b *Builder) PrependUint8Slot(o int, x, d uint8) { |
| 443 | if x != d { |
| 444 | b.PrependUint8(x) |
| 445 | b.Slot(o) |
| 446 | } |
| 447 | } |
| 448 | |
| 449 | // PrependUint16Slot prepends a uint16 onto the object at vtable slot `o`. |
| 450 | // If value `x` equals default `d`, then the slot will be set to zero and no |
| 451 | // other data will be written. |
| 452 | func (b *Builder) PrependUint16Slot(o int, x, d uint16) { |
| 453 | if x != d { |
| 454 | b.PrependUint16(x) |
| 455 | b.Slot(o) |
| 456 | } |
| 457 | } |
| 458 | |
| 459 | // PrependUint32Slot prepends a uint32 onto the object at vtable slot `o`. |
| 460 | // If value `x` equals default `d`, then the slot will be set to zero and no |
| 461 | // other data will be written. |
| 462 | func (b *Builder) PrependUint32Slot(o int, x, d uint32) { |
| 463 | if x != d { |
| 464 | b.PrependUint32(x) |
| 465 | b.Slot(o) |
| 466 | } |
| 467 | } |
| 468 | |
| 469 | // PrependUint64Slot prepends a uint64 onto the object at vtable slot `o`. |
| 470 | // If value `x` equals default `d`, then the slot will be set to zero and no |
| 471 | // other data will be written. |
| 472 | func (b *Builder) PrependUint64Slot(o int, x, d uint64) { |
| 473 | if x != d { |
| 474 | b.PrependUint64(x) |
| 475 | b.Slot(o) |
| 476 | } |
| 477 | } |
| 478 | |
| 479 | // PrependInt8Slot prepends a int8 onto the object at vtable slot `o`. |
| 480 | // If value `x` equals default `d`, then the slot will be set to zero and no |
| 481 | // other data will be written. |
| 482 | func (b *Builder) PrependInt8Slot(o int, x, d int8) { |
| 483 | if x != d { |
| 484 | b.PrependInt8(x) |
| 485 | b.Slot(o) |
| 486 | } |
| 487 | } |
| 488 | |
| 489 | // PrependInt16Slot prepends a int16 onto the object at vtable slot `o`. |
| 490 | // If value `x` equals default `d`, then the slot will be set to zero and no |
| 491 | // other data will be written. |
| 492 | func (b *Builder) PrependInt16Slot(o int, x, d int16) { |
| 493 | if x != d { |
| 494 | b.PrependInt16(x) |
| 495 | b.Slot(o) |
| 496 | } |
| 497 | } |
| 498 | |
| 499 | // PrependInt32Slot prepends a int32 onto the object at vtable slot `o`. |
| 500 | // If value `x` equals default `d`, then the slot will be set to zero and no |
| 501 | // other data will be written. |
| 502 | func (b *Builder) PrependInt32Slot(o int, x, d int32) { |
| 503 | if x != d { |
| 504 | b.PrependInt32(x) |
| 505 | b.Slot(o) |
| 506 | } |
| 507 | } |
| 508 | |
| 509 | // PrependInt64Slot prepends a int64 onto the object at vtable slot `o`. |
| 510 | // If value `x` equals default `d`, then the slot will be set to zero and no |
| 511 | // other data will be written. |
| 512 | func (b *Builder) PrependInt64Slot(o int, x, d int64) { |
| 513 | if x != d { |
| 514 | b.PrependInt64(x) |
| 515 | b.Slot(o) |
| 516 | } |
| 517 | } |
| 518 | |
| 519 | // PrependFloat32Slot prepends a float32 onto the object at vtable slot `o`. |
| 520 | // If value `x` equals default `d`, then the slot will be set to zero and no |
| 521 | // other data will be written. |
| 522 | func (b *Builder) PrependFloat32Slot(o int, x, d float32) { |
| 523 | if x != d { |
| 524 | b.PrependFloat32(x) |
| 525 | b.Slot(o) |
| 526 | } |
| 527 | } |
| 528 | |
| 529 | // PrependFloat64Slot prepends a float64 onto the object at vtable slot `o`. |
| 530 | // If value `x` equals default `d`, then the slot will be set to zero and no |
| 531 | // other data will be written. |
| 532 | func (b *Builder) PrependFloat64Slot(o int, x, d float64) { |
| 533 | if x != d { |
| 534 | b.PrependFloat64(x) |
| 535 | b.Slot(o) |
| 536 | } |
| 537 | } |
| 538 | |
| 539 | // PrependUOffsetTSlot prepends an UOffsetT onto the object at vtable slot `o`. |
| 540 | // If value `x` equals default `d`, then the slot will be set to zero and no |
| 541 | // other data will be written. |
| 542 | func (b *Builder) PrependUOffsetTSlot(o int, x, d UOffsetT) { |
| 543 | if x != d { |
| 544 | b.PrependUOffsetT(x) |
| 545 | b.Slot(o) |
| 546 | } |
| 547 | } |
| 548 | |
| 549 | // PrependStructSlot prepends a struct onto the object at vtable slot `o`. |
| 550 | // Structs are stored inline, so nothing additional is being added. |
| 551 | // In generated code, `d` is always 0. |
| 552 | func (b *Builder) PrependStructSlot(voffset int, x, d UOffsetT) { |
| 553 | if x != d { |
| 554 | b.assertNested() |
| 555 | if x != b.Offset() { |
| 556 | panic("inline data write outside of object") |
| 557 | } |
| 558 | b.Slot(voffset) |
| 559 | } |
| 560 | } |
| 561 | |
| 562 | // Slot sets the vtable key `voffset` to the current location in the buffer. |
| 563 | func (b *Builder) Slot(slotnum int) { |
| 564 | b.vtable[slotnum] = UOffsetT(b.Offset()) |
| 565 | } |
| 566 | |
| 567 | // FinishWithFileIdentifier finalizes a buffer, pointing to the given `rootTable`. |
| 568 | // as well as applys a file identifier |
| 569 | func (b *Builder) FinishWithFileIdentifier(rootTable UOffsetT, fid []byte) { |
| 570 | if fid == nil || len(fid) != fileIdentifierLength { |
| 571 | panic("incorrect file identifier length") |
| 572 | } |
| 573 | // In order to add a file identifier to the flatbuffer message, we need |
| 574 | // to prepare an alignment and file identifier length |
| 575 | b.Prep(b.minalign, SizeInt32+fileIdentifierLength) |
| 576 | for i := fileIdentifierLength - 1; i >= 0; i-- { |
| 577 | // place the file identifier |
| 578 | b.PlaceByte(fid[i]) |
| 579 | } |
| 580 | // finish |
| 581 | b.Finish(rootTable) |
| 582 | } |
| 583 | |
Austin Schuh | 272c613 | 2020-11-14 16:37:52 -0800 | [diff] [blame^] | 584 | // FinishSizePrefixed finalizes a buffer, pointing to the given `rootTable`. |
| 585 | // The buffer is prefixed with the size of the buffer, excluding the size |
| 586 | // of the prefix itself. |
| 587 | func (b *Builder) FinishSizePrefixed(rootTable UOffsetT) { |
| 588 | b.finish(rootTable, true) |
| 589 | } |
| 590 | |
| 591 | // FinishSizePrefixedWithFileIdentifier finalizes a buffer, pointing to the given `rootTable` |
| 592 | // and applies a file identifier. The buffer is prefixed with the size of the buffer, |
| 593 | // excluding the size of the prefix itself. |
| 594 | func (b *Builder) FinishSizePrefixedWithFileIdentifier(rootTable UOffsetT, fid []byte) { |
| 595 | if fid == nil || len(fid) != fileIdentifierLength { |
| 596 | panic("incorrect file identifier length") |
| 597 | } |
| 598 | // In order to add a file identifier and size prefix to the flatbuffer message, |
| 599 | // we need to prepare an alignment, a size prefix length, and file identifier length |
| 600 | b.Prep(b.minalign, SizeInt32+fileIdentifierLength+sizePrefixLength) |
| 601 | for i := fileIdentifierLength - 1; i >= 0; i-- { |
| 602 | // place the file identifier |
| 603 | b.PlaceByte(fid[i]) |
| 604 | } |
| 605 | // finish |
| 606 | b.finish(rootTable, true) |
| 607 | } |
| 608 | |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 609 | // Finish finalizes a buffer, pointing to the given `rootTable`. |
| 610 | func (b *Builder) Finish(rootTable UOffsetT) { |
Austin Schuh | 272c613 | 2020-11-14 16:37:52 -0800 | [diff] [blame^] | 611 | b.finish(rootTable, false) |
| 612 | } |
| 613 | |
| 614 | // finish finalizes a buffer, pointing to the given `rootTable` |
| 615 | // with an optional size prefix. |
| 616 | func (b *Builder) finish(rootTable UOffsetT, sizePrefix bool) { |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 617 | b.assertNotNested() |
Austin Schuh | 272c613 | 2020-11-14 16:37:52 -0800 | [diff] [blame^] | 618 | |
| 619 | if sizePrefix { |
| 620 | b.Prep(b.minalign, SizeUOffsetT+sizePrefixLength) |
| 621 | } else { |
| 622 | b.Prep(b.minalign, SizeUOffsetT) |
| 623 | } |
| 624 | |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 625 | b.PrependUOffsetT(rootTable) |
Austin Schuh | 272c613 | 2020-11-14 16:37:52 -0800 | [diff] [blame^] | 626 | |
| 627 | if sizePrefix { |
| 628 | b.PlaceUint32(uint32(b.Offset())) |
| 629 | } |
| 630 | |
Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame] | 631 | b.finished = true |
| 632 | } |
| 633 | |
| 634 | // vtableEqual compares an unwritten vtable to a written vtable. |
| 635 | func vtableEqual(a []UOffsetT, objectStart UOffsetT, b []byte) bool { |
| 636 | if len(a)*SizeVOffsetT != len(b) { |
| 637 | return false |
| 638 | } |
| 639 | |
| 640 | for i := 0; i < len(a); i++ { |
| 641 | x := GetVOffsetT(b[i*SizeVOffsetT : (i+1)*SizeVOffsetT]) |
| 642 | |
| 643 | // Skip vtable entries that indicate a default value. |
| 644 | if x == 0 && a[i] == 0 { |
| 645 | continue |
| 646 | } |
| 647 | |
| 648 | y := SOffsetT(objectStart) - SOffsetT(a[i]) |
| 649 | if SOffsetT(x) != y { |
| 650 | return false |
| 651 | } |
| 652 | } |
| 653 | return true |
| 654 | } |
| 655 | |
| 656 | // PrependBool prepends a bool to the Builder buffer. |
| 657 | // Aligns and checks for space. |
| 658 | func (b *Builder) PrependBool(x bool) { |
| 659 | b.Prep(SizeBool, 0) |
| 660 | b.PlaceBool(x) |
| 661 | } |
| 662 | |
| 663 | // PrependUint8 prepends a uint8 to the Builder buffer. |
| 664 | // Aligns and checks for space. |
| 665 | func (b *Builder) PrependUint8(x uint8) { |
| 666 | b.Prep(SizeUint8, 0) |
| 667 | b.PlaceUint8(x) |
| 668 | } |
| 669 | |
| 670 | // PrependUint16 prepends a uint16 to the Builder buffer. |
| 671 | // Aligns and checks for space. |
| 672 | func (b *Builder) PrependUint16(x uint16) { |
| 673 | b.Prep(SizeUint16, 0) |
| 674 | b.PlaceUint16(x) |
| 675 | } |
| 676 | |
| 677 | // PrependUint32 prepends a uint32 to the Builder buffer. |
| 678 | // Aligns and checks for space. |
| 679 | func (b *Builder) PrependUint32(x uint32) { |
| 680 | b.Prep(SizeUint32, 0) |
| 681 | b.PlaceUint32(x) |
| 682 | } |
| 683 | |
| 684 | // PrependUint64 prepends a uint64 to the Builder buffer. |
| 685 | // Aligns and checks for space. |
| 686 | func (b *Builder) PrependUint64(x uint64) { |
| 687 | b.Prep(SizeUint64, 0) |
| 688 | b.PlaceUint64(x) |
| 689 | } |
| 690 | |
| 691 | // PrependInt8 prepends a int8 to the Builder buffer. |
| 692 | // Aligns and checks for space. |
| 693 | func (b *Builder) PrependInt8(x int8) { |
| 694 | b.Prep(SizeInt8, 0) |
| 695 | b.PlaceInt8(x) |
| 696 | } |
| 697 | |
| 698 | // PrependInt16 prepends a int16 to the Builder buffer. |
| 699 | // Aligns and checks for space. |
| 700 | func (b *Builder) PrependInt16(x int16) { |
| 701 | b.Prep(SizeInt16, 0) |
| 702 | b.PlaceInt16(x) |
| 703 | } |
| 704 | |
| 705 | // PrependInt32 prepends a int32 to the Builder buffer. |
| 706 | // Aligns and checks for space. |
| 707 | func (b *Builder) PrependInt32(x int32) { |
| 708 | b.Prep(SizeInt32, 0) |
| 709 | b.PlaceInt32(x) |
| 710 | } |
| 711 | |
| 712 | // PrependInt64 prepends a int64 to the Builder buffer. |
| 713 | // Aligns and checks for space. |
| 714 | func (b *Builder) PrependInt64(x int64) { |
| 715 | b.Prep(SizeInt64, 0) |
| 716 | b.PlaceInt64(x) |
| 717 | } |
| 718 | |
| 719 | // PrependFloat32 prepends a float32 to the Builder buffer. |
| 720 | // Aligns and checks for space. |
| 721 | func (b *Builder) PrependFloat32(x float32) { |
| 722 | b.Prep(SizeFloat32, 0) |
| 723 | b.PlaceFloat32(x) |
| 724 | } |
| 725 | |
| 726 | // PrependFloat64 prepends a float64 to the Builder buffer. |
| 727 | // Aligns and checks for space. |
| 728 | func (b *Builder) PrependFloat64(x float64) { |
| 729 | b.Prep(SizeFloat64, 0) |
| 730 | b.PlaceFloat64(x) |
| 731 | } |
| 732 | |
| 733 | // PrependByte prepends a byte to the Builder buffer. |
| 734 | // Aligns and checks for space. |
| 735 | func (b *Builder) PrependByte(x byte) { |
| 736 | b.Prep(SizeByte, 0) |
| 737 | b.PlaceByte(x) |
| 738 | } |
| 739 | |
| 740 | // PrependVOffsetT prepends a VOffsetT to the Builder buffer. |
| 741 | // Aligns and checks for space. |
| 742 | func (b *Builder) PrependVOffsetT(x VOffsetT) { |
| 743 | b.Prep(SizeVOffsetT, 0) |
| 744 | b.PlaceVOffsetT(x) |
| 745 | } |
| 746 | |
| 747 | // PlaceBool prepends a bool to the Builder, without checking for space. |
| 748 | func (b *Builder) PlaceBool(x bool) { |
| 749 | b.head -= UOffsetT(SizeBool) |
| 750 | WriteBool(b.Bytes[b.head:], x) |
| 751 | } |
| 752 | |
| 753 | // PlaceUint8 prepends a uint8 to the Builder, without checking for space. |
| 754 | func (b *Builder) PlaceUint8(x uint8) { |
| 755 | b.head -= UOffsetT(SizeUint8) |
| 756 | WriteUint8(b.Bytes[b.head:], x) |
| 757 | } |
| 758 | |
| 759 | // PlaceUint16 prepends a uint16 to the Builder, without checking for space. |
| 760 | func (b *Builder) PlaceUint16(x uint16) { |
| 761 | b.head -= UOffsetT(SizeUint16) |
| 762 | WriteUint16(b.Bytes[b.head:], x) |
| 763 | } |
| 764 | |
| 765 | // PlaceUint32 prepends a uint32 to the Builder, without checking for space. |
| 766 | func (b *Builder) PlaceUint32(x uint32) { |
| 767 | b.head -= UOffsetT(SizeUint32) |
| 768 | WriteUint32(b.Bytes[b.head:], x) |
| 769 | } |
| 770 | |
| 771 | // PlaceUint64 prepends a uint64 to the Builder, without checking for space. |
| 772 | func (b *Builder) PlaceUint64(x uint64) { |
| 773 | b.head -= UOffsetT(SizeUint64) |
| 774 | WriteUint64(b.Bytes[b.head:], x) |
| 775 | } |
| 776 | |
| 777 | // PlaceInt8 prepends a int8 to the Builder, without checking for space. |
| 778 | func (b *Builder) PlaceInt8(x int8) { |
| 779 | b.head -= UOffsetT(SizeInt8) |
| 780 | WriteInt8(b.Bytes[b.head:], x) |
| 781 | } |
| 782 | |
| 783 | // PlaceInt16 prepends a int16 to the Builder, without checking for space. |
| 784 | func (b *Builder) PlaceInt16(x int16) { |
| 785 | b.head -= UOffsetT(SizeInt16) |
| 786 | WriteInt16(b.Bytes[b.head:], x) |
| 787 | } |
| 788 | |
| 789 | // PlaceInt32 prepends a int32 to the Builder, without checking for space. |
| 790 | func (b *Builder) PlaceInt32(x int32) { |
| 791 | b.head -= UOffsetT(SizeInt32) |
| 792 | WriteInt32(b.Bytes[b.head:], x) |
| 793 | } |
| 794 | |
| 795 | // PlaceInt64 prepends a int64 to the Builder, without checking for space. |
| 796 | func (b *Builder) PlaceInt64(x int64) { |
| 797 | b.head -= UOffsetT(SizeInt64) |
| 798 | WriteInt64(b.Bytes[b.head:], x) |
| 799 | } |
| 800 | |
| 801 | // PlaceFloat32 prepends a float32 to the Builder, without checking for space. |
| 802 | func (b *Builder) PlaceFloat32(x float32) { |
| 803 | b.head -= UOffsetT(SizeFloat32) |
| 804 | WriteFloat32(b.Bytes[b.head:], x) |
| 805 | } |
| 806 | |
| 807 | // PlaceFloat64 prepends a float64 to the Builder, without checking for space. |
| 808 | func (b *Builder) PlaceFloat64(x float64) { |
| 809 | b.head -= UOffsetT(SizeFloat64) |
| 810 | WriteFloat64(b.Bytes[b.head:], x) |
| 811 | } |
| 812 | |
| 813 | // PlaceByte prepends a byte to the Builder, without checking for space. |
| 814 | func (b *Builder) PlaceByte(x byte) { |
| 815 | b.head -= UOffsetT(SizeByte) |
| 816 | WriteByte(b.Bytes[b.head:], x) |
| 817 | } |
| 818 | |
| 819 | // PlaceVOffsetT prepends a VOffsetT to the Builder, without checking for space. |
| 820 | func (b *Builder) PlaceVOffsetT(x VOffsetT) { |
| 821 | b.head -= UOffsetT(SizeVOffsetT) |
| 822 | WriteVOffsetT(b.Bytes[b.head:], x) |
| 823 | } |
| 824 | |
| 825 | // PlaceSOffsetT prepends a SOffsetT to the Builder, without checking for space. |
| 826 | func (b *Builder) PlaceSOffsetT(x SOffsetT) { |
| 827 | b.head -= UOffsetT(SizeSOffsetT) |
| 828 | WriteSOffsetT(b.Bytes[b.head:], x) |
| 829 | } |
| 830 | |
| 831 | // PlaceUOffsetT prepends a UOffsetT to the Builder, without checking for space. |
| 832 | func (b *Builder) PlaceUOffsetT(x UOffsetT) { |
| 833 | b.head -= UOffsetT(SizeUOffsetT) |
| 834 | WriteUOffsetT(b.Bytes[b.head:], x) |
| 835 | } |