1 module bamboo.legacy_hash; 2 3 import std.conv; 4 import bamboo.hashgen; 5 import bamboo.types; 6 7 /// Hashes a module. 8 void hashModule(ref HashGenerator gen, Module mod) 9 { 10 gen.addInt(1); 11 gen.addInt(mod.structs.length + mod.classes.length); 12 13 foreach (type; mod.typesById) 14 { 15 if (type.syntaxKind == SyntaxKind.ClassDeclaration) 16 { 17 hashClass(gen, cast(ClassDeclaration) type); 18 } 19 else 20 { 21 hashStruct(gen, cast(StructDeclaration) type); 22 } 23 } 24 } 25 26 private: 27 28 /// Represents legacy types supported by Panda3D. 29 enum LegacyType 30 { 31 int8, 32 int16, 33 int32, 34 int64, 35 36 uint8, 37 uint16, 38 uint32, 39 uint64, 40 41 float64, 42 43 string_, 44 blob, 45 46 char_ = 19, 47 invalid = 20 48 } 49 50 void hashClass(ref HashGenerator gen, ClassDeclaration type) 51 { 52 gen.addString(type.symbol); 53 54 // Bug(?) in astron: doesn't get the full class graph. 55 version (broken_parents) 56 { 57 gen.addInt(type.parents.length != 0 ? 1 : 0); 58 if (type.parents.length != 0) 59 { 60 gen.addInt(type.parents[0].id); 61 } 62 } 63 else 64 { 65 gen.addInt(type.parents.length); 66 foreach (parent; type.parents) 67 { 68 gen.addInt(parent.id); 69 } 70 } 71 72 if (type.hasConstructor) 73 { 74 hashField(gen, type.constructor); 75 } 76 77 gen.addInt(type.fields.length); 78 foreach (field; type.fields) 79 { 80 hashField(gen, field); 81 } 82 } 83 84 void hashStruct(ref HashGenerator gen, StructDeclaration type) 85 { 86 gen.addString(type.symbol); 87 gen.addInt(1); 88 gen.addInt(0); 89 90 gen.addInt(type.parameters.length); 91 92 foreach (field; type.parameters) 93 { 94 hashField(gen, field); 95 } 96 } 97 98 void hashField(ref HashGenerator gen, FieldDeclaration field) 99 { 100 if (auto molecular = cast(MolecularField) field) 101 { 102 gen.addString(molecular.symbol); 103 gen.addInt(molecular.id); 104 105 gen.addInt(molecular.references.length); 106 107 foreach (decl; molecular.references) 108 { 109 hashField(gen, decl); 110 } 111 return; 112 } 113 114 if (auto atomic = cast(AtomicField) field) 115 { 116 gen.addString(atomic.symbol); 117 gen.addInt(atomic.id); 118 119 gen.addInt(atomic.parameters.length); 120 foreach (parameter; atomic.parameters) 121 { 122 hashParameter(gen, parameter); 123 } 124 125 hashKeywords(gen, atomic.keywords); 126 return; 127 } 128 if (auto para = cast(ParameterField) field) 129 { 130 if (para.keywords.length > 0) 131 { 132 hashKeywords(gen, para.keywords); 133 } 134 hashLegacyType(gen, para.parameter); 135 return; 136 } 137 138 assert(0); 139 } 140 141 void hashParameter(ref HashGenerator gen, Parameter parameter) 142 { 143 hashLegacyType(gen, parameter); 144 } 145 146 void hashKeywords(ref HashGenerator gen, KeywordList list) 147 { 148 // dfmt off 149 immutable int[string] legacyKeywords = [ 150 "required": 0x0001, 151 "broadcast": 0x0002, 152 "ownrecv": 0x0004, 153 "ram": 0x0008, 154 "db": 0x0010, 155 "clsend": 0x0020, 156 "clrecv": 0x0040, 157 "ownsend": 0x0080, 158 "airecv": 0x0100, 159 "": 0, 160 ]; 161 // dfmt on 162 163 int flags; 164 foreach (keyword; list.keywords) 165 { 166 if (auto value = keyword in legacyKeywords) 167 { 168 flags |= *value; 169 } 170 else 171 { 172 // detected nonlegacy keyword. 173 flags = ~0; 174 break; 175 } 176 } 177 178 // Everything is a legacy keyword. 179 if (flags != ~0) 180 { 181 gen.addInt(flags); 182 } 183 else 184 { 185 gen.addInt(list.keywords.length); 186 foreach (keyword; list.keywords) 187 { 188 gen.addString(keyword); 189 } 190 } 191 } 192 193 void hashLegacyType(T)(ref HashGenerator gen, T thing) 194 { 195 LegacyType toLegacy(Type type) 196 { 197 switch (type) with (Type) 198 { 199 case blob: 200 case varblob: 201 return LegacyType.blob; 202 case string_: 203 case varstring: 204 return LegacyType.string_; 205 case int8: 206 return LegacyType.int8; 207 case int16: 208 return LegacyType.int16; 209 case int32: 210 return LegacyType.int32; 211 case int64: 212 return LegacyType.int64; 213 case uint8: 214 return LegacyType.uint8; 215 case uint16: 216 return LegacyType.uint16; 217 case uint32: 218 return LegacyType.uint32; 219 case uint64: 220 return LegacyType.uint64; 221 case char_: 222 return LegacyType.char_; 223 case float64: 224 return LegacyType.float64; 225 226 case float32: 227 default: 228 return LegacyType.invalid; 229 } 230 } 231 232 Type type; 233 LegacyType legacy; 234 235 static if (is(typeof(thing.type) == Type)) 236 { 237 type = thing.type; 238 } 239 else static if (is(typeof(thing.parameterType) == Type)) 240 { 241 type = thing.parameterType; 242 } 243 else 244 { 245 static assert(0, "Cannot deduce type from " ~ T.stringof); 246 } 247 248 legacy = toLegacy(type); 249 250 switch (type) with (Type) 251 { 252 case struct_: 253 if (auto structPara = cast(StructParameter) thing) 254 { 255 if (auto cls = cast(ClassDeclaration) structPara.type) 256 { 257 hashClass(gen, cls); 258 } 259 else if (auto strct = cast(StructDeclaration) structPara.type) 260 { 261 hashStruct(gen, strct); 262 } 263 else 264 { 265 assert(0, "StructParameter is invalid type: " ~ structPara.type.symbol); 266 } 267 } 268 else if (auto cls = cast(ClassDeclaration) thing) 269 { 270 hashClass(gen, cls); 271 } 272 else if (auto strct = cast(StructDeclaration) thing) 273 { 274 hashStruct(gen, strct); 275 } 276 else 277 { 278 assert(0, "Error: " ~ typeid(thing).to!string ~ " is not valid for this parameter."); 279 } 280 break; 281 case array: 282 case vararray: 283 auto array = cast(ArrayParameter) thing; 284 hashLegacyType(gen, array.element); 285 286 if (array.hasRange) 287 { 288 ArrayRange range = array.size; 289 gen.addInt(1); 290 gen.addInt(range.minLength); 291 gen.addInt(range.maxLength); 292 } 293 break; 294 case blob: 295 case varblob: 296 auto blob = cast(SizedParameter) thing; 297 // TODO: Sometimes this should be uint8? 298 gen.addInt(LegacyType.blob); 299 gen.addInt(1); 300 301 if (blob.hasRange) 302 { 303 SizeConstraint range = blob.size; 304 gen.addInt(1); 305 gen.addInt(range.minSize); 306 gen.addInt(range.maxSize); 307 } 308 break; 309 case string_: 310 case varstring: 311 auto strblob = cast(SizedParameter) thing; 312 // TODO: Sometimes this should be char? 313 gen.addInt(LegacyType.string_); 314 gen.addInt(1); 315 316 if (strblob.hasRange) 317 { 318 SizeConstraint range = strblob.size; 319 gen.addInt(1); 320 gen.addInt(range.minSize); 321 gen.addInt(range.maxSize); 322 } 323 break; 324 325 case int8: 326 case int16: 327 case int32: 328 case int64: 329 case uint8: 330 case uint16: 331 case uint32: 332 case uint64: 333 case char_: 334 auto intpara = cast(NumericParameter) thing; 335 gen.addInt(legacy); 336 hashIntType(gen, intpara); 337 break; 338 case float64: 339 auto floatpara = cast(NumericParameter) thing; 340 gen.addInt(LegacyType.float64); 341 gen.addInt(floatpara.divisor); 342 if (floatpara.hasModulus) 343 { 344 gen.addInt(cast(int)(floatpara.modulus * floatpara.divisor)); 345 } 346 if (floatpara.hasRange) 347 { 348 gen.addInt(1); 349 gen.addInt(cast(int)(floatpara.range.min * floatpara.divisor)); 350 gen.addInt(cast(int)(floatpara.range.max * floatpara.divisor)); 351 } 352 break; 353 case float32: 354 case invalid: 355 break; 356 default: 357 assert(0); 358 } 359 } 360 361 void hashIntType(ref HashGenerator gen, NumericParameter numeric) 362 { 363 import std.math : floor; 364 365 gen.addInt(numeric.divisor); 366 if (numeric.hasModulus) 367 { 368 uint modulus = cast(uint) floor(numeric.modulus * numeric.divisor + 0.5); 369 gen.addInt(modulus); 370 } 371 if (numeric.hasRange) 372 { 373 gen.addInt(1); 374 gen.addInt(cast(int)(numeric.range.min * numeric.divisor)); 375 gen.addInt(cast(int)(numeric.range.max * numeric.divisor)); 376 } 377 }