public virtual CtfTimestamp GetTimestampFromEventHeader(ICtfEvent ctfEvent, CtfTimestamp previousTimestamp)
        {
            var variantStruct = GetStreamEventHeaderStructure(ctfEvent);

            Debug.Assert(variantStruct != null);

            if (!variantStruct.FieldsByName.TryGetValue("timestamp", out var timestampValue))
            {
                throw new LTTngPlaybackException("stream.event.header variant 'v' struct has no 'timestamp' field");
            }

            if (!(timestampValue is CtfIntegerValue timestampInteger))
            {
                throw new LTTngPlaybackException("stream.event.header variant 'v' struct field 'timestamp'" +
                                                 " is not an integer");
            }

            if (previousTimestamp is null)
            {
                // todo:I think we need to do something else for this case, especially if the integer size is < 64
                // not sure what yet. maybe base it on the clock's offset?

                return(new CtfTimestamp(this.MetadataCustomization, timestampInteger));
            }

            // Timestamps aren't actually absolute values. To quote from CTF spec 1.8.2 section 8:
            //    For a N-bit integer type referring to a clock, if the integer overflows compared to the N low order bits
            //    of the clock prior value found in the same stream, then it is assumed that one, and only one, overflow
            //    occurred. It is therefore important that events encoding time on a small number of bits happen frequently
            //    enough to detect when more than one N-bit overflow occurs.
            // So to determine a timestamp, we must know the previous timestamp. If they're all the same number of bits, it
            // wouldn't be necessary (I don't think so anyways). But some timestamps are smaller than others.
            if (timestampInteger.Descriptor.Size < 64)
            {
                if (!timestampInteger.Value.TryGetInt64(out long thisTimestamp))
                {
                    Debug.Assert(false);
                    throw new CtfPlaybackException("Unable to retrieve timestamp as long.");
                }

                long previous = (long)previousTimestamp.NanosecondsFromClockBase;

                long oneBitBeyondBitsUsed = 1L << (byte)timestampInteger.Descriptor.Size;
                long bitMask = ~(oneBitBeyondBitsUsed - 1);

                long highBitsFromPreviousTimestamp = previous & bitMask;
                long newTimestamp = highBitsFromPreviousTimestamp | thisTimestamp;
                if (newTimestamp < previous)
                {
                    // handle the overflow case
                    newTimestamp += oneBitBeyondBitsUsed;
                    Debug.Assert(newTimestamp > previous);
                }

                return(new CtfTimestamp(this.MetadataCustomization, timestampInteger, newTimestamp));
            }

            return(new CtfTimestamp(this.MetadataCustomization, timestampInteger));
        }
        public bool GetTimestampsFromPacketContext(
            ICtfPacket ctfPacket,
            ICtfMetadata metadata,
            out CtfTimestamp start,
            out CtfTimestamp end)
        {
            start = null;
            end   = null;

            if (!(ctfPacket.StreamPacketContext is CtfStructValue packetContextStruct))
            {
                throw new PerfPlaybackException("The stream.packet.context is not a structure.");
            }

            if (!packetContextStruct.FieldsByName.TryGetValue("timestamp_begin", out var startFieldValue))
            {
                return(false);
            }

            if (!packetContextStruct.FieldsByName.TryGetValue("timestamp_end", out var endFieldValue))
            {
                return(false);
            }

            if (!(startFieldValue is CtfIntegerValue startFieldInteger))
            {
                return(false);
            }

            if (!(endFieldValue is CtfIntegerValue endFieldInteger))
            {
                return(false);
            }

            start = new CtfTimestamp(this.MetadataCustomization, metadata, startFieldInteger);
            end   = new CtfTimestamp(this.MetadataCustomization, metadata, endFieldInteger);
            return(true);
        }