Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / honnef.co / go / tools@v0.0.1-2020.1.5 / cmd / structlayout-optimize / main.go
1 // structlayout-optimize reorders struct fields to minimize the amount
2 // of padding.
3 package main
4
5 import (
6         "encoding/json"
7         "flag"
8         "fmt"
9         "log"
10         "os"
11         "sort"
12         "strings"
13
14         st "honnef.co/go/tools/structlayout"
15         "honnef.co/go/tools/version"
16 )
17
18 var (
19         fJSON    bool
20         fRecurse bool
21         fVersion bool
22 )
23
24 func init() {
25         flag.BoolVar(&fJSON, "json", false, "Format data as JSON")
26         flag.BoolVar(&fRecurse, "r", false, "Break up structs and reorder their fields freely")
27         flag.BoolVar(&fVersion, "version", false, "Print version and exit")
28 }
29
30 func main() {
31         log.SetFlags(0)
32         flag.Parse()
33
34         if fVersion {
35                 version.Print()
36                 os.Exit(0)
37         }
38
39         var in []st.Field
40         if err := json.NewDecoder(os.Stdin).Decode(&in); err != nil {
41                 log.Fatal(err)
42         }
43         if len(in) == 0 {
44                 return
45         }
46         if !fRecurse {
47                 in = combine(in)
48         }
49         var fields []st.Field
50         for _, field := range in {
51                 if field.IsPadding {
52                         continue
53                 }
54                 fields = append(fields, field)
55         }
56         optimize(fields)
57         fields = pad(fields)
58
59         if fJSON {
60                 json.NewEncoder(os.Stdout).Encode(fields)
61         } else {
62                 for _, field := range fields {
63                         fmt.Println(field)
64                 }
65         }
66 }
67
68 func combine(fields []st.Field) []st.Field {
69         new := st.Field{}
70         cur := ""
71         var out []st.Field
72         wasPad := true
73         for _, field := range fields {
74                 var prefix string
75                 if field.IsPadding {
76                         wasPad = true
77                         continue
78                 }
79                 p := strings.Split(field.Name, ".")
80                 prefix = strings.Join(p[:2], ".")
81                 if field.Align > new.Align {
82                         new.Align = field.Align
83                 }
84                 if !wasPad {
85                         new.End = field.Start
86                         new.Size = new.End - new.Start
87                 }
88                 if prefix != cur {
89                         if cur != "" {
90                                 out = append(out, new)
91                         }
92                         cur = prefix
93                         new = field
94                         new.Name = prefix
95                 } else {
96                         new.Type = "struct"
97                 }
98                 wasPad = false
99         }
100         new.Size = new.End - new.Start
101         out = append(out, new)
102         return out
103 }
104
105 func optimize(fields []st.Field) {
106         sort.Sort(&byAlignAndSize{fields})
107 }
108
109 func pad(fields []st.Field) []st.Field {
110         if len(fields) == 0 {
111                 return nil
112         }
113         var out []st.Field
114         pos := int64(0)
115         offsets := offsetsof(fields)
116         alignment := int64(1)
117         for i, field := range fields {
118                 if field.Align > alignment {
119                         alignment = field.Align
120                 }
121                 if offsets[i] > pos {
122                         padding := offsets[i] - pos
123                         out = append(out, st.Field{
124                                 IsPadding: true,
125                                 Start:     pos,
126                                 End:       pos + padding,
127                                 Size:      padding,
128                         })
129                         pos += padding
130                 }
131                 field.Start = pos
132                 field.End = pos + field.Size
133                 out = append(out, field)
134                 pos += field.Size
135         }
136         sz := size(out)
137         pad := align(sz, alignment) - sz
138         if pad > 0 {
139                 field := out[len(out)-1]
140                 out = append(out, st.Field{
141                         IsPadding: true,
142                         Start:     field.End,
143                         End:       field.End + pad,
144                         Size:      pad,
145                 })
146         }
147         return out
148 }
149
150 func size(fields []st.Field) int64 {
151         n := int64(0)
152         for _, field := range fields {
153                 n += field.Size
154         }
155         return n
156 }
157
158 type byAlignAndSize struct {
159         fields []st.Field
160 }
161
162 func (s *byAlignAndSize) Len() int { return len(s.fields) }
163 func (s *byAlignAndSize) Swap(i, j int) {
164         s.fields[i], s.fields[j] = s.fields[j], s.fields[i]
165 }
166
167 func (s *byAlignAndSize) Less(i, j int) bool {
168         // Place zero sized objects before non-zero sized objects.
169         if s.fields[i].Size == 0 && s.fields[j].Size != 0 {
170                 return true
171         }
172         if s.fields[j].Size == 0 && s.fields[i].Size != 0 {
173                 return false
174         }
175
176         // Next, place more tightly aligned objects before less tightly aligned objects.
177         if s.fields[i].Align != s.fields[j].Align {
178                 return s.fields[i].Align > s.fields[j].Align
179         }
180
181         // Lastly, order by size.
182         if s.fields[i].Size != s.fields[j].Size {
183                 return s.fields[i].Size > s.fields[j].Size
184         }
185
186         return false
187 }
188
189 func offsetsof(fields []st.Field) []int64 {
190         offsets := make([]int64, len(fields))
191         var o int64
192         for i, f := range fields {
193                 a := f.Align
194                 o = align(o, a)
195                 offsets[i] = o
196                 o += f.Size
197         }
198         return offsets
199 }
200
201 // align returns the smallest y >= x such that y % a == 0.
202 func align(x, a int64) int64 {
203         y := x + a - 1
204         return y - y%a
205 }