--- /dev/null
+// structlayout-optimize reorders struct fields to minimize the amount
+// of padding.
+package main
+
+import (
+ "encoding/json"
+ "flag"
+ "fmt"
+ "log"
+ "os"
+ "sort"
+ "strings"
+
+ st "honnef.co/go/tools/structlayout"
+ "honnef.co/go/tools/version"
+)
+
+var (
+ fJSON bool
+ fRecurse bool
+ fVersion bool
+)
+
+func init() {
+ flag.BoolVar(&fJSON, "json", false, "Format data as JSON")
+ flag.BoolVar(&fRecurse, "r", false, "Break up structs and reorder their fields freely")
+ flag.BoolVar(&fVersion, "version", false, "Print version and exit")
+}
+
+func main() {
+ log.SetFlags(0)
+ flag.Parse()
+
+ if fVersion {
+ version.Print()
+ os.Exit(0)
+ }
+
+ var in []st.Field
+ if err := json.NewDecoder(os.Stdin).Decode(&in); err != nil {
+ log.Fatal(err)
+ }
+ if len(in) == 0 {
+ return
+ }
+ if !fRecurse {
+ in = combine(in)
+ }
+ var fields []st.Field
+ for _, field := range in {
+ if field.IsPadding {
+ continue
+ }
+ fields = append(fields, field)
+ }
+ optimize(fields)
+ fields = pad(fields)
+
+ if fJSON {
+ json.NewEncoder(os.Stdout).Encode(fields)
+ } else {
+ for _, field := range fields {
+ fmt.Println(field)
+ }
+ }
+}
+
+func combine(fields []st.Field) []st.Field {
+ new := st.Field{}
+ cur := ""
+ var out []st.Field
+ wasPad := true
+ for _, field := range fields {
+ var prefix string
+ if field.IsPadding {
+ wasPad = true
+ continue
+ }
+ p := strings.Split(field.Name, ".")
+ prefix = strings.Join(p[:2], ".")
+ if field.Align > new.Align {
+ new.Align = field.Align
+ }
+ if !wasPad {
+ new.End = field.Start
+ new.Size = new.End - new.Start
+ }
+ if prefix != cur {
+ if cur != "" {
+ out = append(out, new)
+ }
+ cur = prefix
+ new = field
+ new.Name = prefix
+ } else {
+ new.Type = "struct"
+ }
+ wasPad = false
+ }
+ new.Size = new.End - new.Start
+ out = append(out, new)
+ return out
+}
+
+func optimize(fields []st.Field) {
+ sort.Sort(&byAlignAndSize{fields})
+}
+
+func pad(fields []st.Field) []st.Field {
+ if len(fields) == 0 {
+ return nil
+ }
+ var out []st.Field
+ pos := int64(0)
+ offsets := offsetsof(fields)
+ alignment := int64(1)
+ for i, field := range fields {
+ if field.Align > alignment {
+ alignment = field.Align
+ }
+ if offsets[i] > pos {
+ padding := offsets[i] - pos
+ out = append(out, st.Field{
+ IsPadding: true,
+ Start: pos,
+ End: pos + padding,
+ Size: padding,
+ })
+ pos += padding
+ }
+ field.Start = pos
+ field.End = pos + field.Size
+ out = append(out, field)
+ pos += field.Size
+ }
+ sz := size(out)
+ pad := align(sz, alignment) - sz
+ if pad > 0 {
+ field := out[len(out)-1]
+ out = append(out, st.Field{
+ IsPadding: true,
+ Start: field.End,
+ End: field.End + pad,
+ Size: pad,
+ })
+ }
+ return out
+}
+
+func size(fields []st.Field) int64 {
+ n := int64(0)
+ for _, field := range fields {
+ n += field.Size
+ }
+ return n
+}
+
+type byAlignAndSize struct {
+ fields []st.Field
+}
+
+func (s *byAlignAndSize) Len() int { return len(s.fields) }
+func (s *byAlignAndSize) Swap(i, j int) {
+ s.fields[i], s.fields[j] = s.fields[j], s.fields[i]
+}
+
+func (s *byAlignAndSize) Less(i, j int) bool {
+ // Place zero sized objects before non-zero sized objects.
+ if s.fields[i].Size == 0 && s.fields[j].Size != 0 {
+ return true
+ }
+ if s.fields[j].Size == 0 && s.fields[i].Size != 0 {
+ return false
+ }
+
+ // Next, place more tightly aligned objects before less tightly aligned objects.
+ if s.fields[i].Align != s.fields[j].Align {
+ return s.fields[i].Align > s.fields[j].Align
+ }
+
+ // Lastly, order by size.
+ if s.fields[i].Size != s.fields[j].Size {
+ return s.fields[i].Size > s.fields[j].Size
+ }
+
+ return false
+}
+
+func offsetsof(fields []st.Field) []int64 {
+ offsets := make([]int64, len(fields))
+ var o int64
+ for i, f := range fields {
+ a := f.Align
+ o = align(o, a)
+ offsets[i] = o
+ o += f.Size
+ }
+ return offsets
+}
+
+// align returns the smallest y >= x such that y % a == 0.
+func align(x, a int64) int64 {
+ y := x + a - 1
+ return y - y%a
+}