package flatbuffersimport// Builder is a state machine for creating FlatBuffer objects.// Use a Builder to construct object(s) starting from leaf nodes.//// A Builder constructs byte buffers in a last-first manner for simplicity and// performance.typeBuilderstruct {// `Bytes` gives raw access to the buffer. Most users will want to use // FinishedBytes() instead. Bytes []byte minalign int vtable []UOffsetT objectEnd UOffsetT vtables []UOffsetT head UOffsetT nested bool finished bool sharedStrings map[string]UOffsetT}const fileIdentifierLength = 4const sizePrefixLength = 4// NewBuilder initializes a Builder of size `initial_size`.// The internal buffer is grown as needed.func ( int) *Builder {if <= 0 { = 0 } := &Builder{} .Bytes = make([]byte, ) .head = UOffsetT() .minalign = 1 .vtables = make([]UOffsetT, 0, 16) // sensible default capacityreturn}// Reset truncates the underlying Builder buffer, facilitating alloc-free// reuse of a Builder. It also resets bookkeeping data.func ( *Builder) () {if .Bytes != nil { .Bytes = .Bytes[:cap(.Bytes)] }if .vtables != nil { .vtables = .vtables[:0] }if .vtable != nil { .vtable = .vtable[:0] }if .sharedStrings != nil {for := range .sharedStrings {delete(.sharedStrings, ) } } .head = UOffsetT(len(.Bytes)) .minalign = 1 .nested = false .finished = false}// FinishedBytes returns a pointer to the written data in the byte buffer.// Panics if the builder is not in a finished state (which is caused by calling// `Finish()`).func ( *Builder) () []byte { .assertFinished()return .Bytes[.Head():]}// StartObject initializes bookkeeping for writing a new object.func ( *Builder) ( int) { .assertNotNested() .nested = true// use 32-bit offsets so that arithmetic doesn't overflow.ifcap(.vtable) < || .vtable == nil { .vtable = make([]UOffsetT, ) } else { .vtable = .vtable[:]for := 0; < len(.vtable); ++ { .vtable[] = 0 } } .objectEnd = .Offset()}// WriteVtable serializes the vtable for the current object, if applicable.//// Before writing out the vtable, this checks pre-existing vtables for equality// to this one. If an equal vtable is found, point the object to the existing// vtable and return.//// Because vtable values are sensitive to alignment of object data, not all// logically-equal vtables will be deduplicated.//// A vtable has the following format:// <VOffsetT: size of the vtable in bytes, including this value>// <VOffsetT: size of the object in bytes, including the vtable offset>// <VOffsetT: offset for a field> * N, where N is the number of fields in// the schema for this type. Includes deprecated fields.// Thus, a vtable is made of 2 + N elements, each SizeVOffsetT bytes wide.//// An object has the following format:// <SOffsetT: offset to this object's vtable (may be negative)>// <byte: data>+func ( *Builder) () ( UOffsetT) {// Prepend a zero scalar to the object. Later in this function we'll // write an offset here that points to the object's vtable: .PrependSOffsetT(0) := .Offset() := UOffsetT(0)// Trim vtable of trailing zeroes. := len(.vtable) - 1for ; >= 0 && .vtable[] == 0; -- { } .vtable = .vtable[:+1]// Search backwards through existing vtables, because similar vtables // are likely to have been recently appended. See // BenchmarkVtableDeduplication for a case in which this heuristic // saves about 30% of the time used in writing objects with duplicate // tables.for := len(.vtables) - 1; >= 0; -- {// Find the other vtable, which is associated with `i`: := .vtables[] := len(.Bytes) - int() := GetVOffsetT(.Bytes[:]) := VtableMetadataFields * SizeVOffsetT := + int() := .Bytes[+ : ]// Compare the other vtable to the one under consideration. // If they are equal, store the offset and break:ifvtableEqual(.vtable, , ) { = break } }if == 0 {// Did not find a vtable, so write this one to the buffer.// Write out the current vtable in reverse , because // serialization occurs in last-first order:for := len(.vtable) - 1; >= 0; -- {varUOffsetTif .vtable[] != 0 {// Forward reference to field; // use 32bit number to assert no overflow: = - .vtable[] } .PrependVOffsetT(VOffsetT()) }// The two metadata fields are written last.// First, store the object bytesize: := - .objectEnd .PrependVOffsetT(VOffsetT())// Second, store the vtable bytesize: := (len(.vtable) + VtableMetadataFields) * SizeVOffsetT .PrependVOffsetT(VOffsetT())// Next, write the offset to the new vtable in the // already-allocated SOffsetT at the beginning of this object: := SOffsetT(len(.Bytes)) - SOffsetT()WriteSOffsetT(.Bytes[:],SOffsetT(.Offset())-SOffsetT())// Finally, store this vtable in memory for future // deduplication: .vtables = append(.vtables, .Offset()) } else {// Found a duplicate vtable. := SOffsetT(len(.Bytes)) - SOffsetT() .head = UOffsetT()// Write the offset to the found vtable in the // already-allocated SOffsetT at the beginning of this object:WriteSOffsetT(.Bytes[.head:],SOffsetT()-SOffsetT()) } .vtable = .vtable[:0]return}// EndObject writes data necessary to finish object construction.func ( *Builder) () UOffsetT { .assertNested() := .WriteVtable() .nested = falsereturn}// Doubles the size of the byteslice, and copies the old data towards the// end of the new byteslice (since we build the buffer backwards).func ( *Builder) () {if (int64(len(.Bytes)) & int64(0xC0000000)) != 0 {panic("cannot grow buffer beyond 2 gigabytes") } := len(.Bytes) * 2if == 0 { = 1 }ifcap(.Bytes) >= { .Bytes = .Bytes[:] } else { := make([]byte, -len(.Bytes)) .Bytes = append(.Bytes, ...) } := / 2copy(.Bytes[:], .Bytes[:])}// Head gives the start of useful data in the underlying byte buffer.// Note: unlike other functions, this value is interpreted as from the left.func ( *Builder) () UOffsetT {return .head}// Offset relative to the end of the buffer.func ( *Builder) () UOffsetT {returnUOffsetT(len(.Bytes)) - .head}// Pad places zeros at the current offset.func ( *Builder) ( int) {for := 0; < ; ++ { .PlaceByte(0) }}// Prep prepares to write an element of `size` after `additional_bytes`// have been written, e.g. if you write a string, you need to align such// the int length field is aligned to SizeInt32, and the string data follows it// directly.// If all you need to do is align, `additionalBytes` will be 0.func ( *Builder) (, int) {// Track the biggest thing we've ever aligned to.if > .minalign { .minalign = }// Find the amount of alignment needed such that `size` is properly // aligned after `additionalBytes`: := (^(len(.Bytes) - int(.Head()) + )) + 1 &= ( - 1)// Reallocate the buffer if needed:forint(.head) <= ++ { := len(.Bytes) .growByteBuffer() .head += UOffsetT(len(.Bytes) - ) } .Pad()}// PrependSOffsetT prepends an SOffsetT, relative to where it will be written.func ( *Builder) ( SOffsetT) { .Prep(SizeSOffsetT, 0) // Ensure alignment is already done.if !(UOffsetT() <= .Offset()) {panic("unreachable: off <= b.Offset()") } := SOffsetT(.Offset()) - + SOffsetT(SizeSOffsetT) .PlaceSOffsetT()}// PrependUOffsetT prepends an UOffsetT, relative to where it will be written.func ( *Builder) ( UOffsetT) { .Prep(SizeUOffsetT, 0) // Ensure alignment is already done.if !( <= .Offset()) {panic("unreachable: off <= b.Offset()") } := .Offset() - + UOffsetT(SizeUOffsetT) .PlaceUOffsetT()}// StartVector initializes bookkeeping for writing a new vector.//// A vector has the following format:// <UOffsetT: number of elements in this vector>// <T: data>+, where T is the type of elements of this vector.func ( *Builder) (, , int) UOffsetT { .assertNotNested() .nested = true .Prep(SizeUint32, *) .Prep(, *) // Just in case alignment > int.return .Offset()}// EndVector writes data necessary to finish vector construction.func ( *Builder) ( int) UOffsetT { .assertNested()// we already made space for this, so write without PrependUint32 .PlaceUOffsetT(UOffsetT()) .nested = falsereturn .Offset()}// CreateVectorOfTables serializes slice of table offsets into a vector.func ( *Builder) ( []UOffsetT) UOffsetT { .assertNotNested() .StartVector(4, len(), 4)for := len() - 1; >= 0; -- { .PrependUOffsetT([]) }return .EndVector(len())}typeKeyComparefunc(o1, o2 UOffsetT, buf []byte) boolfunc ( *Builder) ( []UOffsetT, KeyCompare) UOffsetT {sort.Slice(, func(, int) bool {return ([], [], .Bytes) })return .CreateVectorOfTables()}// CreateSharedString Checks if the string is already written// to the buffer before calling CreateStringfunc ( *Builder) ( string) UOffsetT {if .sharedStrings == nil { .sharedStrings = make(map[string]UOffsetT) }if , := .sharedStrings[]; {return } := .CreateString() .sharedStrings[] = return}// CreateString writes a null-terminated string as a vector.func ( *Builder) ( string) UOffsetT { .assertNotNested() .nested = true .Prep(int(SizeUOffsetT), (len()+1)*SizeByte) .PlaceByte(0) := UOffsetT(len()) .head -= copy(.Bytes[.head:.head+], )return .EndVector(len())}// CreateByteString writes a byte slice as a string (null-terminated).func ( *Builder) ( []byte) UOffsetT { .assertNotNested() .nested = true .Prep(int(SizeUOffsetT), (len()+1)*SizeByte) .PlaceByte(0) := UOffsetT(len()) .head -= copy(.Bytes[.head:.head+], )return .EndVector(len())}// CreateByteVector writes a ubyte vectorfunc ( *Builder) ( []byte) UOffsetT { .assertNotNested() .nested = true .Prep(int(SizeUOffsetT), len()*SizeByte) := UOffsetT(len()) .head -= copy(.Bytes[.head:.head+], )return .EndVector(len())}func ( *Builder) () {// If you get this assert, you're in an object while trying to write // data that belongs outside of an object. // To fix this, write non-inline data (like vectors) before creating // objects.if !.nested {panic("Incorrect creation order: must be inside object.") }}func ( *Builder) () {// If you hit this, you're trying to construct a Table/Vector/String // during the construction of its parent table (between the MyTableBuilder // and builder.Finish()). // Move the creation of these sub-objects to above the MyTableBuilder to // not get this assert. // Ignoring this assert may appear to work in simple cases, but the reason // it is here is that storing objects in-line may cause vtable offsets // to not fit anymore. It also leads to vtable duplication.if .nested {panic("Incorrect creation order: object must not be nested.") }}func ( *Builder) () {// If you get this assert, you're attempting to get access a buffer // which hasn't been finished yet. Be sure to call builder.Finish() // with your root table. // If you really need to access an unfinished buffer, use the Bytes // buffer directly.if !.finished {panic("Incorrect use of FinishedBytes(): must call 'Finish' first.") }}// PrependBoolSlot prepends a bool onto the object at vtable slot `o`.// If value `x` equals default `d`, then the slot will be set to zero and no// other data will be written.func ( *Builder) ( int, , bool) { := byte(0)if { = 1 } := byte(0)if { = 1 } .PrependByteSlot(, , )}// PrependByteSlot prepends a byte onto the object at vtable slot `o`.// If value `x` equals default `d`, then the slot will be set to zero and no// other data will be written.func ( *Builder) ( int, , byte) {if != { .PrependByte() .Slot() }}// PrependUint8Slot prepends a uint8 onto the object at vtable slot `o`.// If value `x` equals default `d`, then the slot will be set to zero and no// other data will be written.func ( *Builder) ( int, , uint8) {if != { .PrependUint8() .Slot() }}// PrependUint16Slot prepends a uint16 onto the object at vtable slot `o`.// If value `x` equals default `d`, then the slot will be set to zero and no// other data will be written.func ( *Builder) ( int, , uint16) {if != { .PrependUint16() .Slot() }}// PrependUint32Slot prepends a uint32 onto the object at vtable slot `o`.// If value `x` equals default `d`, then the slot will be set to zero and no// other data will be written.func ( *Builder) ( int, , uint32) {if != { .PrependUint32() .Slot() }}// PrependUint64Slot prepends a uint64 onto the object at vtable slot `o`.// If value `x` equals default `d`, then the slot will be set to zero and no// other data will be written.func ( *Builder) ( int, , uint64) {if != { .PrependUint64() .Slot() }}// PrependInt8Slot prepends a int8 onto the object at vtable slot `o`.// If value `x` equals default `d`, then the slot will be set to zero and no// other data will be written.func ( *Builder) ( int, , int8) {if != { .PrependInt8() .Slot() }}// PrependInt16Slot prepends a int16 onto the object at vtable slot `o`.// If value `x` equals default `d`, then the slot will be set to zero and no// other data will be written.func ( *Builder) ( int, , int16) {if != { .PrependInt16() .Slot() }}// PrependInt32Slot prepends a int32 onto the object at vtable slot `o`.// If value `x` equals default `d`, then the slot will be set to zero and no// other data will be written.func ( *Builder) ( int, , int32) {if != { .PrependInt32() .Slot() }}// PrependInt64Slot prepends a int64 onto the object at vtable slot `o`.// If value `x` equals default `d`, then the slot will be set to zero and no// other data will be written.func ( *Builder) ( int, , int64) {if != { .PrependInt64() .Slot() }}// PrependFloat32Slot prepends a float32 onto the object at vtable slot `o`.// If value `x` equals default `d`, then the slot will be set to zero and no// other data will be written.func ( *Builder) ( int, , float32) {if != { .PrependFloat32() .Slot() }}// PrependFloat64Slot prepends a float64 onto the object at vtable slot `o`.// If value `x` equals default `d`, then the slot will be set to zero and no// other data will be written.func ( *Builder) ( int, , float64) {if != { .PrependFloat64() .Slot() }}// PrependUOffsetTSlot prepends an UOffsetT onto the object at vtable slot `o`.// If value `x` equals default `d`, then the slot will be set to zero and no// other data will be written.func ( *Builder) ( int, , UOffsetT) {if != { .PrependUOffsetT() .Slot() }}// PrependStructSlot prepends a struct onto the object at vtable slot `o`.// Structs are stored inline, so nothing additional is being added.// In generated code, `d` is always 0.func ( *Builder) ( int, , UOffsetT) {if != { .assertNested()if != .Offset() {panic("inline data write outside of object") } .Slot() }}// Slot sets the vtable key `voffset` to the current location in the buffer.func ( *Builder) ( int) { .vtable[] = UOffsetT(.Offset())}// FinishWithFileIdentifier finalizes a buffer, pointing to the given `rootTable`.// as well as applys a file identifierfunc ( *Builder) ( UOffsetT, []byte) {if == nil || len() != fileIdentifierLength {panic("incorrect file identifier length") }// In order to add a file identifier to the flatbuffer message, we need // to prepare an alignment and file identifier length .Prep(.minalign, SizeInt32+fileIdentifierLength)for := fileIdentifierLength - 1; >= 0; -- {// place the file identifier .PlaceByte([]) }// finish .Finish()}// FinishSizePrefixed finalizes a buffer, pointing to the given `rootTable`.// The buffer is prefixed with the size of the buffer, excluding the size// of the prefix itself.func ( *Builder) ( UOffsetT) { .finish(, true)}// FinishSizePrefixedWithFileIdentifier finalizes a buffer, pointing to the given `rootTable`// and applies a file identifier. The buffer is prefixed with the size of the buffer,// excluding the size of the prefix itself.func ( *Builder) ( UOffsetT, []byte) {if == nil || len() != fileIdentifierLength {panic("incorrect file identifier length") }// In order to add a file identifier and size prefix to the flatbuffer message, // we need to prepare an alignment, a size prefix length, and file identifier length .Prep(.minalign, SizeInt32+fileIdentifierLength+sizePrefixLength)for := fileIdentifierLength - 1; >= 0; -- {// place the file identifier .PlaceByte([]) }// finish .finish(, true)}// Finish finalizes a buffer, pointing to the given `rootTable`.func ( *Builder) ( UOffsetT) { .finish(, false)}// finish finalizes a buffer, pointing to the given `rootTable`// with an optional size prefix.func ( *Builder) ( UOffsetT, bool) { .assertNotNested()if { .Prep(.minalign, SizeUOffsetT+sizePrefixLength) } else { .Prep(.minalign, SizeUOffsetT) } .PrependUOffsetT()if { .PlaceUint32(uint32(.Offset())) } .finished = true}// vtableEqual compares an unwritten vtable to a written vtable.func vtableEqual( []UOffsetT, UOffsetT, []byte) bool {iflen()*SizeVOffsetT != len() {returnfalse }for := 0; < len(); ++ { := GetVOffsetT([*SizeVOffsetT : (+1)*SizeVOffsetT])// Skip vtable entries that indicate a default value.if == 0 && [] == 0 {continue } := SOffsetT() - SOffsetT([])ifSOffsetT() != {returnfalse } }returntrue}// PrependBool prepends a bool to the Builder buffer.// Aligns and checks for space.func ( *Builder) ( bool) { .Prep(SizeBool, 0) .PlaceBool()}// PrependUint8 prepends a uint8 to the Builder buffer.// Aligns and checks for space.func ( *Builder) ( uint8) { .Prep(SizeUint8, 0) .PlaceUint8()}// PrependUint16 prepends a uint16 to the Builder buffer.// Aligns and checks for space.func ( *Builder) ( uint16) { .Prep(SizeUint16, 0) .PlaceUint16()}// PrependUint32 prepends a uint32 to the Builder buffer.// Aligns and checks for space.func ( *Builder) ( uint32) { .Prep(SizeUint32, 0) .PlaceUint32()}// PrependUint64 prepends a uint64 to the Builder buffer.// Aligns and checks for space.func ( *Builder) ( uint64) { .Prep(SizeUint64, 0) .PlaceUint64()}// PrependInt8 prepends a int8 to the Builder buffer.// Aligns and checks for space.func ( *Builder) ( int8) { .Prep(SizeInt8, 0) .PlaceInt8()}// PrependInt16 prepends a int16 to the Builder buffer.// Aligns and checks for space.func ( *Builder) ( int16) { .Prep(SizeInt16, 0) .PlaceInt16()}// PrependInt32 prepends a int32 to the Builder buffer.// Aligns and checks for space.func ( *Builder) ( int32) { .Prep(SizeInt32, 0) .PlaceInt32()}// PrependInt64 prepends a int64 to the Builder buffer.// Aligns and checks for space.func ( *Builder) ( int64) { .Prep(SizeInt64, 0) .PlaceInt64()}// PrependFloat32 prepends a float32 to the Builder buffer.// Aligns and checks for space.func ( *Builder) ( float32) { .Prep(SizeFloat32, 0) .PlaceFloat32()}// PrependFloat64 prepends a float64 to the Builder buffer.// Aligns and checks for space.func ( *Builder) ( float64) { .Prep(SizeFloat64, 0) .PlaceFloat64()}// PrependByte prepends a byte to the Builder buffer.// Aligns and checks for space.func ( *Builder) ( byte) { .Prep(SizeByte, 0) .PlaceByte()}// PrependVOffsetT prepends a VOffsetT to the Builder buffer.// Aligns and checks for space.func ( *Builder) ( VOffsetT) { .Prep(SizeVOffsetT, 0) .PlaceVOffsetT()}// PlaceBool prepends a bool to the Builder, without checking for space.func ( *Builder) ( bool) { .head -= UOffsetT(SizeBool)WriteBool(.Bytes[.head:], )}// PlaceUint8 prepends a uint8 to the Builder, without checking for space.func ( *Builder) ( uint8) { .head -= UOffsetT(SizeUint8)WriteUint8(.Bytes[.head:], )}// PlaceUint16 prepends a uint16 to the Builder, without checking for space.func ( *Builder) ( uint16) { .head -= UOffsetT(SizeUint16)WriteUint16(.Bytes[.head:], )}// PlaceUint32 prepends a uint32 to the Builder, without checking for space.func ( *Builder) ( uint32) { .head -= UOffsetT(SizeUint32)WriteUint32(.Bytes[.head:], )}// PlaceUint64 prepends a uint64 to the Builder, without checking for space.func ( *Builder) ( uint64) { .head -= UOffsetT(SizeUint64)WriteUint64(.Bytes[.head:], )}// PlaceInt8 prepends a int8 to the Builder, without checking for space.func ( *Builder) ( int8) { .head -= UOffsetT(SizeInt8)WriteInt8(.Bytes[.head:], )}// PlaceInt16 prepends a int16 to the Builder, without checking for space.func ( *Builder) ( int16) { .head -= UOffsetT(SizeInt16)WriteInt16(.Bytes[.head:], )}// PlaceInt32 prepends a int32 to the Builder, without checking for space.func ( *Builder) ( int32) { .head -= UOffsetT(SizeInt32)WriteInt32(.Bytes[.head:], )}// PlaceInt64 prepends a int64 to the Builder, without checking for space.func ( *Builder) ( int64) { .head -= UOffsetT(SizeInt64)WriteInt64(.Bytes[.head:], )}// PlaceFloat32 prepends a float32 to the Builder, without checking for space.func ( *Builder) ( float32) { .head -= UOffsetT(SizeFloat32)WriteFloat32(.Bytes[.head:], )}// PlaceFloat64 prepends a float64 to the Builder, without checking for space.func ( *Builder) ( float64) { .head -= UOffsetT(SizeFloat64)WriteFloat64(.Bytes[.head:], )}// PlaceByte prepends a byte to the Builder, without checking for space.func ( *Builder) ( byte) { .head -= UOffsetT(SizeByte)WriteByte(.Bytes[.head:], )}// PlaceVOffsetT prepends a VOffsetT to the Builder, without checking for space.func ( *Builder) ( VOffsetT) { .head -= UOffsetT(SizeVOffsetT)WriteVOffsetT(.Bytes[.head:], )}// PlaceSOffsetT prepends a SOffsetT to the Builder, without checking for space.func ( *Builder) ( SOffsetT) { .head -= UOffsetT(SizeSOffsetT)WriteSOffsetT(.Bytes[.head:], )}// PlaceUOffsetT prepends a UOffsetT to the Builder, without checking for space.func ( *Builder) ( UOffsetT) { .head -= UOffsetT(SizeUOffsetT)WriteUOffsetT(.Bytes[.head:], )}
The pages are generated with Goldsv0.8.2. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.