/gobin-codegen

Automatic codegen for encoding/binary marshaling

Primary LanguageGo

This is a work-in-progress stub generator for marshaling/unmarshaling go structs into the encoding/binary format. It handles many basic types, but if you get fancy, you'll break it. It does not handle strings.

The reason this code exists, for a simple, statically-sized struct:

BenchmarkReflectionMarshal       1000000              2016 ns/op
BenchmarkGeneratedMarshal       10000000               223 ns/op

Operation: Takes a file with go structs as input:

package duck

type Quack struct {
        X int
        Y int
}

Run as:

bin/bi duck_decl.go > duck_marshal.go

and outputs go code that does the same thing that binary.Write would do, but faster:

package duck

func (t *Quack) Marshal(w io.Writer) {
        var b [16]byte
        binary.LittleEndian.PutUint64(b[0:8], uint64(t.X))
        binary.LittleEndian.PutUint64(b[8:16], uint64(t.Y))
        w.Write(b[:])
}

You can use these stubs in your own code:

    q := &Quack{X: 1, Y: 2}
    buf := new bytes.Buffer
    if err := q.Marshal(buf) {
      fmt.Println("Could not marshal data: ", err)
      // handle error appropriately, return, etc.
    }
    fmt.Println("Marshaled data: ", buf)

    q2 := &Quack{}
    if err := q2.Unmarshal(buf) {
      fmt.Println("Could not unmarshal buf: ", err)
      // handle error here
    }
    fmt.Println("q2: ", q2)

In addition to standard encoding/binary formats, gobin-codegen will output code to handle variable-length slice data within structs if you ask it to. It does so by first encoding the length of the slice as a varint, and then writing the members of the slice. Such a struct is not compatible with the standard encoding/binary, but will work if you know both sides use gobin-codegen. In this way, gobin-codegen can marshal []byte and other variable length data types.

This is not production-quality code. Its optimizations are limited to small completely static structs - otherwise, the code it outputs will be both longer and slower than that shown above, but probably still 4x faster than the reflection-based marshaling. Generates code to handle marshaling and unmarshaling to/from encoding/binary.

It probably isn't nearly as general as you'd like it to be. Don't depend on it working properly. Don't think that it handles corner cases well. You get the idea. :)

NOTE: This library emits code for variable-length slices by prefixing them with a varint length. This is a departure from the encoding/binary format, which cannot handle variable length slices. If you use a slice type, your binary output will not be compatible with stock encoding/binary any more.

use:

  export GOPATH=`/bin/pwd`
  go install bi
  bin/bi /path/to/your/go/struct/decl.go > generated_code.go

use generated_code.go as you see fit.