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 }