| // 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 "quiche/quic/core/qpack/qpack_header_table.h" | 
 |  | 
 | #include "absl/strings/string_view.h" | 
 | #include "quiche/quic/core/qpack/qpack_static_table.h" | 
 | #include "quiche/quic/platform/api/quic_logging.h" | 
 | #include "quiche/common/platform/api/quiche_logging.h" | 
 |  | 
 | namespace quic { | 
 |  | 
 | QpackEncoderHeaderTable::QpackEncoderHeaderTable() | 
 |     : static_index_(ObtainQpackStaticTable().GetStaticIndex()), | 
 |       static_name_index_(ObtainQpackStaticTable().GetStaticNameIndex()) {} | 
 |  | 
 | uint64_t QpackEncoderHeaderTable::InsertEntry(absl::string_view name, | 
 |                                               absl::string_view value) { | 
 |   const uint64_t index = | 
 |       QpackHeaderTableBase<QpackEncoderDynamicTable>::InsertEntry(name, value); | 
 |  | 
 |   // Make name and value point to the new entry. | 
 |   name = dynamic_entries().back()->name(); | 
 |   value = dynamic_entries().back()->value(); | 
 |  | 
 |   auto index_result = dynamic_index_.insert( | 
 |       std::make_pair(QpackLookupEntry{name, value}, index)); | 
 |   if (!index_result.second) { | 
 |     // An entry with the same name and value already exists.  It needs to be | 
 |     // replaced, because |dynamic_index_| tracks the most recent entry for a | 
 |     // given name and value. | 
 |     QUICHE_DCHECK_GT(index, index_result.first->second); | 
 |     dynamic_index_.erase(index_result.first); | 
 |     auto result = dynamic_index_.insert( | 
 |         std::make_pair(QpackLookupEntry{name, value}, index)); | 
 |     QUICHE_CHECK(result.second); | 
 |   } | 
 |  | 
 |   auto name_result = dynamic_name_index_.insert({name, index}); | 
 |   if (!name_result.second) { | 
 |     // An entry with the same name already exists.  It needs to be replaced, | 
 |     // because |dynamic_name_index_| tracks the most recent entry for a given | 
 |     // name. | 
 |     QUICHE_DCHECK_GT(index, name_result.first->second); | 
 |     dynamic_name_index_.erase(name_result.first); | 
 |     auto result = dynamic_name_index_.insert({name, index}); | 
 |     QUICHE_CHECK(result.second); | 
 |   } | 
 |  | 
 |   return index; | 
 | } | 
 |  | 
 | QpackEncoderHeaderTable::MatchType QpackEncoderHeaderTable::FindHeaderField( | 
 |     absl::string_view name, absl::string_view value, bool* is_static, | 
 |     uint64_t* index) const { | 
 |   QpackLookupEntry query{name, value}; | 
 |  | 
 |   // Look for exact match in static table. | 
 |   auto index_it = static_index_.find(query); | 
 |   if (index_it != static_index_.end()) { | 
 |     *index = index_it->second; | 
 |     *is_static = true; | 
 |     return MatchType::kNameAndValue; | 
 |   } | 
 |  | 
 |   // Look for exact match in dynamic table. | 
 |   index_it = dynamic_index_.find(query); | 
 |   if (index_it != dynamic_index_.end()) { | 
 |     *index = index_it->second; | 
 |     *is_static = false; | 
 |     return MatchType::kNameAndValue; | 
 |   } | 
 |  | 
 |   // Look for name match in static table. | 
 |   auto name_index_it = static_name_index_.find(name); | 
 |   if (name_index_it != static_name_index_.end()) { | 
 |     *index = name_index_it->second; | 
 |     *is_static = true; | 
 |     return MatchType::kName; | 
 |   } | 
 |  | 
 |   // Look for name match in dynamic table. | 
 |   name_index_it = dynamic_name_index_.find(name); | 
 |   if (name_index_it != dynamic_name_index_.end()) { | 
 |     *index = name_index_it->second; | 
 |     *is_static = false; | 
 |     return MatchType::kName; | 
 |   } | 
 |  | 
 |   return MatchType::kNoMatch; | 
 | } | 
 |  | 
 | uint64_t QpackEncoderHeaderTable::MaxInsertSizeWithoutEvictingGivenEntry( | 
 |     uint64_t index) const { | 
 |   QUICHE_DCHECK_LE(dropped_entry_count(), index); | 
 |  | 
 |   if (index > inserted_entry_count()) { | 
 |     // All entries are allowed to be evicted. | 
 |     return dynamic_table_capacity(); | 
 |   } | 
 |  | 
 |   // Initialize to current available capacity. | 
 |   uint64_t max_insert_size = dynamic_table_capacity() - dynamic_table_size(); | 
 |  | 
 |   uint64_t entry_index = dropped_entry_count(); | 
 |   for (const auto& entry : dynamic_entries()) { | 
 |     if (entry_index >= index) { | 
 |       break; | 
 |     } | 
 |     ++entry_index; | 
 |     max_insert_size += entry->Size(); | 
 |   } | 
 |  | 
 |   return max_insert_size; | 
 | } | 
 |  | 
 | uint64_t QpackEncoderHeaderTable::draining_index( | 
 |     float draining_fraction) const { | 
 |   QUICHE_DCHECK_LE(0.0, draining_fraction); | 
 |   QUICHE_DCHECK_LE(draining_fraction, 1.0); | 
 |  | 
 |   const uint64_t required_space = draining_fraction * dynamic_table_capacity(); | 
 |   uint64_t space_above_draining_index = | 
 |       dynamic_table_capacity() - dynamic_table_size(); | 
 |  | 
 |   if (dynamic_entries().empty() || | 
 |       space_above_draining_index >= required_space) { | 
 |     return dropped_entry_count(); | 
 |   } | 
 |  | 
 |   auto it = dynamic_entries().begin(); | 
 |   uint64_t entry_index = dropped_entry_count(); | 
 |   while (space_above_draining_index < required_space) { | 
 |     space_above_draining_index += (*it)->Size(); | 
 |     ++it; | 
 |     ++entry_index; | 
 |     if (it == dynamic_entries().end()) { | 
 |       return inserted_entry_count(); | 
 |     } | 
 |   } | 
 |  | 
 |   return entry_index; | 
 | } | 
 |  | 
 | void QpackEncoderHeaderTable::RemoveEntryFromEnd() { | 
 |   const QpackEntry* const entry = dynamic_entries().front().get(); | 
 |   const uint64_t index = dropped_entry_count(); | 
 |  | 
 |   auto index_it = dynamic_index_.find({entry->name(), entry->value()}); | 
 |   // Remove |dynamic_index_| entry only if it points to the same | 
 |   // QpackEntry in dynamic_entries(). | 
 |   if (index_it != dynamic_index_.end() && index_it->second == index) { | 
 |     dynamic_index_.erase(index_it); | 
 |   } | 
 |  | 
 |   auto name_it = dynamic_name_index_.find(entry->name()); | 
 |   // Remove |dynamic_name_index_| entry only if it points to the same | 
 |   // QpackEntry in dynamic_entries(). | 
 |   if (name_it != dynamic_name_index_.end() && name_it->second == index) { | 
 |     dynamic_name_index_.erase(name_it); | 
 |   } | 
 |  | 
 |   QpackHeaderTableBase<QpackEncoderDynamicTable>::RemoveEntryFromEnd(); | 
 | } | 
 |  | 
 | QpackDecoderHeaderTable::QpackDecoderHeaderTable() | 
 |     : static_entries_(ObtainQpackStaticTable().GetStaticEntries()) {} | 
 |  | 
 | QpackDecoderHeaderTable::~QpackDecoderHeaderTable() { | 
 |   for (auto& entry : observers_) { | 
 |     entry.second->Cancel(); | 
 |   } | 
 | } | 
 |  | 
 | uint64_t QpackDecoderHeaderTable::InsertEntry(absl::string_view name, | 
 |                                               absl::string_view value) { | 
 |   const uint64_t index = | 
 |       QpackHeaderTableBase<QpackDecoderDynamicTable>::InsertEntry(name, value); | 
 |  | 
 |   // Notify and deregister observers whose threshold is met, if any. | 
 |   while (!observers_.empty()) { | 
 |     auto it = observers_.begin(); | 
 |     if (it->first > inserted_entry_count()) { | 
 |       break; | 
 |     } | 
 |     Observer* observer = it->second; | 
 |     observers_.erase(it); | 
 |     observer->OnInsertCountReachedThreshold(); | 
 |   } | 
 |  | 
 |   return index; | 
 | } | 
 |  | 
 | const QpackEntry* QpackDecoderHeaderTable::LookupEntry(bool is_static, | 
 |                                                        uint64_t index) const { | 
 |   if (is_static) { | 
 |     if (index >= static_entries_.size()) { | 
 |       return nullptr; | 
 |     } | 
 |  | 
 |     return &static_entries_[index]; | 
 |   } | 
 |  | 
 |   if (index < dropped_entry_count()) { | 
 |     return nullptr; | 
 |   } | 
 |  | 
 |   index -= dropped_entry_count(); | 
 |  | 
 |   if (index >= dynamic_entries().size()) { | 
 |     return nullptr; | 
 |   } | 
 |  | 
 |   return &dynamic_entries()[index]; | 
 | } | 
 |  | 
 | void QpackDecoderHeaderTable::RegisterObserver(uint64_t required_insert_count, | 
 |                                                Observer* observer) { | 
 |   QUICHE_DCHECK_GT(required_insert_count, 0u); | 
 |   observers_.insert({required_insert_count, observer}); | 
 | } | 
 |  | 
 | void QpackDecoderHeaderTable::UnregisterObserver(uint64_t required_insert_count, | 
 |                                                  Observer* observer) { | 
 |   auto it = observers_.lower_bound(required_insert_count); | 
 |   while (it != observers_.end() && it->first == required_insert_count) { | 
 |     if (it->second == observer) { | 
 |       observers_.erase(it); | 
 |       return; | 
 |     } | 
 |     ++it; | 
 |   } | 
 |  | 
 |   // |observer| must have been registered. | 
 |   QUICHE_NOTREACHED(); | 
 | } | 
 |  | 
 | }  // namespace quic |