Commit | Line | Data |
---|---|---|
866e5b51 | 1 | /******************************************************************************* |
60ae41e1 | 2 | * Copyright (c) 2011, 2014 Ericsson, Ecole Polytechnique de Montreal and others |
866e5b51 FC |
3 | * |
4 | * All rights reserved. This program and the accompanying materials are made | |
5 | * available under the terms of the Eclipse Public License v1.0 which | |
6 | * accompanies this distribution, and is available at | |
7 | * http://www.eclipse.org/legal/epl-v10.html | |
8 | * | |
9 | * Contributors: Matthew Khouzam - Initial API and implementation | |
10 | * Contributors: Simon Marchi - Initial API and implementation | |
11 | *******************************************************************************/ | |
12 | ||
f357bcd4 | 13 | package org.eclipse.tracecompass.ctf.core.event.types; |
866e5b51 | 14 | |
5db5a3a4 AM |
15 | import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull; |
16 | ||
e00e6663 | 17 | import java.util.ArrayList; |
2db699c2 | 18 | import java.util.Iterator; |
a4fa4e36 | 19 | import java.util.LinkedHashMap; |
e00e6663 | 20 | import java.util.List; |
0594c61c | 21 | import java.util.Map; |
e00e6663 | 22 | import java.util.Map.Entry; |
94c255ef | 23 | import java.util.regex.Pattern; |
866e5b51 | 24 | |
a4fa4e36 | 25 | import org.eclipse.jdt.annotation.NonNull; |
2db699c2 | 26 | import org.eclipse.jdt.annotation.Nullable; |
680f9173 | 27 | import org.eclipse.tracecompass.ctf.core.CTFException; |
f357bcd4 AM |
28 | import org.eclipse.tracecompass.ctf.core.event.io.BitBuffer; |
29 | import org.eclipse.tracecompass.ctf.core.event.scope.IDefinitionScope; | |
fbe6fa6f | 30 | import org.eclipse.tracecompass.ctf.core.event.scope.ILexicalScope; |
a4fa4e36 | 31 | |
866e5b51 | 32 | /** |
d37aaa7f | 33 | * A CTF structure declaration. |
77fdc5df | 34 | * |
d37aaa7f FC |
35 | * A structure is similar to a C structure, it is a compound data type that |
36 | * contains other datatypes in fields. they are stored in an hashmap and indexed | |
37 | * by names which are strings. | |
38 | * | |
39 | * @version 1.0 | |
40 | * @author Matthew Khouzam | |
41 | * @author Simon Marchi | |
866e5b51 | 42 | */ |
a4fa4e36 | 43 | public class StructDeclaration extends Declaration { |
866e5b51 FC |
44 | |
45 | // ------------------------------------------------------------------------ | |
46 | // Attributes | |
47 | // ------------------------------------------------------------------------ | |
48 | ||
a4fa4e36 | 49 | /** linked list of field names. So fieldName->fieldValue */ |
367e2932 | 50 | private final @NonNull Map<@NonNull String, IDeclaration> fFieldMap = new LinkedHashMap<>(); |
a4fa4e36 MK |
51 | |
52 | /** maximum bit alignment */ | |
53 | private long fMaxAlign; | |
866e5b51 FC |
54 | |
55 | // ------------------------------------------------------------------------ | |
56 | // Constructors | |
57 | // ------------------------------------------------------------------------ | |
58 | ||
9ac2eb62 MK |
59 | /** |
60 | * The struct declaration, add fields later | |
61 | * | |
62 | * @param align | |
63 | * the minimum alignment of the struct. (if a struct is 8bit | |
64 | * aligned and has a 32 bit aligned field, the struct becomes 32 | |
65 | * bit aligned. | |
66 | */ | |
2b7f6f09 | 67 | public StructDeclaration(long align) { |
a4fa4e36 | 68 | fMaxAlign = Math.max(align, 1); |
a4fa4e36 MK |
69 | } |
70 | ||
866e5b51 FC |
71 | // ------------------------------------------------------------------------ |
72 | // Getters/Setters/Predicates | |
73 | // ------------------------------------------------------------------------ | |
74 | ||
9ac2eb62 MK |
75 | /** |
76 | * Get current alignment | |
a4fa4e36 | 77 | * |
9ac2eb62 MK |
78 | * @return the alignment of the struct and all its fields |
79 | */ | |
2b7f6f09 | 80 | public long getMaxAlign() { |
a4fa4e36 | 81 | return fMaxAlign; |
866e5b51 FC |
82 | } |
83 | ||
9ac2eb62 MK |
84 | /** |
85 | * Query if the struct has a given field | |
a4fa4e36 MK |
86 | * |
87 | * @param name | |
88 | * the name of the field, scopeless please | |
9ac2eb62 MK |
89 | * @return does the field exist? |
90 | */ | |
866e5b51 | 91 | public boolean hasField(String name) { |
a4fa4e36 | 92 | return fFieldMap.containsKey(name); |
866e5b51 FC |
93 | } |
94 | ||
9ac2eb62 | 95 | /** |
2db699c2 | 96 | * Get the fields of the struct as a map. |
a4fa4e36 | 97 | * |
2db699c2 | 98 | * @return a Map of the fields (key is the name) |
9ac2eb62 | 99 | */ |
0594c61c | 100 | public Map<String, IDeclaration> getFields() { |
a4fa4e36 | 101 | return fFieldMap; |
866e5b51 FC |
102 | } |
103 | ||
2db699c2 AM |
104 | /** |
105 | * Get the field declaration corresponding to a field name. | |
106 | * | |
107 | * @param fieldName | |
108 | * The field name | |
109 | * @return The declaration of the field, or null if there is no such field. | |
2db699c2 AM |
110 | */ |
111 | @Nullable | |
112 | public IDeclaration getField(String fieldName) { | |
113 | return fFieldMap.get(fieldName); | |
114 | } | |
115 | ||
9ac2eb62 | 116 | /** |
a4fa4e36 MK |
117 | * Gets the field list. Very important since the map of fields does not |
118 | * retain the order of the fields. | |
119 | * | |
9ac2eb62 MK |
120 | * @return the field list. |
121 | */ | |
367e2932 | 122 | public @NonNull Iterable<@NonNull String> getFieldsList() { |
a4fa4e36 | 123 | return fFieldMap.keySet(); |
866e5b51 FC |
124 | } |
125 | ||
fd74e6c1 MK |
126 | @Override |
127 | public long getAlignment() { | |
a4fa4e36 MK |
128 | return this.fMaxAlign; |
129 | } | |
130 | ||
a4fa4e36 MK |
131 | @Override |
132 | public int getMaximumSize() { | |
133 | int maxSize = 0; | |
2db699c2 AM |
134 | for (IDeclaration field : fFieldMap.values()) { |
135 | maxSize += field.getMaximumSize(); | |
a4fa4e36 MK |
136 | } |
137 | return Math.min(maxSize, Integer.MAX_VALUE); | |
fd74e6c1 | 138 | } |
9ac2eb62 | 139 | |
866e5b51 FC |
140 | // ------------------------------------------------------------------------ |
141 | // Operations | |
142 | // ------------------------------------------------------------------------ | |
143 | ||
144 | @Override | |
145 | public StructDefinition createDefinition(IDefinitionScope definitionScope, | |
680f9173 | 146 | String fieldName, BitBuffer input) throws CTFException { |
a4fa4e36 | 147 | alignRead(input); |
2db699c2 | 148 | final Definition[] myFields = new Definition[fFieldMap.size()]; |
94c255ef MK |
149 | StructDefinition structDefinition = null; |
150 | if (definitionScope == null) { | |
151 | InternalDef localDefinitionScope = new InternalDef(null, null); | |
152 | structDefinition = new StructDefinition(this, localDefinitionScope, fieldName, myFields); | |
153 | localDefinitionScope.setDefinition(structDefinition); | |
154 | } else { | |
155 | structDefinition = new StructDefinition(this, definitionScope, fieldName, myFields); | |
156 | } | |
cc575f45 | 157 | fillStruct(input, myFields, structDefinition); |
a4fa4e36 | 158 | return structDefinition; |
866e5b51 FC |
159 | } |
160 | ||
70f60307 | 161 | /** |
6c7592e1 MK |
162 | * Create a definition from this declaration. This is a faster constructor |
163 | * as it has a lexical scope and this does not need to look it up. | |
70f60307 MK |
164 | * |
165 | * @param definitionScope | |
6c7592e1 MK |
166 | * the definition scope, the parent where the definition will be |
167 | * placed | |
70f60307 | 168 | * @param fieldScope |
6c7592e1 | 169 | * the scope of the definition |
70f60307 | 170 | * @param input |
6c7592e1 MK |
171 | * a bitbuffer to read from |
172 | * @return a reference to the definition | |
680f9173 | 173 | * @throws CTFException |
6c7592e1 | 174 | * error in reading |
fbe6fa6f | 175 | * @since 1.0 |
70f60307 MK |
176 | */ |
177 | public StructDefinition createDefinition(IDefinitionScope definitionScope, | |
680f9173 | 178 | ILexicalScope fieldScope, @NonNull BitBuffer input) throws CTFException { |
70f60307 MK |
179 | alignRead(input); |
180 | final Definition[] myFields = new Definition[fFieldMap.size()]; | |
5db5a3a4 | 181 | |
94c255ef | 182 | StructDefinition structDefinition = new StructDefinition(this, definitionScope, |
5db5a3a4 | 183 | fieldScope, fieldScope.getName(), checkNotNull(fFieldMap.keySet()), myFields); |
cc575f45 | 184 | fillStruct(input, myFields, structDefinition); |
70f60307 MK |
185 | return structDefinition; |
186 | } | |
187 | ||
9ac2eb62 MK |
188 | /** |
189 | * Add a field to the struct | |
a4fa4e36 MK |
190 | * |
191 | * @param name | |
192 | * the name of the field, scopeless | |
193 | * @param declaration | |
194 | * the declaration of the field | |
9ac2eb62 | 195 | */ |
367e2932 | 196 | public void addField(@NonNull String name, IDeclaration declaration) { |
a4fa4e36 MK |
197 | fFieldMap.put(name, declaration); |
198 | fMaxAlign = Math.max(fMaxAlign, declaration.getAlignment()); | |
866e5b51 FC |
199 | } |
200 | ||
680f9173 | 201 | private void fillStruct(@NonNull BitBuffer input, final Definition[] myFields, StructDefinition structDefinition) throws CTFException { |
cc575f45 MK |
202 | Iterator<Map.Entry<String, IDeclaration>> iter = fFieldMap.entrySet().iterator(); |
203 | for (int i = 0; i < fFieldMap.size(); i++) { | |
204 | Map.Entry<String, IDeclaration> entry = iter.next(); | |
5db5a3a4 AM |
205 | /* We should not have inserted null keys... */ |
206 | String key = checkNotNull(entry.getKey()); | |
207 | myFields[i] = entry.getValue().createDefinition(structDefinition, key, input); | |
cc575f45 MK |
208 | } |
209 | } | |
210 | ||
94c255ef MK |
211 | /** |
212 | * Special constructor for fields | |
213 | * | |
214 | * @param eventHeaderDef | |
215 | * the event header, used for scopes | |
216 | * @param definitionScope | |
217 | * the definition scope, in this case, the trace | |
218 | * @param fields | |
219 | * event fields | |
220 | * @param input | |
221 | * the input {@link BitBuffer} | |
222 | * @return the fields definition | |
223 | * @throws CTFException | |
224 | * something went wrong | |
0336f981 | 225 | * @since 1.1 |
94c255ef MK |
226 | */ |
227 | public StructDefinition createFieldDefinition(ICompositeDefinition eventHeaderDef, IDefinitionScope definitionScope, ILexicalScope fields, @NonNull BitBuffer input) throws CTFException { | |
228 | alignRead(input); | |
229 | final Definition[] myFields = new Definition[fFieldMap.size()]; | |
230 | IDefinitionScope merged = definitionScope; | |
231 | if (eventHeaderDef != null) { | |
232 | merged = new InternalDef(definitionScope, eventHeaderDef); | |
233 | } | |
234 | StructDefinition structDefinition = new StructDefinition(this, merged, | |
235 | fields, fields.getName(), checkNotNull(fFieldMap.keySet()), myFields); | |
236 | if (merged instanceof InternalDef) { | |
237 | InternalDef internalDef = (InternalDef) merged; | |
238 | internalDef.setDefinition(structDefinition); | |
239 | } | |
240 | fillStruct(input, myFields, structDefinition); | |
241 | return structDefinition; | |
242 | } | |
243 | ||
244 | private static final Pattern EVENT_HEADER = Pattern.compile(ILexicalScope.EVENT_HEADER.getPath().replaceAll("\\.", "\\\\.") + "\\."); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ | |
245 | ||
5b341dc8 | 246 | static class InternalDef implements IDefinitionScope { |
94c255ef MK |
247 | |
248 | private final ICompositeDefinition fEventHeaderDef; | |
249 | private final IDefinitionScope fTraceDef; | |
250 | private StructDefinition fDefinition; | |
251 | ||
252 | public InternalDef(IDefinitionScope definitionScope, ICompositeDefinition eventHeaderDef) { | |
253 | fTraceDef = definitionScope; | |
254 | fEventHeaderDef = eventHeaderDef; | |
255 | } | |
256 | ||
257 | @Override | |
258 | public ILexicalScope getScopePath() { | |
259 | return ILexicalScope.EVENT; | |
260 | } | |
261 | ||
262 | @Override | |
263 | public IDefinition lookupDefinition(String lookupPath) { | |
264 | IDefinition lookupDefinition = null; | |
265 | if (fTraceDef != null) { | |
266 | lookupDefinition = fTraceDef.lookupDefinition(lookupPath); | |
267 | } | |
268 | if (lookupDefinition == null) { | |
269 | if (fEventHeaderDef != null) { | |
270 | String[] paths = EVENT_HEADER.split(lookupPath); | |
271 | if (paths.length > 1) { | |
272 | String[] childLookup = paths[1].split("\\."); //$NON-NLS-1$ | |
273 | return getRecursiveDef(fEventHeaderDef.getDefinition(childLookup[0]), childLookup, 1); | |
274 | } | |
275 | if (fDefinition != null) { | |
276 | return fDefinition.lookupDefinition(lookupPath); | |
277 | } | |
278 | } | |
279 | } | |
280 | return lookupDefinition; | |
281 | } | |
282 | ||
5b341dc8 MK |
283 | public IDefinition lookupDefinitionBreakLoop(String lookupPath) { |
284 | IDefinition lookupDefinition = null; | |
285 | if (fTraceDef != null) { | |
286 | lookupDefinition = fTraceDef.lookupDefinition(lookupPath); | |
287 | } | |
288 | if (lookupDefinition == null) { | |
289 | if (fEventHeaderDef != null) { | |
290 | String[] paths = EVENT_HEADER.split(lookupPath); | |
291 | if (paths.length > 1) { | |
292 | String[] childLookup = paths[1].split("\\."); //$NON-NLS-1$ | |
293 | return getRecursiveDef(fEventHeaderDef.getDefinition(childLookup[0]), childLookup, 1); | |
294 | } | |
295 | } | |
296 | } | |
297 | return lookupDefinition; | |
298 | } | |
299 | ||
94c255ef MK |
300 | private IDefinition getRecursiveDef(Definition definition, String[] childLookup, int i) { |
301 | if (i == childLookup.length) { | |
302 | return definition; | |
303 | } | |
304 | if (definition instanceof ICompositeDefinition) { | |
305 | ICompositeDefinition compositeDefinition = (ICompositeDefinition) definition; | |
306 | return getRecursiveDef(compositeDefinition.getDefinition(childLookup[i]), childLookup, i + 1); | |
307 | } | |
308 | return null; | |
309 | } | |
310 | ||
311 | public void setDefinition(StructDefinition definition) { | |
312 | fDefinition = definition; | |
313 | } | |
314 | ||
315 | } | |
316 | ||
866e5b51 FC |
317 | @Override |
318 | public String toString() { | |
319 | /* Only used for debugging */ | |
66aa25f0 MK |
320 | StringBuilder sb = new StringBuilder(); |
321 | sb.append("[declaration] struct["); //$NON-NLS-1$ | |
322 | for (Entry<String, IDeclaration> field : fFieldMap.entrySet()) { | |
323 | sb.append(field.getKey()).append(':').append(field.getValue()); | |
324 | } | |
325 | sb.append(']'); | |
326 | return sb.toString(); | |
866e5b51 FC |
327 | } |
328 | ||
4dd0eaed MK |
329 | @Override |
330 | public int hashCode() { | |
331 | final int prime = 31; | |
332 | int result = 1; | |
e00e6663 MK |
333 | for (Entry<String, IDeclaration> field : fFieldMap.entrySet()) { |
334 | result = prime * result + field.getKey().hashCode(); | |
335 | result = prime * result + field.getValue().hashCode(); | |
336 | } | |
a4fa4e36 | 337 | result = (prime * result) + (int) (fMaxAlign ^ (fMaxAlign >>> 32)); |
4dd0eaed MK |
338 | return result; |
339 | } | |
340 | ||
4dd0eaed MK |
341 | @Override |
342 | public boolean equals(Object obj) { | |
343 | if (this == obj) { | |
344 | return true; | |
345 | } | |
346 | if (obj == null) { | |
347 | return false; | |
348 | } | |
349 | if (!(obj instanceof StructDeclaration)) { | |
350 | return false; | |
351 | } | |
352 | StructDeclaration other = (StructDeclaration) obj; | |
e00e6663 | 353 | if (fFieldMap.size() != other.fFieldMap.size()) { |
4dd0eaed MK |
354 | return false; |
355 | } | |
e00e6663 MK |
356 | |
357 | List<String> localFieldNames = new ArrayList<>(); | |
358 | localFieldNames.addAll(fFieldMap.keySet()); | |
359 | ||
360 | List<IDeclaration> localDecs = new ArrayList<>(); | |
361 | localDecs.addAll(fFieldMap.values()); | |
362 | ||
363 | List<String> otherFieldNames = new ArrayList<>(); | |
364 | otherFieldNames.addAll(other.fFieldMap.keySet()); | |
365 | ||
366 | List<IDeclaration> otherDecs = new ArrayList<>(); | |
367 | otherDecs.addAll(other.fFieldMap.values()); | |
368 | ||
94c255ef | 369 | // check fields in order |
e00e6663 MK |
370 | for (int i = 0; i < fFieldMap.size(); i++) { |
371 | if ((!localFieldNames.get(i).equals(otherFieldNames.get(i))) || | |
372 | (!otherDecs.get(i).equals(localDecs.get(i)))) { | |
373 | return false; | |
374 | } | |
375 | } | |
376 | ||
a4fa4e36 | 377 | if (fMaxAlign != other.fMaxAlign) { |
4dd0eaed MK |
378 | return false; |
379 | } | |
380 | return true; | |
381 | } | |
382 | ||
66aa25f0 MK |
383 | @Override |
384 | public boolean isBinaryEquivalent(IDeclaration obj) { | |
385 | if (this == obj) { | |
386 | return true; | |
387 | } | |
388 | if (obj == null) { | |
389 | return false; | |
390 | } | |
391 | if (!(obj instanceof StructDeclaration)) { | |
392 | return false; | |
393 | } | |
394 | StructDeclaration other = (StructDeclaration) obj; | |
395 | if (fFieldMap.size() != other.fFieldMap.size()) { | |
396 | return false; | |
397 | } | |
398 | List<IDeclaration> localDecs = new ArrayList<>(); | |
399 | localDecs.addAll(fFieldMap.values()); | |
400 | List<IDeclaration> otherDecs = new ArrayList<>(); | |
401 | otherDecs.addAll(other.fFieldMap.values()); | |
402 | for (int i = 0; i < fFieldMap.size(); i++) { | |
403 | if (!otherDecs.get(i).isBinaryEquivalent(localDecs.get(i))) { | |
404 | return false; | |
405 | } | |
406 | } | |
407 | ||
408 | if (fMaxAlign != other.fMaxAlign) { | |
409 | return false; | |
410 | } | |
411 | return true; | |
412 | } | |
413 | ||
866e5b51 | 414 | } |