github.com/orijtech/structslop@v0.0.9-0.20230520012622-069644583b8b/sizes.go (about)

     1  // Copyright 2020 Orijtech, Inc. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package structslop
    16  
    17  import (
    18  	"go/types"
    19  )
    20  
    21  type sizes struct {
    22  	stdSizes types.Sizes
    23  	maxAlign int64
    24  }
    25  
    26  func (s *sizes) Offsetsof(fields []*types.Var) []int64 {
    27  	offsets := make([]int64, len(fields))
    28  	var o int64
    29  	for i, f := range fields {
    30  		a := s.Alignof(f.Type())
    31  		o = align(o, a)
    32  		offsets[i] = o
    33  		o += s.Sizeof(f.Type())
    34  	}
    35  	return offsets
    36  }
    37  
    38  func (s *sizes) Sizeof(T types.Type) int64 {
    39  	switch t := T.Underlying().(type) {
    40  	case *types.Array:
    41  		return t.Len() * s.Sizeof(t.Elem())
    42  	case *types.Struct:
    43  		nf := t.NumFields()
    44  		if nf == 0 {
    45  			return 0
    46  		}
    47  		o := int64(0)
    48  		max := int64(1)
    49  		for i := 0; i < nf; i++ {
    50  			ft := t.Field(i).Type()
    51  			a, sz := s.Alignof(ft), s.Sizeof(ft)
    52  			if a > max {
    53  				max = a
    54  			}
    55  			if i == nf-1 && sz == 0 && o != 0 {
    56  				sz = 1
    57  			}
    58  			o = align(o, a) + sz
    59  		}
    60  		return align(o, max)
    61  	}
    62  	return s.stdSizes.Sizeof(T)
    63  }
    64  
    65  func (s *sizes) Alignof(T types.Type) int64 {
    66  	switch t := T.Underlying().(type) {
    67  	case *types.Array:
    68  		return s.Alignof(t.Elem())
    69  	case *types.Struct:
    70  		max := int64(1)
    71  		for i, nf := 0, t.NumFields(); i < nf; i++ {
    72  			if a := s.Alignof(t.Field(i).Type()); a > max {
    73  				max = a
    74  			}
    75  		}
    76  		return max
    77  	case *types.Slice, *types.Interface, *types.Basic:
    78  		return s.stdSizes.Alignof(T)
    79  	}
    80  
    81  	// All other types.
    82  	a := s.Sizeof(T)
    83  	if a < 1 {
    84  		return 1
    85  	}
    86  	// complex{64,128} are aligned like [2]float{32,64}.
    87  	if isComplex(T) {
    88  		a /= 2
    89  	}
    90  	if a > s.maxAlign {
    91  		return s.maxAlign
    92  	}
    93  	return a
    94  }
    95  
    96  // align returns the smallest x >= subject such that x % target == 0.
    97  func align(subject, target int64) int64 {
    98  	x := subject + target - 1
    99  	return x - x%target
   100  }
   101  
   102  func isComplex(typ types.Type) bool {
   103  	t, ok := typ.Underlying().(*types.Basic)
   104  	return ok && t.Info()&types.IsComplex != 0
   105  }