text: // 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 "quic/core/legacy_quic_stream_id_manager.h"
  
  #include "quic/core/quic_session.h"
  #include "quic/core/quic_types.h"
  #include "quic/core/quic_utils.h"
  #include "quic/core/quic_versions.h"
  #include "quic/platform/api/quic_map_util.h"
  
  namespace quic {
  
  LegacyQuicStreamIdManager::LegacyQuicStreamIdManager(
      Perspective perspective,
      QuicTransportVersion transport_version,
      size_t max_open_outgoing_streams,
      size_t max_open_incoming_streams)
      : perspective_(perspective),
        transport_version_(transport_version),
        max_open_outgoing_streams_(max_open_outgoing_streams),
        max_open_incoming_streams_(max_open_incoming_streams),
        next_outgoing_stream_id_(
            QuicUtils::GetFirstBidirectionalStreamId(transport_version_,
                                                     perspective_)),
        largest_peer_created_stream_id_(
            perspective_ == Perspective::IS_SERVER
                ? (QuicVersionUsesCryptoFrames(transport_version_)
                       ? QuicUtils::GetInvalidStreamId(transport_version_)
                       : QuicUtils::GetCryptoStreamId(transport_version_))
                : QuicUtils::GetInvalidStreamId(transport_version_)),
        num_open_incoming_streams_(0),
        num_open_outgoing_streams_(0) {}
  
  LegacyQuicStreamIdManager::~LegacyQuicStreamIdManager() {}
  
  bool LegacyQuicStreamIdManager::CanOpenNextOutgoingStream() const {
    QUICHE_DCHECK_LE(num_open_outgoing_streams_, max_open_outgoing_streams_);
    QUIC_DLOG_IF(INFO, num_open_outgoing_streams_ == max_open_outgoing_streams_)
        << "Failed to create a new outgoing stream. "
        << "Already " << num_open_outgoing_streams_ << " open.";
    return num_open_outgoing_streams_ < max_open_outgoing_streams_;
  }
  
  bool LegacyQuicStreamIdManager::CanOpenIncomingStream() const {
    return num_open_incoming_streams_ < max_open_incoming_streams_;
  }
  
  bool LegacyQuicStreamIdManager::MaybeIncreaseLargestPeerStreamId(
      const QuicStreamId stream_id) {
    available_streams_.erase(stream_id);
  
    if (largest_peer_created_stream_id_ !=
            QuicUtils::GetInvalidStreamId(transport_version_) &&
        stream_id <= largest_peer_created_stream_id_) {
      return true;
    }
  
    // Check if the new number of available streams would cause the number of
    // available streams to exceed the limit.  Note that the peer can create
    // only alternately-numbered streams.
    size_t additional_available_streams =
        (stream_id - largest_peer_created_stream_id_) / 2 - 1;
    if (largest_peer_created_stream_id_ ==
        QuicUtils::GetInvalidStreamId(transport_version_)) {
      additional_available_streams = (stream_id + 1) / 2 - 1;
    }
    size_t new_num_available_streams =
        GetNumAvailableStreams() + additional_available_streams;
    if (new_num_available_streams > MaxAvailableStreams()) {
      QUIC_DLOG(INFO) << perspective_
                      << "Failed to create a new incoming stream with id:"
                      << stream_id << ".  There are already "
                      << GetNumAvailableStreams()
                      << " streams available, which would become "
                      << new_num_available_streams << ", which exceeds the limit "
                      << MaxAvailableStreams() << ".";
      return false;
    }
    QuicStreamId first_available_stream = largest_peer_created_stream_id_ + 2;
    if (largest_peer_created_stream_id_ ==
        QuicUtils::GetInvalidStreamId(transport_version_)) {
      first_available_stream = QuicUtils::GetFirstBidirectionalStreamId(
          transport_version_, QuicUtils::InvertPerspective(perspective_));
    }
    for (QuicStreamId id = first_available_stream; id < stream_id; id += 2) {
      available_streams_.insert(id);
    }
    largest_peer_created_stream_id_ = stream_id;
  
    return true;
  }
  
  QuicStreamId LegacyQuicStreamIdManager::GetNextOutgoingStreamId() {
    QuicStreamId id = next_outgoing_stream_id_;
    next_outgoing_stream_id_ += 2;
    return id;
  }
  
  void LegacyQuicStreamIdManager::ActivateStream(bool is_incoming) {
    if (is_incoming) {
      ++num_open_incoming_streams_;
      return;
    }
    ++num_open_outgoing_streams_;
  }
  
  void LegacyQuicStreamIdManager::OnStreamClosed(bool is_incoming) {
    if (is_incoming) {
      QUIC_BUG_IF(quic_bug_12720_1, num_open_incoming_streams_ == 0);
      --num_open_incoming_streams_;
      return;
    }
    QUIC_BUG_IF(quic_bug_12720_2, num_open_outgoing_streams_ == 0);
    --num_open_outgoing_streams_;
  }
  
  bool LegacyQuicStreamIdManager::IsAvailableStream(QuicStreamId id) const {
    if (!IsIncomingStream(id)) {
      // Stream IDs under next_ougoing_stream_id_ are either open or previously
      // open but now closed.
      return id >= next_outgoing_stream_id_;
    }
    // For peer created streams, we also need to consider available streams.
    return largest_peer_created_stream_id_ ==
               QuicUtils::GetInvalidStreamId(transport_version_) ||
           id > largest_peer_created_stream_id_ ||
           QuicContainsKey(available_streams_, id);
  }
  
  bool LegacyQuicStreamIdManager::IsIncomingStream(QuicStreamId id) const {
    return id % 2 != next_outgoing_stream_id_ % 2;
  }
  
  size_t LegacyQuicStreamIdManager::GetNumAvailableStreams() const {
    return available_streams_.size();
  }
  
  size_t LegacyQuicStreamIdManager::MaxAvailableStreams() const {
    return max_open_incoming_streams_ * kMaxAvailableStreamsMultiplier;
  }
  
  }  // namespace quic
  