| Index: net/quic/quic_stream_sequencer.cc
|
| diff --git a/net/quic/quic_stream_sequencer.cc b/net/quic/quic_stream_sequencer.cc
|
| index bd1bc6e40032335efa87d084428c4aedd87f26fa..3989c4f98b8686f87062c49e946c602cb6b02efc 100644
|
| --- a/net/quic/quic_stream_sequencer.cc
|
| +++ b/net/quic/quic_stream_sequencer.cc
|
| @@ -9,6 +9,7 @@
|
| #include <utility>
|
|
|
| #include "base/logging.h"
|
| +#include "net/quic/quic_frame_list.h"
|
| #include "net/quic/reliable_quic_stream.h"
|
|
|
| using std::min;
|
| @@ -17,10 +18,6 @@ using std::string;
|
|
|
| namespace net {
|
|
|
| -QuicStreamSequencer::FrameData::FrameData(QuicStreamOffset offset,
|
| - const string& segment)
|
| - : offset(offset), segment(segment) {}
|
| -
|
| QuicStreamSequencer::QuicStreamSequencer(ReliableQuicStream* quic_stream)
|
| : stream_(quic_stream),
|
| num_bytes_consumed_(0),
|
| @@ -29,27 +26,12 @@ QuicStreamSequencer::QuicStreamSequencer(ReliableQuicStream* quic_stream)
|
| num_bytes_buffered_(0),
|
| num_frames_received_(0),
|
| num_duplicate_frames_received_(0),
|
| - num_early_frames_received_(0) {
|
| -}
|
| + num_early_frames_received_(0) {}
|
|
|
| -QuicStreamSequencer::~QuicStreamSequencer() {
|
| -}
|
| +QuicStreamSequencer::~QuicStreamSequencer() {}
|
|
|
| void QuicStreamSequencer::OnStreamFrame(const QuicStreamFrame& frame) {
|
| ++num_frames_received_;
|
| - FrameList::iterator insertion_point = FindInsertionPoint(frame);
|
| - if (IsDuplicate(frame, insertion_point)) {
|
| - ++num_duplicate_frames_received_;
|
| - // Silently ignore duplicates.
|
| - return;
|
| - }
|
| -
|
| - if (FrameOverlapsBufferedData(frame, insertion_point)) {
|
| - stream_->CloseConnectionWithDetails(
|
| - QUIC_INVALID_STREAM_FRAME, "Stream frame overlaps with buffered data.");
|
| - return;
|
| - }
|
| -
|
| const QuicStreamOffset byte_offset = frame.offset;
|
| const size_t data_len = frame.data.length();
|
| if (data_len == 0 && !frame.fin) {
|
| @@ -65,16 +47,25 @@ void QuicStreamSequencer::OnStreamFrame(const QuicStreamFrame& frame) {
|
| return;
|
| }
|
| }
|
| + size_t bytes_written;
|
| + QuicErrorCode result =
|
| + buffered_frames_.WriteAtOffset(byte_offset, frame.data, &bytes_written);
|
| +
|
| + if (result == QUIC_INVALID_STREAM_DATA) {
|
| + stream_->CloseConnectionWithDetails(
|
| + QUIC_INVALID_STREAM_FRAME, "Stream frame overlaps with buffered data.");
|
| + return;
|
| + }
|
| + if (result == QUIC_NO_ERROR && bytes_written == 0) {
|
| + ++num_duplicate_frames_received_;
|
| + // Silently ignore duplicates.
|
| + return;
|
| + }
|
|
|
| if (byte_offset > num_bytes_consumed_) {
|
| ++num_early_frames_received_;
|
| }
|
|
|
| - DVLOG(1) << "Buffering stream data at offset " << byte_offset;
|
| - // Inserting an empty string and then copying to avoid the extra copy.
|
| - insertion_point =
|
| - buffered_frames_.insert(insertion_point, FrameData(byte_offset, ""));
|
| - frame.data.CopyToString(&insertion_point->segment);
|
| num_bytes_buffered_ += data_len;
|
|
|
| if (blocked_) {
|
| @@ -103,13 +94,12 @@ void QuicStreamSequencer::CloseStreamAtOffset(QuicStreamOffset offset) {
|
| bool QuicStreamSequencer::MaybeCloseStream() {
|
| if (!blocked_ && IsClosed()) {
|
| DVLOG(1) << "Passing up termination, as we've processed "
|
| - << num_bytes_consumed_ << " of " << close_offset_
|
| - << " bytes.";
|
| + << num_bytes_consumed_ << " of " << close_offset_ << " bytes.";
|
| // This will cause the stream to consume the fin.
|
| // Technically it's an error if num_bytes_consumed isn't exactly
|
| // equal, but error handling seems silly at this point.
|
| stream_->OnDataAvailable();
|
| - buffered_frames_.clear();
|
| + buffered_frames_.Clear();
|
| num_bytes_buffered_ = 0;
|
| return true;
|
| }
|
| @@ -118,163 +108,36 @@ bool QuicStreamSequencer::MaybeCloseStream() {
|
|
|
| int QuicStreamSequencer::GetReadableRegions(iovec* iov, size_t iov_len) const {
|
| DCHECK(!blocked_);
|
| - FrameList::const_iterator it = buffered_frames_.begin();
|
| - size_t index = 0;
|
| - QuicStreamOffset offset = num_bytes_consumed_;
|
| - while (it != buffered_frames_.end() && index < iov_len) {
|
| - if (it->offset != offset) {
|
| - return index;
|
| - }
|
| -
|
| - iov[index].iov_base =
|
| - static_cast<void*>(const_cast<char*>(it->segment.data()));
|
| - iov[index].iov_len = it->segment.size();
|
| - offset += it->segment.size();
|
| -
|
| - ++index;
|
| - ++it;
|
| - }
|
| - return index;
|
| + return buffered_frames_.GetReadableRegions(iov, iov_len);
|
| }
|
|
|
| int QuicStreamSequencer::Readv(const struct iovec* iov, size_t iov_len) {
|
| DCHECK(!blocked_);
|
| - FrameList::iterator it = buffered_frames_.begin();
|
| - size_t iov_index = 0;
|
| - size_t iov_offset = 0;
|
| - size_t frame_offset = 0;
|
| - QuicStreamOffset initial_bytes_consumed = num_bytes_consumed_;
|
| -
|
| - while (iov_index < iov_len && it != buffered_frames_.end() &&
|
| - it->offset == num_bytes_consumed_) {
|
| - int bytes_to_read = min(iov[iov_index].iov_len - iov_offset,
|
| - it->segment.size() - frame_offset);
|
| -
|
| - char* iov_ptr = static_cast<char*>(iov[iov_index].iov_base) + iov_offset;
|
| - memcpy(iov_ptr, it->segment.data() + frame_offset, bytes_to_read);
|
| - frame_offset += bytes_to_read;
|
| - iov_offset += bytes_to_read;
|
| -
|
| - if (iov[iov_index].iov_len == iov_offset) {
|
| - // We've filled this buffer.
|
| - iov_offset = 0;
|
| - ++iov_index;
|
| - }
|
| - if (it->segment.size() == frame_offset) {
|
| - // We've copied this whole frame
|
| - RecordBytesConsumed(it->segment.size());
|
| - buffered_frames_.erase(it);
|
| - it = buffered_frames_.begin();
|
| - frame_offset = 0;
|
| - }
|
| - }
|
| - // Done copying. If there is a partial frame, update it.
|
| - if (frame_offset != 0) {
|
| - buffered_frames_.push_front(
|
| - FrameData(it->offset + frame_offset, it->segment.substr(frame_offset)));
|
| - buffered_frames_.erase(it);
|
| - RecordBytesConsumed(frame_offset);
|
| - }
|
| - return static_cast<int>(num_bytes_consumed_ - initial_bytes_consumed);
|
| + size_t bytes_read = buffered_frames_.ReadvAndInvalidate(iov, iov_len);
|
| + RecordBytesConsumed(bytes_read);
|
| + return static_cast<int>(bytes_read);
|
| }
|
|
|
| bool QuicStreamSequencer::HasBytesToRead() const {
|
| - return !buffered_frames_.empty() &&
|
| - buffered_frames_.begin()->offset == num_bytes_consumed_;
|
| + return buffered_frames_.HasBytesToRead();
|
| }
|
|
|
| bool QuicStreamSequencer::IsClosed() const {
|
| return num_bytes_consumed_ >= close_offset_;
|
| }
|
|
|
| -QuicStreamSequencer::FrameList::iterator
|
| -QuicStreamSequencer::FindInsertionPoint(const QuicStreamFrame& frame) {
|
| - if (buffered_frames_.empty()) {
|
| - return buffered_frames_.begin();
|
| - }
|
| - // If it's after all buffered_frames, return the end.
|
| - if (frame.offset >= (buffered_frames_.rbegin()->offset +
|
| - buffered_frames_.rbegin()->segment.length())) {
|
| - return buffered_frames_.end();
|
| - }
|
| - FrameList::iterator iter = buffered_frames_.begin();
|
| - // Only advance the iterator if the data begins after the already received
|
| - // frame. If the new frame overlaps with an existing frame, the iterator will
|
| - // still point to the frame it overlaps with.
|
| - while (iter != buffered_frames_.end() &&
|
| - frame.offset >= iter->offset + iter->segment.length()) {
|
| - ++iter;
|
| - }
|
| - return iter;
|
| -}
|
| -
|
| -bool QuicStreamSequencer::FrameOverlapsBufferedData(
|
| - const QuicStreamFrame& frame,
|
| - FrameList::const_iterator insertion_point) const {
|
| - if (buffered_frames_.empty() || insertion_point == buffered_frames_.end()) {
|
| - return false;
|
| - }
|
| - // If there is a buffered frame with a higher starting offset, then check to
|
| - // see if the new frame overlaps the beginning of the higher frame.
|
| - if (frame.offset < insertion_point->offset &&
|
| - frame.offset + frame.data.length() > insertion_point->offset) {
|
| - DVLOG(1) << "New frame overlaps next frame: " << frame.offset << " + "
|
| - << frame.data.size() << " > " << insertion_point->offset;
|
| - return true;
|
| - }
|
| - // If there is a buffered frame with a lower starting offset, then check to
|
| - // see if the buffered frame runs into the new frame.
|
| - if (frame.offset >= insertion_point->offset &&
|
| - frame.offset <
|
| - insertion_point->offset + insertion_point->segment.length()) {
|
| - DVLOG(1) << "Preceeding frame overlaps new frame: "
|
| - << insertion_point->offset << " + "
|
| - << insertion_point->segment.length() << " > " << frame.offset;
|
| - return true;
|
| - }
|
| -
|
| - return false;
|
| -}
|
| -
|
| void QuicStreamSequencer::MarkConsumed(size_t num_bytes_consumed) {
|
| DCHECK(!blocked_);
|
| - size_t end_offset = num_bytes_consumed_ + num_bytes_consumed;
|
| - while (!buffered_frames_.empty() && end_offset != num_bytes_consumed_) {
|
| - FrameList::iterator it = buffered_frames_.begin();
|
| - if (it->offset != num_bytes_consumed_) {
|
| - LOG(DFATAL) << "Invalid argument to MarkConsumed. "
|
| - << " num_bytes_consumed_: " << num_bytes_consumed_
|
| - << " end_offset: " << end_offset << " offset: " << it->offset
|
| - << " length: " << it->segment.length();
|
| - stream_->Reset(QUIC_ERROR_PROCESSING_STREAM);
|
| - return;
|
| - }
|
| -
|
| - if (it->offset + it->segment.length() <= end_offset) {
|
| - RecordBytesConsumed(it->segment.length());
|
| - // This chunk is entirely consumed.
|
| - buffered_frames_.erase(it);
|
| - continue;
|
| - }
|
| -
|
| - // Partially consume this frame.
|
| - size_t delta = end_offset - it->offset;
|
| - RecordBytesConsumed(delta);
|
| - string new_data = it->segment.substr(delta);
|
| - buffered_frames_.erase(it);
|
| - buffered_frames_.push_front(FrameData(num_bytes_consumed_, new_data));
|
| - break;
|
| + bool result =
|
| + buffered_frames_.IncreaseTotalReadAndInvalidate(num_bytes_consumed);
|
| + if (!result) {
|
| + LOG(DFATAL) << "Invalid argument to MarkConsumed."
|
| + << " expect to consume: " << num_bytes_consumed
|
| + << ", but not enough bytes available.";
|
| + stream_->Reset(QUIC_ERROR_PROCESSING_STREAM);
|
| + return;
|
| }
|
| -}
|
| -
|
| -bool QuicStreamSequencer::IsDuplicate(
|
| - const QuicStreamFrame& frame,
|
| - FrameList::const_iterator insertion_point) const {
|
| - // A frame is duplicate if the frame offset is smaller than the bytes consumed
|
| - // or identical to an already received frame.
|
| - return frame.offset < num_bytes_consumed_ ||
|
| - (insertion_point != buffered_frames_.end() &&
|
| - frame.offset == insertion_point->offset);
|
| + RecordBytesConsumed(num_bytes_consumed);
|
| }
|
|
|
| void QuicStreamSequencer::SetBlockedUntilFlush() {
|
|
|