Commit 65f2cba2 authored by Maks Orlovich's avatar Maks Orlovich Committed by Commit Bot

disk_cache::MemBackend --- fix GetAvailableRange over big, sparse ranges

Previous code both would perform very badly --- as it's scanning over 4K
increments, which is iffy if there is a big hole in the middle --- and
could also suffer from wrap around in index computations (an alternative
may be to fix just that...)

This change replace hash table keeping track of ranges with the ordered
std::map, which makes finding adjacent kids easy.

Bug: 808561
Change-Id: I3442bbbf6c0e4bc6089ba89265ebb27d739d7edc
Reviewed-on: https://chromium-review.googlesource.com/901862
Commit-Queue: Maks Orlovich <morlovich@chromium.org>
Reviewed-by: default avatarMatt Menke <mmenke@chromium.org>
Cr-Commit-Position: refs/heads/master@{#534731}
parent 053af6f7
......@@ -1821,6 +1821,12 @@ void DiskCacheEntryTest::GetAvailableRange() {
EXPECT_EQ(0, cb.GetResult(rv));
EXPECT_EQ(kTinyLen * 2, start);
// Get a huge range with maximum boundary
start = -1;
rv = entry->GetAvailableRange(0x2100000, std::numeric_limits<int32_t>::max(),
&start, cb.callback());
EXPECT_EQ(0, cb.GetResult(rv));
entry->Close();
}
......
......@@ -12,6 +12,7 @@
#include "base/metrics/histogram_macros.h"
#include "base/strings/stringprintf.h"
#include "base/values.h"
#include "net/base/interval.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "net/disk_cache/memory/mem_backend_impl.h"
......@@ -527,36 +528,35 @@ int MemEntryImpl::InternalGetAvailableRange(int64_t offset,
if (offset < 0 || len < 0 || !start)
return net::ERR_INVALID_ARGUMENT;
MemEntryImpl* current_child = nullptr;
// Find the first child and record the number of empty bytes.
int empty = FindNextChild(offset, len, &current_child);
if (current_child && empty < len) {
*start = offset + empty;
len -= empty;
// Counts the number of continuous bytes.
int continuous = 0;
// This loop scan for continuous bytes.
while (len && current_child) {
// Number of bytes available in this child.
int data_size = current_child->GetDataSize(kSparseData) -
ToChildOffset(*start + continuous);
if (data_size > len)
data_size = len;
// We have found more continuous bytes so increment the count. Also
// decrement the length we should scan.
continuous += data_size;
len -= data_size;
// If the next child is discontinuous, break the loop.
if (FindNextChild(*start + continuous, len, &current_child))
net::Interval<int64_t> requested(offset, offset + len);
// Find the first relevant child, if any --- may have to skip over
// one entry as it may be before the range (consider, for example,
// if the request is for [2048, 10000), while [0, 1024) is a valid range
// for the entry).
EntryMap::const_iterator i = children_->lower_bound(ToChildIndex(offset));
if (i != children_->cend() && !ChildInterval(i).Intersects(requested))
++i;
net::Interval<int64_t> found;
if (i != children_->cend() &&
requested.Intersects(ChildInterval(i), &found)) {
// Found something relevant; now just need to expand this out if next
// children are contiguous and relevant to the request.
while (true) {
++i;
net::Interval<int64_t> relevant_in_next_child;
if (i == children_->cend() ||
!requested.Intersects(ChildInterval(i), &relevant_in_next_child) ||
relevant_in_next_child.min() != found.max()) {
break;
}
found.SpanningUnion(relevant_in_next_child);
}
return continuous;
*start = found.min();
return found.Length();
}
*start = offset;
return 0;
}
......@@ -589,36 +589,17 @@ MemEntryImpl* MemEntryImpl::GetChild(int64_t offset, bool create) {
return nullptr;
}
int MemEntryImpl::FindNextChild(int64_t offset, int len, MemEntryImpl** child) {
DCHECK(child);
*child = nullptr;
int scanned_len = 0;
// This loop tries to find the first existing child.
while (scanned_len < len) {
// This points to the current offset in the child.
int current_child_offset = ToChildOffset(offset + scanned_len);
MemEntryImpl* current_child = GetChild(offset + scanned_len, false);
if (current_child) {
int child_first_pos = current_child->child_first_pos_;
// This points to the first byte that we should be reading from, we need
// to take care of the filled region and the current offset in the child.
int first_pos = std::max(current_child_offset, child_first_pos);
// If the first byte position we should read from doesn't exceed the
// filled region, we have found the first child.
if (first_pos < current_child->GetDataSize(kSparseData)) {
*child = current_child;
// We need to advance the scanned length.
scanned_len += first_pos - current_child_offset;
break;
}
}
scanned_len += kMaxSparseEntrySize - current_child_offset;
}
return scanned_len;
net::Interval<int64_t> MemEntryImpl::ChildInterval(
MemEntryImpl::EntryMap::const_iterator i) {
DCHECK(i != children_->cend());
const MemEntryImpl* child = i->second;
// The valid range in child is [child_first_pos_, DataSize), since the child
// entry ops just use standard disk_cache::Entry API, so DataSize is
// not aware of any hole in the beginning.
int64_t child_responsibility_start = (i->first) * kMaxSparseEntrySize;
return net::Interval<int64_t>(
child_responsibility_start + child->child_first_pos_,
child_responsibility_start + child->GetDataSize(kSparseData));
}
} // namespace disk_cache
......@@ -7,9 +7,9 @@
#include <stdint.h>
#include <map>
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
#include "base/containers/linked_list.h"
......@@ -17,6 +17,7 @@
#include "base/macros.h"
#include "base/time/time.h"
#include "base/trace_event/memory_usage_estimator.h"
#include "net/base/interval.h"
#include "net/base/net_export.h"
#include "net/disk_cache/disk_cache.h"
#include "net/log/net_log_with_source.h"
......@@ -140,7 +141,7 @@ class NET_EXPORT_PRIVATE MemEntryImpl final
MemEntryImpl* parent,
net::NetLog* net_log);
using EntryMap = std::unordered_map<int, MemEntryImpl*>;
using EntryMap = std::map<int, MemEntryImpl*>;
static const int kNumStreams = 3;
......@@ -165,10 +166,11 @@ class NET_EXPORT_PRIVATE MemEntryImpl final
// created.
MemEntryImpl* GetChild(int64_t offset, bool create);
// Finds the first child located within the range [|offset|, |offset + len|).
// Returns the number of bytes ahead of |offset| to reach the first available
// bytes in the entry. The first child found is output to |child|.
int FindNextChild(int64_t offset, int len, MemEntryImpl** child);
// Returns an interval describing what's stored in the child entry pointed to
// by i, in global coordinates.
// Precondition: i != children_.end();
net::Interval<int64_t> ChildInterval(
MemEntryImpl::EntryMap::const_iterator i);
std::string key_;
std::vector<char> data_[kNumStreams]; // User data.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment