aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/golangci/maligned/maligned.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/golangci/maligned/maligned.go')
-rw-r--r--vendor/github.com/golangci/maligned/maligned.go253
1 files changed, 253 insertions, 0 deletions
diff --git a/vendor/github.com/golangci/maligned/maligned.go b/vendor/github.com/golangci/maligned/maligned.go
new file mode 100644
index 000000000..c2492b2ff
--- /dev/null
+++ b/vendor/github.com/golangci/maligned/maligned.go
@@ -0,0 +1,253 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package maligned
+
+import (
+ "fmt"
+ "go/ast"
+ "go/build"
+ "go/token"
+ "go/types"
+ "sort"
+ "strings"
+
+ "golang.org/x/tools/go/loader"
+)
+
+var fset = token.NewFileSet()
+
+type Issue struct {
+ OldSize, NewSize int
+ NewStructDef string
+ Pos token.Position
+}
+
+func Run(prog *loader.Program) []Issue {
+ flagVerbose := true
+ fset = prog.Fset
+
+ var issues []Issue
+
+ for _, pkg := range prog.InitialPackages() {
+ for _, file := range pkg.Files {
+ ast.Inspect(file, func(node ast.Node) bool {
+ if s, ok := node.(*ast.StructType); ok {
+ i := malign(node.Pos(), pkg.Types[s].Type.(*types.Struct), flagVerbose)
+ if i != nil {
+ issues = append(issues, *i)
+ }
+ }
+ return true
+ })
+ }
+ }
+
+ return issues
+}
+
+func malign(pos token.Pos, str *types.Struct, verbose bool) *Issue {
+ wordSize := int64(8)
+ maxAlign := int64(8)
+ switch build.Default.GOARCH {
+ case "386", "arm":
+ wordSize, maxAlign = 4, 4
+ case "amd64p32":
+ wordSize = 4
+ }
+
+ s := gcSizes{wordSize, maxAlign}
+ sz := s.Sizeof(str)
+ opt, fields := optimalSize(str, &s, verbose)
+ if sz == opt {
+ return nil
+ }
+
+ newStructDefParts := []string{"struct{"}
+
+ var w int
+ for _, f := range fields {
+ if n := len(f.Name()); n > w {
+ w = n
+ }
+ }
+ spaces := strings.Repeat(" ", w)
+ for _, f := range fields {
+ line := fmt.Sprintf("\t%s%s\t%s,", f.Name(), spaces[len(f.Name()):], f.Type().String())
+ newStructDefParts = append(newStructDefParts, line)
+ }
+ newStructDefParts = append(newStructDefParts, "}")
+
+ return &Issue{
+ OldSize: int(sz),
+ NewSize: int(opt),
+ NewStructDef: strings.Join(newStructDefParts, "\n"),
+ Pos: fset.Position(pos),
+ }
+}
+
+func optimalSize(str *types.Struct, sizes *gcSizes, stable bool) (int64, []*types.Var) {
+ nf := str.NumFields()
+ fields := make([]*types.Var, nf)
+ alignofs := make([]int64, nf)
+ sizeofs := make([]int64, nf)
+ for i := 0; i < nf; i++ {
+ fields[i] = str.Field(i)
+ ft := fields[i].Type()
+ alignofs[i] = sizes.Alignof(ft)
+ sizeofs[i] = sizes.Sizeof(ft)
+ }
+ if stable { // Stable keeps as much of the order as possible, but slower
+ sort.Stable(&byAlignAndSize{fields, alignofs, sizeofs})
+ } else {
+ sort.Sort(&byAlignAndSize{fields, alignofs, sizeofs})
+ }
+ return sizes.Sizeof(types.NewStruct(fields, nil)), fields
+}
+
+type byAlignAndSize struct {
+ fields []*types.Var
+ alignofs []int64
+ sizeofs []int64
+}
+
+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]
+ s.alignofs[i], s.alignofs[j] = s.alignofs[j], s.alignofs[i]
+ s.sizeofs[i], s.sizeofs[j] = s.sizeofs[j], s.sizeofs[i]
+}
+
+func (s *byAlignAndSize) Less(i, j int) bool {
+ // Place zero sized objects before non-zero sized objects.
+ if s.sizeofs[i] == 0 && s.sizeofs[j] != 0 {
+ return true
+ }
+ if s.sizeofs[j] == 0 && s.sizeofs[i] != 0 {
+ return false
+ }
+
+ // Next, place more tightly aligned objects before less tightly aligned objects.
+ if s.alignofs[i] != s.alignofs[j] {
+ return s.alignofs[i] > s.alignofs[j]
+ }
+
+ // Lastly, order by size.
+ if s.sizeofs[i] != s.sizeofs[j] {
+ return s.sizeofs[i] > s.sizeofs[j]
+ }
+
+ return false
+}
+
+// Code below based on go/types.StdSizes.
+
+type gcSizes struct {
+ WordSize int64
+ MaxAlign int64
+}
+
+func (s *gcSizes) Alignof(T types.Type) int64 {
+ // NOTE: On amd64, complex64 is 8 byte aligned,
+ // even though float32 is only 4 byte aligned.
+
+ // For arrays and structs, alignment is defined in terms
+ // of alignment of the elements and fields, respectively.
+ switch t := T.Underlying().(type) {
+ case *types.Array:
+ // spec: "For a variable x of array type: unsafe.Alignof(x)
+ // is the same as unsafe.Alignof(x[0]), but at least 1."
+ return s.Alignof(t.Elem())
+ case *types.Struct:
+ // spec: "For a variable x of struct type: unsafe.Alignof(x)
+ // is the largest of the values unsafe.Alignof(x.f) for each
+ // field f of x, but at least 1."
+ max := int64(1)
+ for i, nf := 0, t.NumFields(); i < nf; i++ {
+ if a := s.Alignof(t.Field(i).Type()); a > max {
+ max = a
+ }
+ }
+ return max
+ }
+ a := s.Sizeof(T) // may be 0
+ // spec: "For a variable x of any type: unsafe.Alignof(x) is at least 1."
+ if a < 1 {
+ return 1
+ }
+ if a > s.MaxAlign {
+ return s.MaxAlign
+ }
+ return a
+}
+
+var basicSizes = [...]byte{
+ types.Bool: 1,
+ types.Int8: 1,
+ types.Int16: 2,
+ types.Int32: 4,
+ types.Int64: 8,
+ types.Uint8: 1,
+ types.Uint16: 2,
+ types.Uint32: 4,
+ types.Uint64: 8,
+ types.Float32: 4,
+ types.Float64: 8,
+ types.Complex64: 8,
+ types.Complex128: 16,
+}
+
+func (s *gcSizes) Sizeof(T types.Type) int64 {
+ switch t := T.Underlying().(type) {
+ case *types.Basic:
+ k := t.Kind()
+ if int(k) < len(basicSizes) {
+ if s := basicSizes[k]; s > 0 {
+ return int64(s)
+ }
+ }
+ if k == types.String {
+ return s.WordSize * 2
+ }
+ case *types.Array:
+ n := t.Len()
+ if n == 0 {
+ return 0
+ }
+ a := s.Alignof(t.Elem())
+ z := s.Sizeof(t.Elem())
+ return align(z, a)*(n-1) + z
+ case *types.Slice:
+ return s.WordSize * 3
+ case *types.Struct:
+ nf := t.NumFields()
+ if nf == 0 {
+ return 0
+ }
+
+ var o int64
+ max := int64(1)
+ for i := 0; i < nf; i++ {
+ ft := t.Field(i).Type()
+ a, sz := s.Alignof(ft), s.Sizeof(ft)
+ if a > max {
+ max = a
+ }
+ if i == nf-1 && sz == 0 && o != 0 {
+ sz = 1
+ }
+ o = align(o, a) + sz
+ }
+ return align(o, max)
+ case *types.Interface:
+ return s.WordSize * 2
+ }
+ return s.WordSize // catch-all
+}
+
+// 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
+}