gitee.com/lonely0422/gometalinter.git@v3.0.1-0.20190307123442-32416ab75314+incompatible/_linters/src/github.com/mdempsky/maligned/maligned.go (about) 1 // Copyright 2013 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package main 6 7 import ( 8 "flag" 9 "fmt" 10 "go/ast" 11 "go/build" 12 "go/token" 13 "go/types" 14 "log" 15 "sort" 16 17 "github.com/kisielk/gotool" 18 "golang.org/x/tools/go/loader" 19 ) 20 21 var fset = token.NewFileSet() 22 23 func main() { 24 flag.Parse() 25 26 importPaths := gotool.ImportPaths(flag.Args()) 27 if len(importPaths) == 0 { 28 return 29 } 30 31 var conf loader.Config 32 conf.Fset = fset 33 for _, importPath := range importPaths { 34 conf.Import(importPath) 35 } 36 prog, err := conf.Load() 37 if err != nil { 38 log.Fatal(err) 39 } 40 41 for _, pkg := range prog.InitialPackages() { 42 for _, file := range pkg.Files { 43 ast.Inspect(file, func(node ast.Node) bool { 44 if s, ok := node.(*ast.StructType); ok { 45 malign(node.Pos(), pkg.Types[s].Type.(*types.Struct)) 46 } 47 return true 48 }) 49 } 50 } 51 } 52 53 func malign(pos token.Pos, str *types.Struct) { 54 wordSize := int64(8) 55 maxAlign := int64(8) 56 switch build.Default.GOARCH { 57 case "386", "arm": 58 wordSize, maxAlign = 4, 4 59 case "amd64p32": 60 wordSize = 4 61 } 62 63 s := gcSizes{wordSize, maxAlign} 64 sz, opt := s.Sizeof(str), optimalSize(str, &s) 65 if sz != opt { 66 fmt.Printf("%s: struct of size %d could be %d\n", fset.Position(pos), sz, opt) 67 } 68 } 69 70 func optimalSize(str *types.Struct, sizes *gcSizes) int64 { 71 nf := str.NumFields() 72 fields := make([]*types.Var, nf) 73 alignofs := make([]int64, nf) 74 sizeofs := make([]int64, nf) 75 for i := 0; i < nf; i++ { 76 fields[i] = str.Field(i) 77 ft := fields[i].Type() 78 alignofs[i] = sizes.Alignof(ft) 79 sizeofs[i] = sizes.Sizeof(ft) 80 } 81 sort.Sort(&byAlignAndSize{fields, alignofs, sizeofs}) 82 return sizes.Sizeof(types.NewStruct(fields, nil)) 83 } 84 85 type byAlignAndSize struct { 86 fields []*types.Var 87 alignofs []int64 88 sizeofs []int64 89 } 90 91 func (s *byAlignAndSize) Len() int { return len(s.fields) } 92 func (s *byAlignAndSize) Swap(i, j int) { 93 s.fields[i], s.fields[j] = s.fields[j], s.fields[i] 94 s.alignofs[i], s.alignofs[j] = s.alignofs[j], s.alignofs[i] 95 s.sizeofs[i], s.sizeofs[j] = s.sizeofs[j], s.sizeofs[i] 96 } 97 98 func (s *byAlignAndSize) Less(i, j int) bool { 99 // Place zero sized objects before non-zero sized objects. 100 if s.sizeofs[i] == 0 && s.sizeofs[j] != 0 { 101 return true 102 } 103 if s.sizeofs[j] == 0 && s.sizeofs[i] != 0 { 104 return false 105 } 106 107 // Next, place more tightly aligned objects before less tightly aligned objects. 108 if s.alignofs[i] != s.alignofs[j] { 109 return s.alignofs[i] > s.alignofs[j] 110 } 111 112 // Lastly, order by size. 113 if s.sizeofs[i] != s.sizeofs[j] { 114 return s.sizeofs[i] > s.sizeofs[j] 115 } 116 117 return false 118 } 119 120 // Code below based on go/types.StdSizes. 121 122 type gcSizes struct { 123 WordSize int64 124 MaxAlign int64 125 } 126 127 func (s *gcSizes) Alignof(T types.Type) int64 { 128 // NOTE: On amd64, complex64 is 8 byte aligned, 129 // even though float32 is only 4 byte aligned. 130 131 // For arrays and structs, alignment is defined in terms 132 // of alignment of the elements and fields, respectively. 133 switch t := T.Underlying().(type) { 134 case *types.Array: 135 // spec: "For a variable x of array type: unsafe.Alignof(x) 136 // is the same as unsafe.Alignof(x[0]), but at least 1." 137 return s.Alignof(t.Elem()) 138 case *types.Struct: 139 // spec: "For a variable x of struct type: unsafe.Alignof(x) 140 // is the largest of the values unsafe.Alignof(x.f) for each 141 // field f of x, but at least 1." 142 max := int64(1) 143 for i, nf := 0, t.NumFields(); i < nf; i++ { 144 if a := s.Alignof(t.Field(i).Type()); a > max { 145 max = a 146 } 147 } 148 return max 149 } 150 a := s.Sizeof(T) // may be 0 151 // spec: "For a variable x of any type: unsafe.Alignof(x) is at least 1." 152 if a < 1 { 153 return 1 154 } 155 if a > s.MaxAlign { 156 return s.MaxAlign 157 } 158 return a 159 } 160 161 var basicSizes = [...]byte{ 162 types.Bool: 1, 163 types.Int8: 1, 164 types.Int16: 2, 165 types.Int32: 4, 166 types.Int64: 8, 167 types.Uint8: 1, 168 types.Uint16: 2, 169 types.Uint32: 4, 170 types.Uint64: 8, 171 types.Float32: 4, 172 types.Float64: 8, 173 types.Complex64: 8, 174 types.Complex128: 16, 175 } 176 177 func (s *gcSizes) Sizeof(T types.Type) int64 { 178 switch t := T.Underlying().(type) { 179 case *types.Basic: 180 k := t.Kind() 181 if int(k) < len(basicSizes) { 182 if s := basicSizes[k]; s > 0 { 183 return int64(s) 184 } 185 } 186 if k == types.String { 187 return s.WordSize * 2 188 } 189 case *types.Array: 190 n := t.Len() 191 if n == 0 { 192 return 0 193 } 194 a := s.Alignof(t.Elem()) 195 z := s.Sizeof(t.Elem()) 196 return align(z, a)*(n-1) + z 197 case *types.Slice: 198 return s.WordSize * 3 199 case *types.Struct: 200 nf := t.NumFields() 201 if nf == 0 { 202 return 0 203 } 204 205 var o int64 206 max := int64(1) 207 for i := 0; i < nf; i++ { 208 ft := t.Field(i).Type() 209 a, sz := s.Alignof(ft), s.Sizeof(ft) 210 if a > max { 211 max = a 212 } 213 if i == nf-1 && sz == 0 && o != 0 { 214 sz = 1 215 } 216 o = align(o, a) + sz 217 } 218 return align(o, max) 219 case *types.Interface: 220 return s.WordSize * 2 221 } 222 return s.WordSize // catch-all 223 } 224 225 // align returns the smallest y >= x such that y % a == 0. 226 func align(x, a int64) int64 { 227 y := x + a - 1 228 return y - y%a 229 }