blob: 108383ae6032febfbcac9f510b6ecdb4a93ae027 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011, 2014 Ericsson, Ecole Polytechnique de Montreal and others
*
* 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
* Contributors: Simon Marchi - Initial API and implementation
*******************************************************************************/
package org.eclipse.tracecompass.internal.ctf.core.trace;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.tracecompass.ctf.core.CTFException;
import org.eclipse.tracecompass.ctf.core.event.IEventDeclaration;
import org.eclipse.tracecompass.ctf.core.event.types.IDeclaration;
import org.eclipse.tracecompass.ctf.core.event.types.IEventHeaderDeclaration;
import org.eclipse.tracecompass.ctf.core.event.types.StructDeclaration;
import org.eclipse.tracecompass.ctf.core.trace.CTFStreamInput;
import org.eclipse.tracecompass.ctf.core.trace.CTFTrace;
import org.eclipse.tracecompass.ctf.core.trace.ICTFStream;
import org.eclipse.tracecompass.internal.ctf.core.event.EventDeclaration;
import org.eclipse.tracecompass.internal.ctf.core.event.metadata.ParseException;
import org.eclipse.tracecompass.internal.ctf.core.utils.SparseList;
/**
* <b><u>Stream</u></b>
* <p>
* Represents a stream in a trace.
*/
public class CTFStream implements ICTFStream {
// ------------------------------------------------------------------------
// Attributes
// ------------------------------------------------------------------------
/**
* The numerical ID of the stream
*/
private long fId = 0;
/**
* Declarations of the stream-specific structures
*/
private StructDeclaration fPacketContextDecl = null;
private IDeclaration fEventHeaderDecl = null;
private StructDeclaration fEventContextDecl = null;
/**
* The trace to which the stream belongs
*/
private CTFTrace fTrace = null;
/**
* Maps event ids to events
*/
private List<@Nullable IEventDeclaration> fEvents = new ArrayList<>();
private boolean fEventUnsetId = false;
private boolean fStreamIdSet = false;
/**
* The inputs associated to this stream
*/
private final Set<CTFStreamInput> fInputs = new HashSet<>();
// ------------------------------------------------------------------------
// Constructors
// ------------------------------------------------------------------------
/**
* Constructs a Stream that belongs to a Trace
*
* @param trace
* The trace to which belongs this stream.
*/
public CTFStream(CTFTrace trace) {
fTrace = trace;
}
// ------------------------------------------------------------------------
// Getters/Setters/Predicates
// ------------------------------------------------------------------------
/**
* Sets the id of a stream
*
* @param id
* the id of a stream
*/
public void setId(long id) {
fId = id;
fStreamIdSet = true;
}
@Override
public long getId() {
return fId;
}
@Override
public boolean isIdSet() {
return fStreamIdSet;
}
@Override
public boolean isEventHeaderSet() {
return fEventHeaderDecl != null;
}
@Override
public boolean isEventContextSet() {
return fEventContextDecl != null;
}
@Override
public boolean isPacketContextSet() {
return fPacketContextDecl != null;
}
/**
* Sets the event header
*
* @param eventHeader
* the current event header for all events in this stream
*/
public void setEventHeader(StructDeclaration eventHeader) {
fEventHeaderDecl = eventHeader;
}
/**
* Sets the event header, this typically has the id and the timestamp
*
* @param eventHeader
* the current event header for all events in this stream
*/
public void setEventHeader(IEventHeaderDeclaration eventHeader) {
fEventHeaderDecl = eventHeader;
}
/**
*
* @param eventContext
* the context for all events in this stream
*/
public void setEventContext(StructDeclaration eventContext) {
fEventContextDecl = eventContext;
}
/**
*
* @param packetContext
* the packet context for all packets in this stream
*/
public void setPacketContext(StructDeclaration packetContext) {
fPacketContextDecl = packetContext;
}
@Override
public IDeclaration getEventHeaderDeclaration() {
return fEventHeaderDecl;
}
@Override
public StructDeclaration getEventContextDecl() {
return fEventContextDecl;
}
@Override
public StructDeclaration getPacketContextDecl() {
return fPacketContextDecl;
}
@Override
public Set<CTFStreamInput> getStreamInputs() {
return fInputs;
}
@Override
public CTFTrace getTrace() {
return fTrace;
}
/**
* @since 2.0
*/
@Override
public List<@Nullable IEventDeclaration> getEventDeclarations() {
return Collections.unmodifiableList(fEvents);
}
@Override
public IEventDeclaration getEventDeclaration(int eventId) {
int eventIndex = (eventId == IEventDeclaration.UNSET_EVENT_ID) ? 0 : eventId;
if (eventIndex < 0) {
/* Any negative value other than UNSET_EVENT_ID is invalid */
throw new IllegalArgumentException("Event ID cannot be negative."); //$NON-NLS-1$
}
if (eventIndex >= fEvents.size()) {
/* This ID could be valid, but there are no declarations with it */
return null;
}
return fEvents.get(eventIndex);
}
// ------------------------------------------------------------------------
// Operations
// ------------------------------------------------------------------------
/**
* Adds an event to the event list.
*
* An event in a stream can omit its id if it is the only event in this
* stream. An event for which no id has been specified has a null id. It is
* thus not possible to add an event with the null key if the map is not
* empty. It is also not possible to add an event to the map if the null key
* is present in the map.
*
* @param event
* The event to add
* @throws ParseException
* If there was a problem reading the event or adding it to the
* stream
* @since 2.0
*/
public void addEvent(IEventDeclaration event) throws ParseException {
if (fEventUnsetId) {
throw new ParseException("Event without id with multiple events in a stream"); //$NON-NLS-1$
}
int id = ((EventDeclaration) event).id();
/*
* If there is an event without id (the null key), it must be the only
* one
*/
if (id == IEventDeclaration.UNSET_EVENT_ID) {
if (!fEvents.isEmpty()) {
throw new ParseException("Event without id with multiple events in a stream"); //$NON-NLS-1$
}
fEventUnsetId = true;
fEvents.add(event);
} else {
/* Check if an event with the same ID already exists */
if (fEvents.size() > id && fEvents.get(id) != null) {
throw new ParseException("Event id already exists"); //$NON-NLS-1$
}
ensureSize(id);
/* Put the event in the list */
fEvents.set(id, event);
}
}
/**
* Add a list of event declarations to this stream. There must be no overlap
* between the two lists of event declarations. This will merge the two
* lists and preserve the indexes of both lists.
*
* @param events
* list of the events to add
* @throws CTFException
* if the list already contains data
*/
public void addEvents(Collection<IEventDeclaration> events) throws CTFException {
if (fEventUnsetId) {
throw new CTFException("Cannot add to a stream with an unidentified event"); //$NON-NLS-1$
}
if (fEvents.isEmpty()) {
fEvents.addAll(events);
return;
}
for (IEventDeclaration event : events) {
if (event != null) {
int index = event.getId().intValue();
ensureSize(index);
if (fEvents.get(index) != null) {
throw new CTFException("Both lists have an event defined at position " + index); //$NON-NLS-1$
}
fEvents.set(index, event);
}
}
}
private void ensureSize(int index) {
List<@Nullable IEventDeclaration> list = fEvents;
if (list instanceof ArrayList) {
if (index > 50000) {
SparseList<@Nullable IEventDeclaration> sparseList = new SparseList<>(fEvents);
sparseList.ensureSize(index + 1);
fEvents = sparseList;
return;
}
((ArrayList<@Nullable IEventDeclaration>) list).ensureCapacity(index);
while (list.size() <= index) {
list.add(null);
}
} else if (list instanceof SparseList) {
SparseList sparseList = (SparseList) list;
sparseList.ensureSize(index + 1);
}
}
/**
* Add an input to this Stream
*
* @param input
* The StreamInput to add.
*/
public void addInput(CTFStreamInput input) {
fInputs.add(input);
}
@Override
public String toString() {
return "Stream [id=" + fId + ", packetContextDecl=" + fPacketContextDecl //$NON-NLS-1$ //$NON-NLS-2$
+ ", eventHeaderDecl=" + fEventHeaderDecl //$NON-NLS-1$
+ ", eventContextDecl=" + fEventContextDecl + ", trace=" + fTrace //$NON-NLS-1$ //$NON-NLS-2$
+ ", events=" + fEvents + ", inputs=" + fInputs + "]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
}