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 }