// Copyright 2014 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/spdy/hpack_header_table.h" #include #include "base/logging.h" #include "net/spdy/hpack_constants.h" #include "net/spdy/hpack_static_table.h" #include "net/spdy/hpack_string_util.h" namespace net { using base::StringPiece; bool HpackHeaderTable::EntryComparator::operator() ( const HpackEntry* lhs, const HpackEntry* rhs) const { int result = lhs->name().compare(rhs->name()); if (result != 0) return result < 0; result = lhs->value().compare(rhs->value()); if (result != 0) return result < 0; const size_t lhs_index = lhs->IsLookup() ? 0 : 1 + lhs->InsertionIndex(); const size_t rhs_index = rhs->IsLookup() ? 0 : 1 + rhs->InsertionIndex(); DCHECK(lhs == rhs || lhs_index != rhs_index) << "lhs: (" << lhs->name() << ", " << rhs->value() << ") rhs: (" << rhs->name() << ", " << rhs->value() << ")" << " lhs index: " << lhs_index << " rhs index: " << rhs_index; return lhs_index < rhs_index; } HpackHeaderTable::HpackHeaderTable() : static_entries_(ObtainHpackStaticTable().GetStaticEntries()), static_index_(ObtainHpackStaticTable().GetStaticIndex()), settings_size_bound_(kDefaultHeaderTableSizeSetting), size_(0), max_size_(kDefaultHeaderTableSizeSetting), total_insertions_(static_entries_.size()) {} HpackHeaderTable::~HpackHeaderTable() {} const HpackEntry* HpackHeaderTable::GetByIndex(size_t index) { if (index == 0) { return NULL; } index -= 1; if (index < static_entries_.size()) { return &static_entries_[index]; } index -= static_entries_.size(); if (index < dynamic_entries_.size()) { return &dynamic_entries_[index]; } return NULL; } const HpackEntry* HpackHeaderTable::GetByName(StringPiece name) { HpackEntry query(name, ""); { OrderedEntrySet::const_iterator it = static_index_.lower_bound(&query); if (it != static_index_.end() && (*it)->name() == name) { return *it; } } { OrderedEntrySet::const_iterator it = dynamic_index_.lower_bound(&query); if (it != dynamic_index_.end() && (*it)->name() == name) { return *it; } } return NULL; } const HpackEntry* HpackHeaderTable::GetByNameAndValue(StringPiece name, StringPiece value) { HpackEntry query(name, value); { OrderedEntrySet::const_iterator it = static_index_.lower_bound(&query); if (it != static_index_.end() && (*it)->name() == name && (*it)->value() == value) { return *it; } } { OrderedEntrySet::const_iterator it = dynamic_index_.lower_bound(&query); if (it != dynamic_index_.end() && (*it)->name() == name && (*it)->value() == value) { return *it; } } return NULL; } size_t HpackHeaderTable::IndexOf(const HpackEntry* entry) const { if (entry->IsLookup()) { return 0; } else if (entry->IsStatic()) { return 1 + entry->InsertionIndex(); } else { return total_insertions_ - entry->InsertionIndex() + static_entries_.size(); } } void HpackHeaderTable::SetMaxSize(size_t max_size) { CHECK_LE(max_size, settings_size_bound_); max_size_ = max_size; if (size_ > max_size_) { Evict(EvictionCountToReclaim(size_ - max_size_)); CHECK_LE(size_, max_size_); } } void HpackHeaderTable::SetSettingsHeaderTableSize(size_t settings_size) { settings_size_bound_ = settings_size; if (settings_size_bound_ < max_size_) { SetMaxSize(settings_size_bound_); } } void HpackHeaderTable::EvictionSet(StringPiece name, StringPiece value, EntryTable::iterator* begin_out, EntryTable::iterator* end_out) { size_t eviction_count = EvictionCountForEntry(name, value); *begin_out = dynamic_entries_.end() - eviction_count; *end_out = dynamic_entries_.end(); } size_t HpackHeaderTable::EvictionCountForEntry(StringPiece name, StringPiece value) const { size_t available_size = max_size_ - size_; size_t entry_size = HpackEntry::Size(name, value); if (entry_size <= available_size) { // No evictions are required. return 0; } return EvictionCountToReclaim(entry_size - available_size); } size_t HpackHeaderTable::EvictionCountToReclaim(size_t reclaim_size) const { size_t count = 0; for (EntryTable::const_reverse_iterator it = dynamic_entries_.rbegin(); it != dynamic_entries_.rend() && reclaim_size != 0; ++it, ++count) { reclaim_size -= std::min(reclaim_size, it->Size()); } return count; } void HpackHeaderTable::Evict(size_t count) { for (size_t i = 0; i != count; ++i) { CHECK(!dynamic_entries_.empty()); HpackEntry* entry = &dynamic_entries_.back(); size_ -= entry->Size(); CHECK_EQ(1u, dynamic_index_.erase(entry)); dynamic_entries_.pop_back(); } } const HpackEntry* HpackHeaderTable::TryAddEntry(StringPiece name, StringPiece value) { Evict(EvictionCountForEntry(name, value)); size_t entry_size = HpackEntry::Size(name, value); if (entry_size > (max_size_ - size_)) { // Entire table has been emptied, but there's still insufficient room. DCHECK(dynamic_entries_.empty()); DCHECK_EQ(0u, size_); return NULL; } dynamic_entries_.push_front(HpackEntry(name, value, false, // is_static total_insertions_)); CHECK(dynamic_index_.insert(&dynamic_entries_.front()).second); size_ += entry_size; ++total_insertions_; return &dynamic_entries_.front(); } void HpackHeaderTable::DebugLogTableState() const { DVLOG(2) << "Dynamic table:"; for (EntryTable::const_iterator it = dynamic_entries_.begin(); it != dynamic_entries_.end(); ++it) { DVLOG(2) << " " << it->GetDebugString(); } DVLOG(2) << "Full Static Index:"; for (OrderedEntrySet::const_iterator it = static_index_.begin(); it != static_index_.end(); ++it) { DVLOG(2) << " " << (*it)->GetDebugString(); } DVLOG(2) << "Full Dynamic Index:"; for (OrderedEntrySet::const_iterator it = dynamic_index_.begin(); it != dynamic_index_.end(); ++it) { DVLOG(2) << " " << (*it)->GetDebugString(); } } } // namespace net