| // Copyright (c) 2018 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "net/third_party/quiche/src/quic/tools/quic_tcp_like_trace_converter.h" | 
 |  | 
 | #include "net/third_party/quiche/src/quic/core/quic_constants.h" | 
 |  | 
 | namespace quic { | 
 |  | 
 | QuicTcpLikeTraceConverter::QuicTcpLikeTraceConverter() | 
 |     : largest_observed_control_frame_id_(kInvalidControlFrameId), | 
 |       connection_offset_(0) {} | 
 |  | 
 | QuicTcpLikeTraceConverter::StreamOffsetSegment::StreamOffsetSegment() | 
 |     : connection_offset(0) {} | 
 |  | 
 | QuicTcpLikeTraceConverter::StreamOffsetSegment::StreamOffsetSegment( | 
 |     QuicStreamOffset stream_offset, | 
 |     uint64_t connection_offset, | 
 |     QuicByteCount data_length) | 
 |     : stream_data(stream_offset, stream_offset + data_length), | 
 |       connection_offset(connection_offset) {} | 
 |  | 
 | QuicTcpLikeTraceConverter::StreamInfo::StreamInfo() : fin(false) {} | 
 |  | 
 | QuicIntervalSet<uint64_t> QuicTcpLikeTraceConverter::OnStreamFrameSent( | 
 |     QuicStreamId stream_id, | 
 |     QuicStreamOffset offset, | 
 |     QuicByteCount data_length, | 
 |     bool fin) { | 
 |   QuicIntervalSet<uint64_t> connection_offsets; | 
 |   if (fin) { | 
 |     // Stream fin consumes a connection offset. | 
 |     ++data_length; | 
 |   } | 
 |   StreamInfo* stream_info = | 
 |       &streams_info_.emplace(stream_id, StreamInfo()).first->second; | 
 |   // Get connection offsets of retransmission data in this frame. | 
 |   for (const auto& segment : stream_info->segments) { | 
 |     QuicInterval<QuicStreamOffset> retransmission(offset, offset + data_length); | 
 |     retransmission.IntersectWith(segment.stream_data); | 
 |     if (retransmission.Empty()) { | 
 |       continue; | 
 |     } | 
 |     const uint64_t connection_offset = segment.connection_offset + | 
 |                                        retransmission.min() - | 
 |                                        segment.stream_data.min(); | 
 |     connection_offsets.Add(connection_offset, | 
 |                            connection_offset + retransmission.Length()); | 
 |   } | 
 |  | 
 |   if (stream_info->fin) { | 
 |     return connection_offsets; | 
 |   } | 
 |  | 
 |   // Get connection offsets of new data in this frame. | 
 |   QuicStreamOffset least_unsent_offset = | 
 |       stream_info->segments.empty() | 
 |           ? 0 | 
 |           : stream_info->segments.back().stream_data.max(); | 
 |   if (least_unsent_offset >= offset + data_length) { | 
 |     return connection_offsets; | 
 |   } | 
 |   // Ignore out-of-order stream data so that as connection offset increases, | 
 |   // stream offset increases. | 
 |   QuicStreamOffset new_data_offset = std::max(least_unsent_offset, offset); | 
 |   QuicByteCount new_data_length = offset + data_length - new_data_offset; | 
 |   connection_offsets.Add(connection_offset_, | 
 |                          connection_offset_ + new_data_length); | 
 |   if (!stream_info->segments.empty() && | 
 |       new_data_offset == least_unsent_offset && | 
 |       connection_offset_ == | 
 |           stream_info->segments.back().connection_offset + | 
 |               stream_info->segments.back().stream_data.Length()) { | 
 |     // Extend the last segment if both stream and connection offsets are | 
 |     // contiguous. | 
 |     stream_info->segments.back().stream_data.SetMax(new_data_offset + | 
 |                                                     new_data_length); | 
 |   } else { | 
 |     stream_info->segments.emplace_back(new_data_offset, connection_offset_, | 
 |                                        new_data_length); | 
 |   } | 
 |   stream_info->fin = fin; | 
 |   connection_offset_ += new_data_length; | 
 |  | 
 |   return connection_offsets; | 
 | } | 
 |  | 
 | QuicInterval<uint64_t> QuicTcpLikeTraceConverter::OnControlFrameSent( | 
 |     QuicControlFrameId control_frame_id, | 
 |     QuicByteCount control_frame_length) { | 
 |   if (control_frame_id > largest_observed_control_frame_id_) { | 
 |     // New control frame. | 
 |     QuicInterval<uint64_t> connection_offset = QuicInterval<uint64_t>( | 
 |         connection_offset_, connection_offset_ + control_frame_length); | 
 |     connection_offset_ += control_frame_length; | 
 |     control_frames_info_[control_frame_id] = connection_offset; | 
 |     largest_observed_control_frame_id_ = control_frame_id; | 
 |     return connection_offset; | 
 |   } | 
 |   const auto iter = control_frames_info_.find(control_frame_id); | 
 |   if (iter == control_frames_info_.end()) { | 
 |     // Ignore out of order control frames. | 
 |     return {}; | 
 |   } | 
 |   return iter->second; | 
 | } | 
 |  | 
 | }  // namespace quic |