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