blob: 3f43bf93a0eedb99a88cf8e3de9bec90a8a715d9 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2014 Ericsson
*
* All rights reserved. This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0 which
* accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Matthew Khouzam - Initial API and implementation
*******************************************************************************/
package org.eclipse.tracecompass.internal.ctf.core.event.types;
import java.util.List;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.tracecompass.ctf.core.CTFException;
import org.eclipse.tracecompass.ctf.core.event.io.BitBuffer;
import org.eclipse.tracecompass.ctf.core.event.scope.IDefinitionScope;
import org.eclipse.tracecompass.ctf.core.event.types.AbstractArrayDefinition;
import org.eclipse.tracecompass.ctf.core.event.types.CompoundDeclaration;
import org.eclipse.tracecompass.ctf.core.event.types.Definition;
import org.eclipse.tracecompass.ctf.core.event.types.IDeclaration;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
/**
* A CTF array declaration
*
* Arrays are fixed-length. Their length is declared in the type declaration
* within the meta-data. They contain an array of "inner type" elements, which
* can refer to any type not containing the type of the array being declared (no
* circular dependency). The length is the number of elements in an array.
*
* @author Matthew Khouzam
*/
public final class ArrayDeclaration extends CompoundDeclaration {
// ------------------------------------------------------------------------
// Attributes
// ------------------------------------------------------------------------
private final int fLength;
private final IDeclaration fElemType;
/**
* <pre>
* Cache where we can pre-generate the children names
* Key&colon; parent name
* Value&colon; children names
* ex: field &#8594; &lbrace;field&lbrack;0&rbrack;, field&lbrack;1&rbrack;, &hellip; field&lbrack;n&rbrack;&rbrace;
* </pre>
*
* TODO: investigate performance
*/
private final transient ArrayListMultimap<String, String> fChildrenNames = ArrayListMultimap.create();
// ------------------------------------------------------------------------
// Constructors
// ------------------------------------------------------------------------
/**
* Constructor
*
* @param length
* how many elements in the array
* @param elemType
* what type of element is in the array
*/
public ArrayDeclaration(int length, IDeclaration elemType) {
fLength = length;
fElemType = elemType;
}
// ------------------------------------------------------------------------
// Getters/Setters/Predicates
// ------------------------------------------------------------------------
@Override
public IDeclaration getElementType() {
return fElemType;
}
/**
* Get the length of the array
*
* @return the length of the array
*/
public int getLength() {
return fLength;
}
// ------------------------------------------------------------------------
// Operations
// ------------------------------------------------------------------------
@Override
public AbstractArrayDefinition createDefinition(@Nullable IDefinitionScope definitionScope,
@NonNull String fieldName, BitBuffer input) throws CTFException {
alignRead(input);
if (isAlignedBytes()) {
byte[] data = new byte[fLength];
if (input.getByteBuffer().remaining() < fLength) {
throw new CTFException("Buffer underflow"); //$NON-NLS-1$
}
input.get(data);
return new ByteArrayDefinition(this, definitionScope, fieldName, data);
}
@NonNull List<@NonNull Definition> definitions = read(input, definitionScope, fieldName);
return new ArrayDefinition(this, definitionScope, fieldName, definitions);
}
@Override
public String toString() {
/* Only used for debugging */
return "[declaration] array[" + Integer.toHexString(hashCode()) + ']'; //$NON-NLS-1$
}
private @NonNull List<@NonNull Definition> read(@NonNull BitBuffer input, @Nullable IDefinitionScope definitionScope, String fieldName) throws CTFException {
Builder<@NonNull Definition> definitions = new ImmutableList.Builder<>();
if (!fChildrenNames.containsKey(fieldName)) {
for (int i = 0; i < fLength; i++) {
fChildrenNames.put(fieldName, fieldName + '[' + i + ']');
}
}
List<String> elemNames = fChildrenNames.get(fieldName);
for (int i = 0; i < fLength; i++) {
String name = elemNames.get(i);
if (name == null) {
throw new IllegalStateException("Field " + fieldName + " has a nameless field"); //$NON-NLS-1$//$NON-NLS-2$
}
definitions.add(fElemType.createDefinition(definitionScope, name, input));
}
return definitions.build();
}
@Override
public int getMaximumSize() {
long val = (long) fLength * fElemType.getMaximumSize();
return (int) Math.min(Integer.MAX_VALUE, val);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + fElemType.hashCode();
result = prime * result + fLength;
return result;
}
@Override
public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
ArrayDeclaration other = (ArrayDeclaration) obj;
if (!fElemType.equals(other.fElemType)) {
return false;
}
return (fLength == other.fLength);
}
@Override
public boolean isBinaryEquivalent(@Nullable IDeclaration obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
ArrayDeclaration other = (ArrayDeclaration) obj;
if (!fElemType.isBinaryEquivalent(other.fElemType)) {
return false;
}
return (fLength == other.fLength);
}
}