| Index: net/disk_cache/sparse_control.cc
|
| ===================================================================
|
| --- net/disk_cache/sparse_control.cc (revision 0)
|
| +++ net/disk_cache/sparse_control.cc (revision 0)
|
| @@ -0,0 +1,407 @@
|
| +// Copyright (c) 2009 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/sparse_control.h"
|
| +
|
| +#include "base/logging.h"
|
| +#include "base/string_util.h"
|
| +#include "base/time.h"
|
| +#include "net/base/io_buffer.h"
|
| +#include "net/base/net_errors.h"
|
| +#include "net/disk_cache/backend_impl.h"
|
| +#include "net/disk_cache/entry_impl.h"
|
| +
|
| +using base::Time;
|
| +
|
| +namespace {
|
| +
|
| +// Stream of the sparse data index.
|
| +const int kSparseIndex = 2;
|
| +
|
| +// Stream of the sparse data.
|
| +const int kSparseData = 1;
|
| +
|
| +}
|
| +
|
| +namespace disk_cache {
|
| +
|
| +SparseControl::~SparseControl() {
|
| + if (child_)
|
| + CloseChild();
|
| + if (init_)
|
| + WriteSparseData();
|
| +}
|
| +
|
| +int SparseControl::Init() {
|
| + DCHECK(!init_);
|
| +
|
| + // We should not have sparse data for the exposed entry.
|
| + if (entry_->GetDataSize(kSparseData))
|
| + return net::ERR_CACHE_OPERATION_NOT_SUPPORTED;
|
| +
|
| + // Now see if there is something where we store our data.
|
| + int rv = net::OK;
|
| + int data_len = entry_->GetDataSize(kSparseIndex);
|
| + if (!data_len) {
|
| + rv = CreateSparseEntry();
|
| + } else {
|
| + rv = OpenSparseEntry(data_len);
|
| + }
|
| +
|
| + if (rv == net::OK)
|
| + init_ = true;
|
| + return rv;
|
| +}
|
| +
|
| +int SparseControl::StartIO(SparseOperation op, int64 offset, net::IOBuffer* buf,
|
| + int buf_len, net::CompletionCallback* callback) {
|
| + DCHECK(init_);
|
| + // We don't support simultaneous IO for sparse data.
|
| + if (operation_ != kNoOperation)
|
| + return net::ERR_CACHE_OPERATION_NOT_SUPPORTED;
|
| +
|
| + if (offset < 0 || buf_len < 0)
|
| + return net::ERR_INVALID_ARGUMENT;
|
| +
|
| + // We only support up to 64 GB.
|
| + if (offset + buf_len >= 0x1000000000LL)
|
| + return net::ERR_CACHE_OPERATION_NOT_SUPPORTED;
|
| +
|
| + DCHECK(!user_buf_);
|
| + DCHECK(!user_callback_);
|
| +
|
| + // Copy the operation parameters.
|
| + operation_ = op;
|
| + offset_ = offset;
|
| + user_buf_ = new net::ReusedIOBuffer(buf, buf_len);
|
| + buf_len_ = buf_len;
|
| +
|
| + result_ = 0;
|
| + pending_ = false;
|
| + finished_ = false;
|
| + user_callback_ = callback;
|
| +
|
| + DoChildrenIO();
|
| +
|
| + if (!pending_) {
|
| + // Everything was done synchronously.
|
| + operation_ = kNoOperation;
|
| + user_buf_ = NULL;
|
| + user_callback_ = NULL;
|
| + return result_;
|
| + }
|
| +
|
| + return net::ERR_IO_PENDING;
|
| +}
|
| +
|
| +int SparseControl::GetAvailableRange(int64 offset, int len, int64* start) {
|
| + DCHECK(init_);
|
| + NOTIMPLEMENTED();
|
| + return net::ERR_CACHE_OPERATION_NOT_SUPPORTED;
|
| +}
|
| +
|
| +// We are going to start using this entry to store sparse data, so we have to
|
| +// initialize our control info.
|
| +int SparseControl::CreateSparseEntry() {
|
| + // TODO(rvargas): Set/check a flag in EntryStore.
|
| +
|
| + memset(&sparse_header_, 0, sizeof(sparse_header_));
|
| + sparse_header_.signature = Time::Now().ToInternalValue();
|
| + sparse_header_.magic = kIndexMagic;
|
| + sparse_header_.parent_key_len = entry_->GetKey().size();
|
| + children_map_.Resize(kNumSparseBits, true);
|
| +
|
| + // Save the header. The bitmap is saved in the destructor.
|
| + scoped_refptr<net::IOBuffer> buf =
|
| + new net::WrappedIOBuffer(reinterpret_cast<char*>(&sparse_header_));
|
| +
|
| + int rv = entry_->WriteData(kSparseIndex, 0, buf, sizeof(sparse_header_), NULL,
|
| + false);
|
| + if (rv != sizeof(sparse_header_)) {
|
| + DLOG(ERROR) << "Unable to save sparse_header_";
|
| + return net::ERR_CACHE_OPERATION_NOT_SUPPORTED;
|
| + }
|
| + return net::OK;
|
| +}
|
| +
|
| +// We are opening an entry from disk. Make sure that our control data is there.
|
| +int SparseControl::OpenSparseEntry(int data_len) {
|
| + if (data_len < static_cast<int>(sizeof(SparseData)))
|
| + return net::ERR_CACHE_OPERATION_NOT_SUPPORTED;
|
| +
|
| + if (entry_->GetDataSize(kSparseData))
|
| + return net::ERR_CACHE_OPERATION_NOT_SUPPORTED;
|
| +
|
| + // TODO(rvargas): Set/check a flag in EntryStore.
|
| +
|
| + // Dont't go over board with the bitmap. 8 KB gives us offsets up to 64 GB.
|
| + int map_len = data_len - sizeof(sparse_header_);
|
| + if (map_len > 8 * 1024 || map_len % 4)
|
| + return net::ERR_CACHE_OPERATION_NOT_SUPPORTED;
|
| +
|
| + scoped_refptr<net::IOBuffer> buf =
|
| + new net::WrappedIOBuffer(reinterpret_cast<char*>(&sparse_header_));
|
| +
|
| + // Read header.
|
| + int rv = entry_->ReadData(kSparseIndex, 0, buf, sizeof(sparse_header_), NULL);
|
| + if (rv != static_cast<int>(sizeof(sparse_header_)))
|
| + return net::ERR_CACHE_READ_FAILURE;
|
| +
|
| + // The real validation should be performed by the caller. This is just to
|
| + // double check.
|
| + if (sparse_header_.magic != kIndexMagic ||
|
| + sparse_header_.parent_key_len !=
|
| + static_cast<int>(entry_->GetKey().size()))
|
| + return net::ERR_CACHE_OPERATION_NOT_SUPPORTED;
|
| +
|
| + // Read the actual bitmap.
|
| + buf = new net::IOBuffer(map_len);
|
| + rv = entry_->ReadData(kSparseIndex, sizeof(sparse_header_), buf, map_len,
|
| + NULL);
|
| + if (rv != map_len)
|
| + return net::ERR_CACHE_READ_FAILURE;
|
| +
|
| + // Grow the bitmap to the current size and copy the bits.
|
| + children_map_.Resize(map_len * 8, false);
|
| + children_map_.SetMap(reinterpret_cast<uint32*>(buf->data()), map_len);
|
| + return net::OK;
|
| +}
|
| +
|
| +bool SparseControl::OpenChild() {
|
| + DCHECK_GE(result_, 0);
|
| +
|
| + std::string key = GenerateChildKey();
|
| + if (child_) {
|
| + // Keep using the same child or open another one?.
|
| + if (key == child_->GetKey())
|
| + return true;
|
| + CloseChild();
|
| + }
|
| +
|
| + // Se if we are tracking this child.
|
| + bool child_present = ChildPresent();
|
| + if (kReadOperation == operation_ && !child_present)
|
| + return false;
|
| +
|
| + if (!child_present || !entry_->backend_->OpenEntry(key, &child_)) {
|
| + if (!entry_->backend_->CreateEntry(key, &child_)) {
|
| + child_ = NULL;
|
| + result_ = net::ERR_CACHE_READ_FAILURE;
|
| + return false;
|
| + }
|
| + // Write signature.
|
| + InitChildData();
|
| + return true;
|
| + }
|
| +
|
| + // TODO(rvargas): Set/check a flag in EntryStore.
|
| +
|
| + scoped_refptr<net::WrappedIOBuffer> buf =
|
| + new net::WrappedIOBuffer(reinterpret_cast<char*>(&child_data_));
|
| +
|
| + // Read signature.
|
| + int rv = child_->ReadData(kSparseIndex, 0, buf, sizeof(child_data_), NULL);
|
| + if (rv != sizeof(child_data_)) {
|
| + result_ = net::ERR_CACHE_READ_FAILURE;
|
| + return false;
|
| + }
|
| +
|
| + // TODO(rvargas): Proper error handling and check magic etc.
|
| + if (child_data_.header.signature != sparse_header_.signature) {
|
| + result_ = net::ERR_CACHE_READ_FAILURE;
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +void SparseControl::CloseChild() {
|
| + scoped_refptr<net::WrappedIOBuffer> buf =
|
| + new net::WrappedIOBuffer(reinterpret_cast<char*>(&child_data_));
|
| +
|
| + // Save the allocation bitmap before closing the child entry.
|
| + int rv = child_->WriteData(kSparseIndex, 0, buf, sizeof(child_data_),
|
| + NULL, false);
|
| + if (rv != sizeof(child_data_))
|
| + DLOG(ERROR) << "Failed to save child data";
|
| + child_->Close();
|
| + child_ = NULL;
|
| +}
|
| +
|
| +// If this entry is called entry_name, child entreies will be named something
|
| +// like Range_entry_name:XXX:YYY where XXX is the entry signature and YYY is the
|
| +// number of the particular child.
|
| +std::string SparseControl::GenerateChildKey() {
|
| + return StringPrintf("Range_%s:%llx:%llx", entry_->GetKey().c_str(),
|
| + sparse_header_.signature, offset_ >> 20);
|
| +}
|
| +
|
| +bool SparseControl::ChildPresent() {
|
| + int child_bit = static_cast<int>(offset_ >> 20);
|
| + if (children_map_.Size() < child_bit)
|
| + return false;
|
| +
|
| + return children_map_.Get(child_bit);
|
| +}
|
| +
|
| +void SparseControl::SetChildBit() {
|
| + int child_bit = static_cast<int>(offset_ >> 20);
|
| +
|
| + // We may have to increase the bitmap of child entries.
|
| + if (children_map_.Size() <= child_bit)
|
| + children_map_.Resize(Bitmap::RequiredArraySize(child_bit + 1) * 32, true);
|
| +
|
| + children_map_.Set(child_bit, true);
|
| +}
|
| +
|
| +void SparseControl::WriteSparseData() {
|
| + scoped_refptr<net::IOBuffer> buf = new net::WrappedIOBuffer(
|
| + reinterpret_cast<const char*>(children_map_.GetMap()));
|
| +
|
| + int len = children_map_.ArraySize() * 4;
|
| + int rv = entry_->WriteData(kSparseIndex, sizeof(sparse_header_), buf, len,
|
| + NULL, false);
|
| + if (rv != len) {
|
| + DLOG(ERROR) << "Unable to save sparse map";
|
| + }
|
| +}
|
| +
|
| +bool SparseControl::VerifyRange() {
|
| + DCHECK_GE(result_, 0);
|
| +
|
| + child_offset_ = static_cast<int>(offset_) & 0xfffff;
|
| + child_len_ = std::min(buf_len_, 0x100000 - child_offset_);
|
| +
|
| + // We can write to anywhere in this child.
|
| + if (operation_ != kReadOperation)
|
| + return true;
|
| +
|
| + // Check that there are no holes in this range.
|
| + int last_bit = (child_offset_ + child_len_ + 1023) >> 10;
|
| + int start = child_offset_ >> 10;
|
| + if (child_map_.FindNextBit(&start, last_bit, false)) {
|
| + // Something is not here.
|
| + if (start == child_offset_ >> 10)
|
| + return false;
|
| +
|
| + // We have the first part.
|
| + // TODO(rvargas): Avoid coming back here again after the actual read.
|
| + child_len_ = (start << 10) - child_offset_;
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +void SparseControl::UpdateRange(int result) {
|
| + if (result <= 0 || operation_ != kWriteOperation)
|
| + return;
|
| +
|
| + // Write the bitmap.
|
| + int last_bit = (child_offset_ + result + 1023) >> 10;
|
| + child_map_.SetRange(child_offset_ >> 10, last_bit, true);
|
| +
|
| + // TODO(rvargas): Keep track of partial writes so that we don't consider the
|
| + // whole block to be present.
|
| +}
|
| +
|
| +void SparseControl::InitChildData() {
|
| + memset(&child_data_, 0, sizeof(child_data_));
|
| + child_data_.header = sparse_header_;
|
| +
|
| + scoped_refptr<net::WrappedIOBuffer> buf =
|
| + new net::WrappedIOBuffer(reinterpret_cast<char*>(&child_data_));
|
| +
|
| + int rv = child_->WriteData(kSparseIndex, 0, buf, sizeof(child_data_),
|
| + NULL, false);
|
| + if (rv != sizeof(child_data_))
|
| + DLOG(ERROR) << "Failed to save child data";
|
| + SetChildBit();
|
| +}
|
| +
|
| +void SparseControl::DoChildrenIO() {
|
| + while (DoChildIO()) continue;
|
| +
|
| + if (pending_ && finished_)
|
| + DoUserCallback();
|
| +}
|
| +
|
| +bool SparseControl::DoChildIO() {
|
| + finished_ = true;
|
| + if (!buf_len_ || result_ < 0)
|
| + return false;
|
| +
|
| + if (!OpenChild())
|
| + return false;
|
| +
|
| + if (!VerifyRange())
|
| + return false;
|
| +
|
| + // We have more work to do. Let's not trigger a callback to the caller.
|
| + finished_ = false;
|
| + net::CompletionCallback* callback = user_callback_ ? &child_callback_ : NULL;
|
| +
|
| + int rv;
|
| + if (kReadOperation == operation_) {
|
| + rv = child_->ReadData(kSparseData, child_offset_, user_buf_, child_len_,
|
| + callback);
|
| + } else {
|
| + DCHECK(kWriteOperation == operation_);
|
| + rv = child_->WriteData(kSparseData, child_offset_, user_buf_, child_len_,
|
| + callback, false);
|
| + }
|
| +
|
| + if (rv == net::ERR_IO_PENDING) {
|
| + if (!pending_) {
|
| + pending_ = true;
|
| + // The child will protect himself against closing the entry while IO is in
|
| + // progress. However, this entry can still be closed, and that would not
|
| + // be a good thing for us, so we increase the refcount until we're
|
| + // finished doing sparse stuff.
|
| + entry_->AddRef();
|
| + }
|
| + return false;
|
| + }
|
| +
|
| + DoChildIOCompleted(rv);
|
| + return true;
|
| +}
|
| +
|
| +void SparseControl::DoChildIOCompleted(int result) {
|
| + if (result < 0) {
|
| + // We fail the whole operation if we encounter an error.
|
| + result_ = result;
|
| + return;
|
| + }
|
| +
|
| + UpdateRange(result);
|
| +
|
| + result_ += result;
|
| + offset_ += result;
|
| + buf_len_ -= result;
|
| +
|
| + // We'll be reusing the user provided buffer for the next chunk.
|
| + if (buf_len_)
|
| + user_buf_->SetOffset(result_);
|
| +}
|
| +
|
| +void SparseControl::OnChildIOCompleted(int result) {
|
| + DCHECK_NE(net::ERR_IO_PENDING, result);
|
| + DoChildIOCompleted(result);
|
| +
|
| + // We are running a callback from the message loop. It's time to restart what
|
| + // we were doing before.
|
| + DoChildrenIO();
|
| +}
|
| +
|
| +void SparseControl::DoUserCallback() {
|
| + DCHECK(user_callback_);
|
| + net::CompletionCallback* c = user_callback_;
|
| + user_callback_ = NULL;
|
| + user_buf_ = NULL;
|
| + pending_ = false;
|
| + operation_ = kNoOperation;
|
| + entry_->Release(); // Don't touch object after this line.
|
| + c->Run(result_);
|
| +}
|
| +
|
| +} // namespace disk_cache
|
|
|
| Property changes on: net\disk_cache\sparse_control.cc
|
| ___________________________________________________________________
|
| Added: svn:eol-style
|
| + LF
|
|
|
|
|