Skip to content

Instantly share code, notes, and snippets.

@somombo
Last active May 21, 2020 09:50
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save somombo/bc8feb5b7f1abecd50685594656cc6ba to your computer and use it in GitHub Desktop.
Save somombo/bc8feb5b7f1abecd50685594656cc6ba to your computer and use it in GitHub Desktop.
Example of Global (Un)Marshaling with flatbuffers

Global flatbuffer marshalling and unmarshalling will soon be available once google/flatbuffers#5593 lands

See also google/flatbuffers#5339 (comment):

func (m *MonsterT) Marshal() ([]byte, error) {
  b := flatbuffers.NewBuilder(0)
  b.Finish(m.Pack(b)) 
  return b.FinishedBytes(), nil
}

func (m *MonsterT) Unmarshal(data []byte) error {
  GetRootAsMonster(data, 0).UnpackTo(m)
  return nil
}

func (m *MonsterT) Pack(b *flatbuffers.Builder) flatbuffers.UOffsetT {
  // ...
}

func (m *Monster) UnpackTo(dst *MonsterT) {
  // ...
}

func (m *Monster) Unpack() (*MonsterT) {
  dst := &MonsterT{}
  m.UnpackTo(dst)
  return dst
}
// Adapted from https://github.com/protocolbuffers/protobuf/blob/master/examples/addressbook.proto
namespace generated;
// hypothetically is generated to the directory github.com/google/flatbuffers/samples/protocompat/generated
enum PhoneType:byte {
MOBILE = 0,
HOME,
WORK = 2
}
table PhoneNumber {
number:string;
type:PhoneType;
}
// Adapted from https://github.com/golang/protobuf/blob/master/ptypes/timestamp/timestamp.proto
struct Timestamp {
seconds:int64;
nanos:int32;
}
table Person {
name:string;
id:int32; // Unique ID number for this person.
email:string;
phones:[PhoneNumber];
last_updated: Timestamp; // google.protobuf.Timestamp
}
// Our address book file is just one of these.
table AddressBook {
people:[Person];
}
root_type AddressBook;
//go:generate flatb --go --gen-object-api addressbook.fbs
//go:generate cp marshaling.go.gen generated/marshaling.go
package main
import (
"fmt"
"github.com/google/flatbuffers/go/protocompat"
generated "github.com/google/flatbuffers/samples/protocompat/generated"
)
// func TestMarshalling(t *testing.T) {
func main() {
var err error
serialData := []byte{0x4, 0x0, 0x0, 0x0, 0xaa, 0xff, 0xff, 0xff, 0x4, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0xb0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x64, 0xff, 0xff, 0xff, 0x10, 0x0, 0x0, 0x0, 0x7c, 0x0, 0x0, 0x0, 0x8f, 0xc, 0x0, 0x0, 0x7c, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x4c, 0x0, 0x0, 0x0, 0x2c, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x60, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x2, 0x4, 0x0, 0x0, 0x0, 0xd, 0x0, 0x0, 0x0, 0x2b, 0x32, 0x36, 0x30, 0x39, 0x37, 0x37, 0x39, 0x37, 0x35, 0x37, 0x33, 0x33, 0x0, 0x6, 0x0, 0x8, 0x0, 0x4, 0x0, 0x6, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0xf, 0x0, 0x0, 0x0, 0x2b, 0x31, 0x2e, 0x33, 0x32, 0x31, 0x2e, 0x32, 0x32, 0x32, 0x2e, 0x33, 0x31, 0x31, 0x33, 0x0, 0xa0, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x1, 0x4, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0x35, 0x35, 0x35, 0x2d, 0x35, 0x35, 0x35, 0x2d, 0x30, 0x30, 0x30, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9, 0x0, 0x0, 0x0, 0x4a, 0x61, 0x6e, 0x65, 0x20, 0x42, 0x75, 0x63, 0x6b, 0x0, 0x0, 0x0, 0xc, 0x0, 0x14, 0x0, 0x10, 0x0, 0xc, 0x0, 0x8, 0x0, 0x4, 0x0, 0xc, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x0, 0x0, 0x2d, 0x2d, 0x0, 0x0, 0x4c, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0x8, 0x0, 0xc, 0x0, 0x8, 0x0, 0x7, 0x0, 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x4, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0x35, 0x35, 0x35, 0x2d, 0x35, 0x35, 0x35, 0x2d, 0x35, 0x35, 0x35, 0x35, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x6a, 0x61, 0x63, 0x6b, 0x40, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x0, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x4a, 0x61, 0x63, 0x6b, 0x20, 0x44, 0x6f, 0x65, 0x0, 0x0, 0x0, 0x0}
book := &generated.AddressBookT{}
err = protocompat.Unmarshal(serialData, book)
if err != nil {
panic(err)
}
fmt.Printf("\n - Old Phone Number: %s\n - Old Book Object: %#+v\n", book.People[1].Phones[0].Number, book)
// Mutate
fmt.Println("\n - Mutating the data ...")
book.People[1].Phones[0].Number = "123-456-7890"
// serialize mutated version
newSerialData, err := protocompat.Marshal(book)
if err != nil {
panic(err)
}
fmt.Printf(" - The old serial data is not identical to the new? %t!\n", newSerialData[150] != serialData[150]) // []
newDataObj := &generated.AddressBookT{}
err = protocompat.Unmarshal(newSerialData, newDataObj)
if err != nil {
panic(err)
}
fmt.Printf("\n - New Phone Number: %s\n - And the New Data Object: %#+v\n\n", book.People[1].Phones[0].Number, book)
}
// protocompat Codec
// The following would live in github.com/google/flatbuffers/go/protocompat
package protocompat
// Codec implements gRPC-go Codec which is used to encode and decode messages.
var Codec = "flatbuffers_protocompat"
// FlatbuffersCodec defines the interface gRPC uses to encode and decode messages. Note
// that implementations of this interface must be thread safe; a Codec's
// methods can be called from concurrent goroutines.
type FlatbuffersCodec struct{}
// Marshal returns the wire format of v.
func (FlatbuffersCodec) Marshal(v interface{}) ([]byte, error) {
return Marshal(v)
}
// Unmarshal parses the wire format into v.
func (FlatbuffersCodec) Unmarshal(data []byte, v interface{}) error {
return Unmarshal(data, v)
}
// String old gRPC Codec interface func
func (FlatbuffersCodec) String() string {
return Codec
}
// Name returns the name of the Codec implementation. The returned string
// will be used as part of content type in transmission. The result must be
// static; the result cannot change between calls.
//
// add Name() for ForceCodec interface
func (FlatbuffersCodec) Name() string {
return Codec
}
// The following would live in github.com/google/flatbuffers/go/protocompat
package protocompat
import (
"errors"
"fmt"
)
type Marshaler interface {
Marshal() ([]byte, error)
}
func Marshal(v interface{}) ([]byte, error) {
V, ok := v.(Marshaler)
if ok != true {
return nil, errors.New(fmt.Sprintf("Non-marshalable value provided v=%#+v", v))
}
return V.Marshal()
}
type Unmarshaler interface {
Unmarshal([]byte) error
}
func Unmarshal(data []byte, v interface{}) error {
V, ok := v.(Unmarshaler)
if ok != true {
return errors.New(fmt.Sprintf("Non-unmarshalable value provided v=%#+v", v))
}
return V.Unmarshal(data)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment