github.com/Konstantin8105/c4go@v0.0.0-20240505174241-768bb1c65a51/types/sizeof.go (about)

     1  package types
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/Konstantin8105/c4go/program"
     8  	"github.com/Konstantin8105/c4go/util"
     9  )
    10  
    11  var sizeofStack []string
    12  
    13  // SizeOf returns the number of bytes for a type. This the same as using the
    14  // sizeof operator/function in C.
    15  func SizeOf(p *program.Program, cType string) (size int, err error) {
    16  	defer func() {
    17  		if err != nil {
    18  			err = fmt.Errorf("cannot determine sizeof : |%s|. err = %v", cType, err)
    19  		}
    20  	}()
    21  
    22  	// stack check
    23  	for i := range sizeofStack {
    24  		if sizeofStack[i] == cType {
    25  			return 0, fmt.Errorf("sizeof stack loop : %v", sizeofStack)
    26  		}
    27  	}
    28  	sizeofStack = append(sizeofStack, cType) // add to stack
    29  	defer func() {
    30  		// remove last element from stack
    31  		if len(sizeofStack) == 0 {
    32  			err = fmt.Errorf("cannot remove last sizeof stack element: slice is empty")
    33  			sizeofStack = make([]string, 10)
    34  		} else {
    35  			sizeofStack = sizeofStack[:len(sizeofStack)-1]
    36  		}
    37  	}()
    38  
    39  	// Remove keywords that do not effect the size.
    40  	cType = util.CleanCType(cType)
    41  	cType = strings.Replace(cType, "unsigned ", "", -1)
    42  	cType = strings.Replace(cType, "signed ", "", -1)
    43  
    44  	// FIXME: The pointer size will be different on different platforms. We
    45  	// should find out the correct size at runtime.
    46  	pointerSize := 8
    47  
    48  	if cType == "FILE *" || cType == "FILE" || cType == "struct _IO_FILE *" {
    49  		return pointerSize, nil
    50  	}
    51  
    52  	// Enum with name
    53  	if strings.HasPrefix(cType, "enum ") {
    54  		return SizeOf(p, "int")
    55  	}
    56  
    57  	// typedef int Integer;
    58  	if v, ok := p.GetBaseTypeOfTypedef(cType); ok {
    59  		return SizeOf(p, v)
    60  	}
    61  
    62  	// typedef Enum
    63  	if _, ok := p.EnumTypedefName[cType]; ok {
    64  		return SizeOf(p, "int")
    65  	}
    66  
    67  	// A structure will be the sum of its parts.
    68  	var isStruct, ok bool
    69  	var s *program.Struct
    70  	cType = util.GenerateCorrectType(cType)
    71  	if s, ok = p.Structs[cType]; ok {
    72  		isStruct = true
    73  	} else if s, ok = p.Structs["struct "+cType]; ok {
    74  		isStruct = true
    75  	} else if strings.HasPrefix(cType, "struct ") {
    76  		if s, ok = p.Structs[cType[len("struct "):]]; ok {
    77  			isStruct = true
    78  		}
    79  	}
    80  	if isStruct {
    81  		totalBytes := 0
    82  
    83  		// last parameter
    84  		// -1 - undefine
    85  		//  0 - value
    86  		// +1 - pointer
    87  
    88  		last := 0
    89  
    90  		for k := 0; k < len(s.FieldNames); k++ {
    91  			fn := s.FieldNames[k]
    92  			t := s.Fields[fn]
    93  
    94  			var bytes int
    95  			var err error
    96  
    97  			var new_par int = -1
    98  
    99  			switch f := t.(type) {
   100  			case string:
   101  				bytes, err = SizeOf(p, f)
   102  				if IsPointer(f, p) {
   103  					new_par = 1
   104  				} else {
   105  					new_par = 0
   106  				}
   107  
   108  			case *program.Struct:
   109  				bytes, err = SizeOf(p, f.Name)
   110  			}
   111  
   112  			if err != nil {
   113  				err = fmt.Errorf(
   114  					"cannot calculate `struct` sizeof for `%T`. bytes = '%v'. %v",
   115  					t, bytes, err)
   116  				return 0, err
   117  			}
   118  
   119  			if last != new_par {
   120  				var deli float64 = float64(totalBytes)/float64(pointerSize) + 1
   121  				totalBytes = int(deli) * pointerSize
   122  			}
   123  
   124  			totalBytes += bytes
   125  		}
   126  
   127  		// The size of a struct is rounded up to fit the size of the pointer of
   128  		// the OS.
   129  		if totalBytes%pointerSize != 0 {
   130  			totalBytes += pointerSize - (totalBytes % pointerSize)
   131  		}
   132  
   133  		return totalBytes, nil
   134  	}
   135  
   136  	// An union will be the max size of its parts.
   137  	var isUnion bool
   138  	cType = util.GenerateCorrectType(cType)
   139  	if _, ok := p.Unions[cType]; ok {
   140  		isUnion = true
   141  	} else if _, ok := p.Unions["union "+cType]; ok {
   142  		isUnion = true
   143  	}
   144  	if isUnion {
   145  		byteCount := 0
   146  
   147  		s, ok := p.Unions[cType]
   148  		if !ok {
   149  			return 0, fmt.Errorf("error in union")
   150  		}
   151  
   152  		for k := 0; k < len(s.FieldNames); k++ {
   153  			fn := s.FieldNames[k]
   154  			t := s.Fields[fn]
   155  
   156  			var bytes int
   157  
   158  			switch f := t.(type) {
   159  			case string:
   160  				bytes, err = SizeOf(p, f)
   161  
   162  			case *program.Struct:
   163  				bytes, err = SizeOf(p, f.Name)
   164  			}
   165  
   166  			if err != nil {
   167  				err = fmt.Errorf("cannot canculate `union` sizeof for `%T`. %v",
   168  					t, err)
   169  				return 0, err
   170  			}
   171  
   172  			if byteCount < bytes {
   173  				byteCount = bytes
   174  			}
   175  		}
   176  
   177  		// The size of an union is rounded up to fit the size of the pointer of
   178  		// the OS.
   179  		if byteCount%pointerSize != 0 {
   180  			byteCount += pointerSize - (byteCount % pointerSize)
   181  		}
   182  
   183  		return byteCount, nil
   184  	}
   185  
   186  	// Function pointers are one byte?
   187  	if strings.Contains(cType, "(") {
   188  		return 1, nil
   189  	}
   190  
   191  	if strings.HasSuffix(cType, "*") {
   192  		return pointerSize, nil
   193  	}
   194  
   195  	switch cType {
   196  	case "char", "void":
   197  		return 1, nil
   198  
   199  	case "short":
   200  		return 2, nil
   201  
   202  	case "int", "float", "long int":
   203  		return 4, nil
   204  
   205  	case "long", "long long", "long long int", "double":
   206  		return 8, nil
   207  
   208  	case "long double", "long long unsigned int":
   209  		return 16, nil
   210  	}
   211  
   212  	// definition type
   213  	if t, ok := program.DefinitionType[cType]; ok && cType != t {
   214  		return SizeOf(p, t)
   215  	}
   216  
   217  	// resolved type
   218  	conv := func(t string) (bytes int, ok bool) {
   219  		switch t {
   220  		case "byte", "int8", "uint8":
   221  			return 1, true
   222  
   223  		case "int16", "uint16":
   224  			return 2, true
   225  
   226  		case "int32", "uint32", "rune", "float32":
   227  			return 4, true
   228  
   229  		case "int64", "uint64", "float64", "complex64", "uintptr", "int", "uint":
   230  			return 8, true
   231  
   232  		case "complex128":
   233  			return 16, true
   234  		}
   235  		return -1, false
   236  	}
   237  	if t, ok := conv(cType); ok {
   238  		return t, nil
   239  	}
   240  	if r, err := ResolveType(p, cType); err != nil {
   241  		if t, ok := conv(r); ok {
   242  			return t, nil
   243  		}
   244  	}
   245  
   246  	// Get size for array types like: `base_type [count]`
   247  	arrayType, arraySize := GetArrayTypeAndSize(cType)
   248  	if arraySize <= 0 {
   249  		return 0, nil
   250  	}
   251  
   252  	baseSize, err := SizeOf(p, arrayType)
   253  	if err != nil {
   254  		return 0, fmt.Errorf("error in sizeof baseSize for `%v`",
   255  			arrayType)
   256  	}
   257  
   258  	return baseSize * arraySize, nil
   259  }