| // Copyright (c) 2012 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/disk_cache/blockfile/block_bitmaps_v3.h" | 
 |  | 
 | #include "base/metrics/histogram.h" | 
 | #include "base/time/time.h" | 
 | #include "net/disk_cache/blockfile/disk_format_base.h" | 
 | #include "net/disk_cache/blockfile/trace.h" | 
 |  | 
 | using base::TimeTicks; | 
 |  | 
 | namespace disk_cache { | 
 |  | 
 | BlockBitmaps::BlockBitmaps() { | 
 | } | 
 |  | 
 | BlockBitmaps::~BlockBitmaps() { | 
 | } | 
 |  | 
 | void BlockBitmaps::Init(const BlockFilesBitmaps& bitmaps) { | 
 |   bitmaps_ = bitmaps; | 
 | } | 
 |  | 
 | bool BlockBitmaps::CreateBlock(FileType block_type, | 
 |                                int block_count, | 
 |                                Addr* block_address) { | 
 |   DCHECK_NE(block_type, EXTERNAL); | 
 |   DCHECK_NE(block_type, RANKINGS); | 
 |   if (block_count < 1 || block_count > kMaxNumBlocks) | 
 |     return false; | 
 |  | 
 |   int header_num = HeaderNumberForNewBlock(block_type, block_count); | 
 |   if (header_num < 0) | 
 |     return false; | 
 |  | 
 |   int index; | 
 |   if (!bitmaps_[header_num].CreateMapBlock(block_count, &index)) | 
 |     return false; | 
 |  | 
 |   if (!index && (block_type == BLOCK_ENTRIES || block_type == BLOCK_EVICTED) && | 
 |       !bitmaps_[header_num].CreateMapBlock(block_count, &index)) { | 
 |     // index 0 for entries is a reserved value. | 
 |     return false; | 
 |   } | 
 |  | 
 |   Addr address(block_type, block_count, bitmaps_[header_num].FileId(), index); | 
 |   block_address->set_value(address.value()); | 
 |   Trace("CreateBlock 0x%x", address.value()); | 
 |   return true; | 
 | } | 
 |  | 
 | void BlockBitmaps::DeleteBlock(Addr address) { | 
 |   if (!address.is_initialized() || address.is_separate_file()) | 
 |     return; | 
 |  | 
 |   int header_num = GetHeaderNumber(address); | 
 |   if (header_num < 0) | 
 |     return; | 
 |  | 
 |   Trace("DeleteBlock 0x%x", address.value()); | 
 |   bitmaps_[header_num].DeleteMapBlock(address.start_block(), | 
 |                                       address.num_blocks()); | 
 | } | 
 |  | 
 | void BlockBitmaps::Clear() { | 
 |   bitmaps_.clear(); | 
 | } | 
 |  | 
 | void BlockBitmaps::ReportStats() { | 
 |   int used_blocks[kFirstAdditionalBlockFile]; | 
 |   int load[kFirstAdditionalBlockFile]; | 
 |   for (int16 i = 0; i < kFirstAdditionalBlockFile; i++) { | 
 |     GetFileStats(i, &used_blocks[i], &load[i]); | 
 |   } | 
 |   UMA_HISTOGRAM_COUNTS("DiskCache.Blocks_0", used_blocks[0]); | 
 |   UMA_HISTOGRAM_COUNTS("DiskCache.Blocks_1", used_blocks[1]); | 
 |   UMA_HISTOGRAM_COUNTS("DiskCache.Blocks_2", used_blocks[2]); | 
 |   UMA_HISTOGRAM_COUNTS("DiskCache.Blocks_3", used_blocks[3]); | 
 |  | 
 |   UMA_HISTOGRAM_ENUMERATION("DiskCache.BlockLoad_0", load[0], 101); | 
 |   UMA_HISTOGRAM_ENUMERATION("DiskCache.BlockLoad_1", load[1], 101); | 
 |   UMA_HISTOGRAM_ENUMERATION("DiskCache.BlockLoad_2", load[2], 101); | 
 |   UMA_HISTOGRAM_ENUMERATION("DiskCache.BlockLoad_3", load[3], 101); | 
 | } | 
 |  | 
 | bool BlockBitmaps::IsValid(Addr address) { | 
 | #ifdef NDEBUG | 
 |   return true; | 
 | #else | 
 |   if (!address.is_initialized() || address.is_separate_file()) | 
 |     return false; | 
 |  | 
 |   int header_num = GetHeaderNumber(address); | 
 |   if (header_num < 0) | 
 |     return false; | 
 |  | 
 |   bool rv = bitmaps_[header_num].UsedMapBlock(address.start_block(), | 
 |                                               address.num_blocks()); | 
 |   DCHECK(rv); | 
 |   return rv; | 
 | #endif | 
 | } | 
 |  | 
 | int BlockBitmaps::GetHeaderNumber(Addr address) { | 
 |   DCHECK_GE(bitmaps_.size(), kFirstAdditionalBlockFileV3); | 
 |   DCHECK(address.is_block_file() || !address.is_initialized()); | 
 |   if (!address.is_initialized()) | 
 |     return -1; | 
 |  | 
 |   int file_index = address.FileNumber(); | 
 |   if (static_cast<unsigned int>(file_index) >= bitmaps_.size()) | 
 |     return -1; | 
 |  | 
 |   return file_index; | 
 | } | 
 |  | 
 | int BlockBitmaps::HeaderNumberForNewBlock(FileType block_type, | 
 |                                           int block_count) { | 
 |   DCHECK_GT(block_type, 0); | 
 |   int header_num = block_type - 1; | 
 |   bool found = true; | 
 |  | 
 |   TimeTicks start = TimeTicks::Now(); | 
 |   while (bitmaps_[header_num].NeedToGrowBlockFile(block_count)) { | 
 |     header_num = bitmaps_[header_num].NextFileId(); | 
 |     if (!header_num) { | 
 |       found = false; | 
 |       break; | 
 |     } | 
 |   } | 
 |  | 
 |   if (!found) { | 
 |     // Restart the search, looking for any file with space. We know that all | 
 |     // files of this type are low on free blocks, but we cannot grow any file | 
 |     // at this time. | 
 |     header_num = block_type - 1; | 
 |     do { | 
 |       if (bitmaps_[header_num].CanAllocate(block_count)) { | 
 |         found = true;  // Make sure file 0 is not mistaken with a failure. | 
 |         break; | 
 |       } | 
 |       header_num = bitmaps_[header_num].NextFileId(); | 
 |     } while (header_num); | 
 |  | 
 |     if (!found) | 
 |       header_num = -1; | 
 |   } | 
 |  | 
 |   LOCAL_HISTOGRAM_TIMES("DiskCache.GetFileForNewBlock", | 
 |                         TimeTicks::Now() - start); | 
 |   return header_num; | 
 | } | 
 |  | 
 | // We are interested in the total number of blocks used by this file type, and | 
 | // the max number of blocks that we can store (reported as the percentage of | 
 | // used blocks). In order to find out the number of used blocks, we have to | 
 | // substract the empty blocks from the total blocks for each file in the chain. | 
 | void BlockBitmaps::GetFileStats(int index, int* used_count, int* load) { | 
 |   int max_blocks = 0; | 
 |   *used_count = 0; | 
 |   *load = 0; | 
 |   do { | 
 |     int capacity = bitmaps_[index].Capacity(); | 
 |     int used = capacity - bitmaps_[index].EmptyBlocks(); | 
 |     DCHECK_GE(used, 0); | 
 |  | 
 |     max_blocks += capacity; | 
 |     *used_count += used; | 
 |  | 
 |     index = bitmaps_[index].NextFileId(); | 
 |   } while (index); | 
 |  | 
 |   if (max_blocks) | 
 |     *load = *used_count * 100 / max_blocks; | 
 | } | 
 |  | 
 | }  // namespace disk_cache |