| /******************************************************************************* |
| * 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$ |
| } |
| } |