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 }