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 |