+++ /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
-}