Home | History | Annotate | Download | only in types
      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 // This file implements Sizes.
      6 
      7 package types
      8 
      9 // Sizes defines the sizing functions for package unsafe.
     10 type Sizes interface {
     11 	// Alignof returns the alignment of a variable of type T.
     12 	// Alignof must implement the alignment guarantees required by the spec.
     13 	Alignof(T Type) int64
     14 
     15 	// Offsetsof returns the offsets of the given struct fields, in bytes.
     16 	// Offsetsof must implement the offset guarantees required by the spec.
     17 	Offsetsof(fields []*Var) []int64
     18 
     19 	// Sizeof returns the size of a variable of type T.
     20 	// Sizeof must implement the size guarantees required by the spec.
     21 	Sizeof(T Type) int64
     22 }
     23 
     24 // StdSizes is a convenience type for creating commonly used Sizes.
     25 // It makes the following simplifying assumptions:
     26 //
     27 //	- The size of explicitly sized basic types (int16, etc.) is the
     28 //	  specified size.
     29 //	- The size of strings and interfaces is 2*WordSize.
     30 //	- The size of slices is 3*WordSize.
     31 //	- The size of an array of n elements corresponds to the size of
     32 //	  a struct of n consecutive fields of the array's element type.
     33 //      - The size of a struct is the offset of the last field plus that
     34 //	  field's size. As with all element types, if the struct is used
     35 //	  in an array its size must first be aligned to a multiple of the
     36 //	  struct's alignment.
     37 //	- All other types have size WordSize.
     38 //	- Arrays and structs are aligned per spec definition; all other
     39 //	  types are naturally aligned with a maximum alignment MaxAlign.
     40 //
     41 // *StdSizes implements Sizes.
     42 //
     43 type StdSizes struct {
     44 	WordSize int64 // word size in bytes - must be >= 4 (32bits)
     45 	MaxAlign int64 // maximum alignment in bytes - must be >= 1
     46 }
     47 
     48 func (s *StdSizes) Alignof(T Type) int64 {
     49 	// For arrays and structs, alignment is defined in terms
     50 	// of alignment of the elements and fields, respectively.
     51 	switch t := T.Underlying().(type) {
     52 	case *Array:
     53 		// spec: "For a variable x of array type: unsafe.Alignof(x)
     54 		// is the same as unsafe.Alignof(x[0]), but at least 1."
     55 		return s.Alignof(t.elem)
     56 	case *Struct:
     57 		// spec: "For a variable x of struct type: unsafe.Alignof(x)
     58 		// is the largest of the values unsafe.Alignof(x.f) for each
     59 		// field f of x, but at least 1."
     60 		max := int64(1)
     61 		for _, f := range t.fields {
     62 			if a := s.Alignof(f.typ); a > max {
     63 				max = a
     64 			}
     65 		}
     66 		return max
     67 	case *Slice, *Interface:
     68 		// Multiword data structures are effectively structs
     69 		// in which each element has size WordSize.
     70 		return s.WordSize
     71 	case *Basic:
     72 		// Strings are like slices and interfaces.
     73 		if t.Info()&IsString != 0 {
     74 			return s.WordSize
     75 		}
     76 	}
     77 	a := s.Sizeof(T) // may be 0
     78 	// spec: "For a variable x of any type: unsafe.Alignof(x) is at least 1."
     79 	if a < 1 {
     80 		return 1
     81 	}
     82 	// complex{64,128} are aligned like [2]float{32,64}.
     83 	if isComplex(T) {
     84 		a /= 2
     85 	}
     86 	if a > s.MaxAlign {
     87 		return s.MaxAlign
     88 	}
     89 	return a
     90 }
     91 
     92 func (s *StdSizes) Offsetsof(fields []*Var) []int64 {
     93 	offsets := make([]int64, len(fields))
     94 	var o int64
     95 	for i, f := range fields {
     96 		a := s.Alignof(f.typ)
     97 		o = align(o, a)
     98 		offsets[i] = o
     99 		o += s.Sizeof(f.typ)
    100 	}
    101 	return offsets
    102 }
    103 
    104 var basicSizes = [...]byte{
    105 	Bool:       1,
    106 	Int8:       1,
    107 	Int16:      2,
    108 	Int32:      4,
    109 	Int64:      8,
    110 	Uint8:      1,
    111 	Uint16:     2,
    112 	Uint32:     4,
    113 	Uint64:     8,
    114 	Float32:    4,
    115 	Float64:    8,
    116 	Complex64:  8,
    117 	Complex128: 16,
    118 }
    119 
    120 func (s *StdSizes) Sizeof(T Type) int64 {
    121 	switch t := T.Underlying().(type) {
    122 	case *Basic:
    123 		assert(isTyped(T))
    124 		k := t.kind
    125 		if int(k) < len(basicSizes) {
    126 			if s := basicSizes[k]; s > 0 {
    127 				return int64(s)
    128 			}
    129 		}
    130 		if k == String {
    131 			return s.WordSize * 2
    132 		}
    133 	case *Array:
    134 		n := t.len
    135 		if n == 0 {
    136 			return 0
    137 		}
    138 		a := s.Alignof(t.elem)
    139 		z := s.Sizeof(t.elem)
    140 		return align(z, a)*(n-1) + z
    141 	case *Slice:
    142 		return s.WordSize * 3
    143 	case *Struct:
    144 		n := t.NumFields()
    145 		if n == 0 {
    146 			return 0
    147 		}
    148 		offsets := s.Offsetsof(t.fields)
    149 		return offsets[n-1] + s.Sizeof(t.fields[n-1].typ)
    150 	case *Interface:
    151 		return s.WordSize * 2
    152 	}
    153 	return s.WordSize // catch-all
    154 }
    155 
    156 // stdSizes is used if Config.Sizes == nil.
    157 var stdSizes = StdSizes{8, 8}
    158 
    159 func (conf *Config) alignof(T Type) int64 {
    160 	if s := conf.Sizes; s != nil {
    161 		if a := s.Alignof(T); a >= 1 {
    162 			return a
    163 		}
    164 		panic("Config.Sizes.Alignof returned an alignment < 1")
    165 	}
    166 	return stdSizes.Alignof(T)
    167 }
    168 
    169 func (conf *Config) offsetsof(T *Struct) []int64 {
    170 	var offsets []int64
    171 	if T.NumFields() > 0 {
    172 		// compute offsets on demand
    173 		if s := conf.Sizes; s != nil {
    174 			offsets = s.Offsetsof(T.fields)
    175 			// sanity checks
    176 			if len(offsets) != T.NumFields() {
    177 				panic("Config.Sizes.Offsetsof returned the wrong number of offsets")
    178 			}
    179 			for _, o := range offsets {
    180 				if o < 0 {
    181 					panic("Config.Sizes.Offsetsof returned an offset < 0")
    182 				}
    183 			}
    184 		} else {
    185 			offsets = stdSizes.Offsetsof(T.fields)
    186 		}
    187 	}
    188 	return offsets
    189 }
    190 
    191 // offsetof returns the offset of the field specified via
    192 // the index sequence relative to typ. All embedded fields
    193 // must be structs (rather than pointer to structs).
    194 func (conf *Config) offsetof(typ Type, index []int) int64 {
    195 	var o int64
    196 	for _, i := range index {
    197 		s := typ.Underlying().(*Struct)
    198 		o += conf.offsetsof(s)[i]
    199 		typ = s.fields[i].typ
    200 	}
    201 	return o
    202 }
    203 
    204 func (conf *Config) sizeof(T Type) int64 {
    205 	if s := conf.Sizes; s != nil {
    206 		if z := s.Sizeof(T); z >= 0 {
    207 			return z
    208 		}
    209 		panic("Config.Sizes.Sizeof returned a size < 0")
    210 	}
    211 	return stdSizes.Sizeof(T)
    212 }
    213 
    214 // align returns the smallest y >= x such that y % a == 0.
    215 func align(x, a int64) int64 {
    216 	y := x + a - 1
    217 	return y - y%a
    218 }
    219