summaryrefslogtreecommitdiffstats
path: root/net/disk_cache
diff options
context:
space:
mode:
authorclamy@chromium.org <clamy@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-09-18 23:33:01 +0000
committerclamy@chromium.org <clamy@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-09-18 23:33:01 +0000
commitb6b0551fa5cb75a65124acf005a85f0fa57a7eaf (patch)
tree041ca127423fdc069af02afeba37f624b85531fb /net/disk_cache
parentbf1599c4174cc5fc2a85eccd4ba99d08d258436a (diff)
downloadchromium_src-b6b0551fa5cb75a65124acf005a85f0fa57a7eaf.zip
chromium_src-b6b0551fa5cb75a65124acf005a85f0fa57a7eaf.tar.gz
chromium_src-b6b0551fa5cb75a65124acf005a85f0fa57a7eaf.tar.bz2
SimpleCache: merge the first and second stream in one file
This CL merges the two first streams in a single file in order to reduce the disk space consumption of the Simple Cache. BUG=173398 Review URL: https://chromiumcodereview.appspot.com/23983005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@223989 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/disk_cache')
-rw-r--r--net/disk_cache/backend_unittest.cc11
-rw-r--r--net/disk_cache/entry_unittest.cc164
-rw-r--r--net/disk_cache/simple/simple_entry_format.h20
-rw-r--r--net/disk_cache/simple/simple_entry_impl.cc182
-rw-r--r--net/disk_cache/simple/simple_entry_impl.h46
-rw-r--r--net/disk_cache/simple/simple_index_file.cc6
-rw-r--r--net/disk_cache/simple/simple_synchronous_entry.cc348
-rw-r--r--net/disk_cache/simple/simple_synchronous_entry.h69
-rw-r--r--net/disk_cache/simple/simple_test_util.cc2
-rw-r--r--net/disk_cache/simple/simple_util.cc18
-rw-r--r--net/disk_cache/simple/simple_util.h15
11 files changed, 635 insertions, 246 deletions
diff --git a/net/disk_cache/backend_unittest.cc b/net/disk_cache/backend_unittest.cc
index ed07129..ab00178 100644
--- a/net/disk_cache/backend_unittest.cc
+++ b/net/disk_cache/backend_unittest.cc
@@ -3250,7 +3250,7 @@ TEST_F(DiskCacheBackendTest, SimpleCacheOpenMissingFile) {
// Delete one of the files in the entry.
base::FilePath to_delete_file = cache_path_.AppendASCII(
- disk_cache::simple_util::GetFilenameFromKeyAndIndex(key, 0));
+ disk_cache::simple_util::GetFilenameFromKeyAndFileIndex(key, 0));
EXPECT_TRUE(base::PathExists(to_delete_file));
EXPECT_TRUE(disk_cache::DeleteCacheFile(to_delete_file));
@@ -3259,9 +3259,8 @@ TEST_F(DiskCacheBackendTest, SimpleCacheOpenMissingFile) {
// Confirm the rest of the files are gone.
for (int i = 1; i < disk_cache::kSimpleEntryFileCount; ++i) {
- base::FilePath
- should_be_gone_file(cache_path_.AppendASCII(
- disk_cache::simple_util::GetFilenameFromKeyAndIndex(key, i)));
+ base::FilePath should_be_gone_file(cache_path_.AppendASCII(
+ disk_cache::simple_util::GetFilenameFromKeyAndFileIndex(key, i)));
EXPECT_FALSE(base::PathExists(should_be_gone_file));
}
}
@@ -3286,9 +3285,9 @@ TEST_F(DiskCacheBackendTest, SimpleCacheOpenBadFile) {
entry->Close();
entry = NULL;
- // Write an invalid header on stream 1.
+ // Write an invalid header for stream 0 and stream 1.
base::FilePath entry_file1_path = cache_path_.AppendASCII(
- disk_cache::simple_util::GetFilenameFromKeyAndIndex(key, 1));
+ disk_cache::simple_util::GetFilenameFromKeyAndFileIndex(key, 0));
disk_cache::SimpleFileHeader header;
header.initial_magic_number = GG_UINT64_C(0xbadf00d);
diff --git a/net/disk_cache/entry_unittest.cc b/net/disk_cache/entry_unittest.cc
index 8de30f7..705e6b2 100644
--- a/net/disk_cache/entry_unittest.cc
+++ b/net/disk_cache/entry_unittest.cc
@@ -21,6 +21,7 @@
#include "net/disk_cache/mem_entry_impl.h"
#include "net/disk_cache/simple/simple_entry_format.h"
#include "net/disk_cache/simple/simple_entry_impl.h"
+#include "net/disk_cache/simple/simple_synchronous_entry.h"
#include "net/disk_cache/simple/simple_test_util.h"
#include "net/disk_cache/simple/simple_util.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -62,7 +63,7 @@ class DiskCacheEntryTest : public DiskCacheTestWithCache {
void UpdateSparseEntry();
void DoomSparseEntry();
void PartialSparseEntry();
- bool SimpleCacheMakeBadChecksumEntry(const char* key, int* data_size);
+ bool SimpleCacheMakeBadChecksumEntry(const std::string& key, int* data_size);
};
// This part of the test runs on the background thread.
@@ -2414,7 +2415,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheDoomedEntry) {
// Creates an entry with corrupted last byte in stream 0.
// Requires SimpleCacheMode.
-bool DiskCacheEntryTest::SimpleCacheMakeBadChecksumEntry(const char* key,
+bool DiskCacheEntryTest::SimpleCacheMakeBadChecksumEntry(const std::string& key,
int* data_size) {
disk_cache::Entry* entry = NULL;
@@ -2428,21 +2429,21 @@ bool DiskCacheEntryTest::SimpleCacheMakeBadChecksumEntry(const char* key,
scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kDataSize));
base::strlcpy(buffer->data(), data, kDataSize);
- EXPECT_EQ(kDataSize, WriteData(entry, 0, 0, buffer.get(), kDataSize, false));
+ EXPECT_EQ(kDataSize, WriteData(entry, 1, 0, buffer.get(), kDataSize, false));
entry->Close();
entry = NULL;
// Corrupt the last byte of the data.
base::FilePath entry_file0_path = cache_path_.AppendASCII(
- disk_cache::simple_util::GetFilenameFromKeyAndIndex(key, 0));
+ disk_cache::simple_util::GetFilenameFromKeyAndFileIndex(key, 0));
int flags = base::PLATFORM_FILE_WRITE | base::PLATFORM_FILE_OPEN;
base::PlatformFile entry_file0 =
base::CreatePlatformFile(entry_file0_path, flags, NULL, NULL);
if (entry_file0 == base::kInvalidPlatformFileValue)
return false;
+
int64 file_offset =
- disk_cache::simple_util::GetFileOffsetFromKeyAndDataOffset(
- key, kDataSize - 2);
+ sizeof(disk_cache::SimpleFileHeader) + key.size() + kDataSize - 2;
EXPECT_EQ(1, base::WritePlatformFile(entry_file0, file_offset, "X", 1));
if (!base::ClosePlatformFile(entry_file0))
return false;
@@ -2466,10 +2467,10 @@ TEST_F(DiskCacheEntryTest, SimpleCacheBadChecksum) {
ScopedEntryPtr entry_closer(entry);
const int kReadBufferSize = 200;
- EXPECT_GE(kReadBufferSize, entry->GetDataSize(0));
+ EXPECT_GE(kReadBufferSize, entry->GetDataSize(1));
scoped_refptr<net::IOBuffer> read_buffer(new net::IOBuffer(kReadBufferSize));
EXPECT_EQ(net::ERR_CACHE_CHECKSUM_MISMATCH,
- ReadData(entry, 0, 0, read_buffer.get(), kReadBufferSize));
+ ReadData(entry, 1, 0, read_buffer.get(), kReadBufferSize));
}
// Tests that an entry that has had an IO error occur can still be Doomed().
@@ -2488,10 +2489,10 @@ TEST_F(DiskCacheEntryTest, SimpleCacheErrorThenDoom) {
ScopedEntryPtr entry_closer(entry);
const int kReadBufferSize = 200;
- EXPECT_GE(kReadBufferSize, entry->GetDataSize(0));
+ EXPECT_GE(kReadBufferSize, entry->GetDataSize(1));
scoped_refptr<net::IOBuffer> read_buffer(new net::IOBuffer(kReadBufferSize));
EXPECT_EQ(net::ERR_CACHE_CHECKSUM_MISMATCH,
- ReadData(entry, 0, 0, read_buffer.get(), kReadBufferSize));
+ ReadData(entry, 1, 0, read_buffer.get(), kReadBufferSize));
entry->Doom(); // Should not crash.
}
@@ -2530,7 +2531,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheNoEOF) {
// record.
int kTruncationBytes = -implicit_cast<int>(sizeof(disk_cache::SimpleFileEOF));
const base::FilePath entry_path = cache_path_.AppendASCII(
- disk_cache::simple_util::GetFilenameFromKeyAndIndex(key, 0));
+ disk_cache::simple_util::GetFilenameFromKeyAndFileIndex(key, 0));
const int64 invalid_size =
disk_cache::simple_util::GetFileSizeFromKeyAndDataSize(key,
kTruncationBytes);
@@ -2558,13 +2559,12 @@ TEST_F(DiskCacheEntryTest, SimpleCacheNonOptimisticOperationsBasic) {
CacheTestFillBuffer(write_buffer->data(), write_buffer->size(), false);
EXPECT_EQ(
write_buffer->size(),
- WriteData(entry, 0, 0, write_buffer.get(), write_buffer->size(), false));
+ WriteData(entry, 1, 0, write_buffer.get(), write_buffer->size(), false));
scoped_refptr<net::IOBufferWithSize> read_buffer(
new net::IOBufferWithSize(kBufferSize));
- EXPECT_EQ(
- read_buffer->size(),
- ReadData(entry, 0, 0, read_buffer.get(), read_buffer->size()));
+ EXPECT_EQ(read_buffer->size(),
+ ReadData(entry, 1, 0, read_buffer.get(), read_buffer->size()));
}
TEST_F(DiskCacheEntryTest, SimpleCacheNonOptimisticOperationsDontBlock) {
@@ -2591,7 +2591,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheNonOptimisticOperationsDontBlock) {
CacheTestFillBuffer(write_buffer->data(), write_buffer->size(), false);
CallbackTest write_callback(&helper, false);
int ret = entry->WriteData(
- 0,
+ 1,
0,
write_buffer.get(),
write_buffer->size(),
@@ -2624,7 +2624,7 @@ TEST_F(DiskCacheEntryTest,
CacheTestFillBuffer(write_buffer->data(), write_buffer->size(), false);
CallbackTest write_callback(&helper, false);
int ret = entry->WriteData(
- 0,
+ 1,
0,
write_buffer.get(),
write_buffer->size(),
@@ -2637,7 +2637,7 @@ TEST_F(DiskCacheEntryTest,
new net::IOBufferWithSize(kBufferSize));
CallbackTest read_callback(&helper, false);
ret = entry->ReadData(
- 0,
+ 1,
0,
read_buffer.get(),
read_buffer->size(),
@@ -2689,7 +2689,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheOptimistic) {
// This write may or may not be optimistic (it depends if the previous
// optimistic create already finished by the time we call the write here).
int ret = entry->WriteData(
- 0,
+ 1,
0,
buffer1.get(),
kSize1,
@@ -2702,7 +2702,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheOptimistic) {
// This Read must not be optimistic, since we don't support that yet.
EXPECT_EQ(net::ERR_IO_PENDING,
entry->ReadData(
- 0,
+ 1,
0,
buffer1_read.get(),
kSize1,
@@ -2715,7 +2715,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheOptimistic) {
// should be empty, so the next Write operation must run as optimistic.
EXPECT_EQ(kSize2,
entry->WriteData(
- 0,
+ 1,
0,
buffer2.get(),
kSize2,
@@ -2726,7 +2726,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheOptimistic) {
// operation finishes and we can then test for HasOneRef() below.
EXPECT_EQ(net::ERR_IO_PENDING,
entry->ReadData(
- 0,
+ 1,
0,
buffer2_read.get(),
kSize2,
@@ -2832,7 +2832,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheOptimistic4) {
// operation finishes. Write must fail since we are writing in a closed entry.
EXPECT_EQ(
net::ERR_IO_PENDING,
- entry->WriteData(0, 0, buffer1.get(), kSize1, cb.callback(), false));
+ entry->WriteData(1, 0, buffer1.get(), kSize1, cb.callback(), false));
EXPECT_EQ(net::ERR_FAILED, cb.GetResult(net::ERR_IO_PENDING));
// Finish running the pending tasks so that we fully complete the close
@@ -2861,12 +2861,12 @@ TEST_F(DiskCacheEntryTest, SimpleCacheOptimistic4) {
// entry.
EXPECT_EQ(kSize1,
entry2->WriteData(
- 0, 0, buffer1.get(), kSize1, net::CompletionCallback(), false));
+ 1, 0, buffer1.get(), kSize1, net::CompletionCallback(), false));
// Lets do another read so we block until both the write and the read
// operation finishes and we can then test for HasOneRef() below.
EXPECT_EQ(net::ERR_IO_PENDING,
- entry2->ReadData(0, 0, buffer1.get(), kSize1, cb.callback()));
+ entry2->ReadData(1, 0, buffer1.get(), kSize1, cb.callback()));
EXPECT_EQ(kSize1, cb.GetResult(net::ERR_IO_PENDING));
// Check that we are not leaking.
@@ -2897,11 +2897,11 @@ TEST_F(DiskCacheEntryTest, SimpleCacheOptimistic5) {
EXPECT_EQ(
net::ERR_IO_PENDING,
- entry->WriteData(0, 0, buffer1.get(), kSize1, cb.callback(), false));
+ entry->WriteData(1, 0, buffer1.get(), kSize1, cb.callback(), false));
EXPECT_EQ(kSize1, cb.GetResult(net::ERR_IO_PENDING));
EXPECT_EQ(net::ERR_IO_PENDING,
- entry->ReadData(0, 0, buffer1.get(), kSize1, cb.callback()));
+ entry->ReadData(1, 0, buffer1.get(), kSize1, cb.callback()));
EXPECT_EQ(kSize1, cb.GetResult(net::ERR_IO_PENDING));
// Check that we are not leaking.
@@ -2931,7 +2931,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheOptimistic6) {
EXPECT_EQ(
net::ERR_IO_PENDING,
- entry->WriteData(0, 0, buffer1.get(), kSize1, cb.callback(), false));
+ entry->WriteData(1, 0, buffer1.get(), kSize1, cb.callback(), false));
EXPECT_EQ(kSize1, cb.GetResult(net::ERR_IO_PENDING));
entry->Doom();
@@ -2939,7 +2939,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheOptimistic6) {
// This Read must not be optimistic, since we don't support that yet.
EXPECT_EQ(net::ERR_IO_PENDING,
- entry->ReadData(0, 0, buffer1_read.get(), kSize1, cb.callback()));
+ entry->ReadData(1, 0, buffer1_read.get(), kSize1, cb.callback()));
EXPECT_EQ(kSize1, cb.GetResult(net::ERR_IO_PENDING));
EXPECT_EQ(0, memcmp(buffer1->data(), buffer1_read->data(), kSize1));
@@ -2970,7 +2970,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheOptimisticWriteReleases) {
// operations. To ensure the queue is empty, we issue a write and wait until
// it completes.
EXPECT_EQ(kWriteSize,
- WriteData(entry, 0, 0, buffer1.get(), kWriteSize, false));
+ WriteData(entry, 1, 0, buffer1.get(), kWriteSize, false));
EXPECT_TRUE(buffer1->HasOneRef());
// Finally, we should perform an optimistic write and confirm that all
@@ -3015,7 +3015,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheCreateDoomRace) {
for (int i = 0; i < disk_cache::kSimpleEntryFileCount; ++i) {
base::FilePath entry_file_path = cache_path_.AppendASCII(
- disk_cache::simple_util::GetFilenameFromKeyAndIndex(key, i));
+ disk_cache::simple_util::GetFilenameFromKeyAndFileIndex(key, i));
base::PlatformFileInfo info;
EXPECT_FALSE(file_util::GetFileInfo(entry_file_path, &info));
}
@@ -3157,7 +3157,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheEvictOldEntries) {
scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kWriteSize));
CacheTestFillBuffer(buffer->data(), kWriteSize, false);
EXPECT_EQ(kWriteSize,
- WriteData(entry, 0, 0, buffer.get(), kWriteSize, false));
+ WriteData(entry, 1, 0, buffer.get(), kWriteSize, false));
entry->Close();
AddDelay();
@@ -3166,7 +3166,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheEvictOldEntries) {
ASSERT_EQ(net::OK, CreateEntry(key2 + base::StringPrintf("%d", i), &entry));
ScopedEntryPtr entry_closer(entry);
EXPECT_EQ(kWriteSize,
- WriteData(entry, 0, 0, buffer.get(), kWriteSize, false));
+ WriteData(entry, 1, 0, buffer.get(), kWriteSize, false));
}
// TODO(pasko): Find a way to wait for the eviction task(s) to finish by using
@@ -3202,7 +3202,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheInFlightTruncate) {
ASSERT_EQ(net::OK, CreateEntry(key, &entry));
EXPECT_EQ(kBufferSize,
- WriteData(entry, 0, 0, write_buffer.get(), kBufferSize, false));
+ WriteData(entry, 1, 0, write_buffer.get(), kBufferSize, false));
entry->Close();
entry = NULL;
@@ -3217,7 +3217,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheInFlightTruncate) {
scoped_refptr<net::IOBuffer> read_buffer(new net::IOBuffer(kReadBufferSize));
CallbackTest read_callback(&helper, false);
EXPECT_EQ(net::ERR_IO_PENDING,
- entry->ReadData(0,
+ entry->ReadData(1,
0,
read_buffer.get(),
kReadBufferSize,
@@ -3231,7 +3231,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheInFlightTruncate) {
CacheTestFillBuffer(truncate_buffer->data(), kReadBufferSize, false);
CallbackTest truncate_callback(&helper, false);
EXPECT_EQ(net::ERR_IO_PENDING,
- entry->WriteData(0,
+ entry->WriteData(1,
0,
truncate_buffer.get(),
kReadBufferSize,
@@ -3271,7 +3271,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheInFlightRead) {
CallbackTest write_callback(&helper, false);
EXPECT_EQ(net::ERR_IO_PENDING,
- entry->WriteData(0,
+ entry->WriteData(1,
0,
write_buffer.get(),
kBufferSize,
@@ -3283,7 +3283,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheInFlightRead) {
scoped_refptr<net::IOBuffer> read_buffer(new net::IOBuffer(kBufferSize));
CallbackTest read_callback(&helper, false);
EXPECT_EQ(net::ERR_IO_PENDING,
- entry->ReadData(0,
+ entry->ReadData(1,
0,
read_buffer.get(),
kBufferSize,
@@ -3367,19 +3367,19 @@ TEST_F(DiskCacheEntryTest, SimpleCacheMultipleReadersCheckCRC2) {
disk_cache::Entry* entry = NULL;
ASSERT_EQ(net::OK, OpenEntry(key, &entry));
ScopedEntryPtr entry_closer(entry);
- EXPECT_EQ(1, ReadData(entry, 0, 0, read_buffer1.get(), 1));
+ EXPECT_EQ(1, ReadData(entry, 1, 0, read_buffer1.get(), 1));
// Advance the 2nd reader by the same amount.
disk_cache::Entry* entry2 = NULL;
EXPECT_EQ(net::OK, OpenEntry(key, &entry2));
ScopedEntryPtr entry2_closer(entry2);
- EXPECT_EQ(1, ReadData(entry2, 0, 0, read_buffer2.get(), 1));
+ EXPECT_EQ(1, ReadData(entry2, 1, 0, read_buffer2.get(), 1));
// Continue reading 1st.
- EXPECT_GT(0, ReadData(entry, 0, 1, read_buffer1.get(), size));
+ EXPECT_GT(0, ReadData(entry, 1, 1, read_buffer1.get(), size));
// This read should fail as well because we have previous read failures.
- EXPECT_GT(0, ReadData(entry2, 0, 1, read_buffer2.get(), 1));
+ EXPECT_GT(0, ReadData(entry2, 1, 1, read_buffer2.get(), 1));
DisableIntegrityCheck();
}
@@ -3403,7 +3403,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheReadCombineCRC) {
ASSERT_EQ(net::OK, CreateEntry(key, &entry));
EXPECT_NE(null, entry);
- EXPECT_EQ(kSize, WriteData(entry, 0, 0, buffer1.get(), kSize, false));
+ EXPECT_EQ(kSize, WriteData(entry, 1, 0, buffer1.get(), kSize, false));
entry->Close();
disk_cache::Entry* entry2 = NULL;
@@ -3414,14 +3414,14 @@ TEST_F(DiskCacheEntryTest, SimpleCacheReadCombineCRC) {
int offset = 0;
int buf_len = kHalfSize;
scoped_refptr<net::IOBuffer> buffer1_read1(new net::IOBuffer(buf_len));
- EXPECT_EQ(buf_len, ReadData(entry2, 0, offset, buffer1_read1.get(), buf_len));
+ EXPECT_EQ(buf_len, ReadData(entry2, 1, offset, buffer1_read1.get(), buf_len));
EXPECT_EQ(0, memcmp(buffer1->data(), buffer1_read1->data(), buf_len));
// Read the second half of the data.
offset = buf_len;
buf_len = kHalfSize;
scoped_refptr<net::IOBuffer> buffer1_read2(new net::IOBuffer(buf_len));
- EXPECT_EQ(buf_len, ReadData(entry2, 0, offset, buffer1_read2.get(), buf_len));
+ EXPECT_EQ(buf_len, ReadData(entry2, 1, offset, buffer1_read2.get(), buf_len));
char* buffer1_data = buffer1->data() + offset;
EXPECT_EQ(0, memcmp(buffer1_data, buffer1_read2->data(), buf_len));
@@ -3483,4 +3483,78 @@ TEST_F(DiskCacheEntryTest, SimpleCacheNonSequentialWrite) {
entry = NULL;
}
+// Test that changing stream1 size does not affect stream0 (stream0 and stream1
+// are stored in the same file in Simple Cache).
+TEST_F(DiskCacheEntryTest, SimpleCacheStream1SizeChanges) {
+ SetSimpleCacheMode();
+ InitCache();
+ disk_cache::Entry* entry = NULL;
+ const char key[] = "the key";
+ const int kSize = 100;
+ scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kSize));
+ scoped_refptr<net::IOBuffer> buffer_read(new net::IOBuffer(kSize));
+ CacheTestFillBuffer(buffer->data(), kSize, false);
+
+ ASSERT_EQ(net::OK, CreateEntry(key, &entry));
+ EXPECT_TRUE(entry);
+
+ // Write something into stream0.
+ EXPECT_EQ(kSize, WriteData(entry, 0, 0, buffer.get(), kSize, false));
+ EXPECT_EQ(kSize, ReadData(entry, 0, 0, buffer_read.get(), kSize));
+ EXPECT_EQ(0, memcmp(buffer->data(), buffer_read->data(), kSize));
+ entry->Close();
+
+ // Extend stream1.
+ ASSERT_EQ(net::OK, OpenEntry(key, &entry));
+ int stream1_size = 100;
+ EXPECT_EQ(0, WriteData(entry, 1, stream1_size, buffer.get(), 0, false));
+ EXPECT_EQ(stream1_size, entry->GetDataSize(1));
+ entry->Close();
+
+ // Check that stream0 data has not been modified and that the EOF record for
+ // stream 0 contains a crc.
+ // The entry needs to be reopened before checking the crc: Open will perform
+ // the synchronization with the previous Close. This ensures the EOF records
+ // have been written to disk before we attempt to read them independently.
+ ASSERT_EQ(net::OK, OpenEntry(key, &entry));
+ base::FilePath entry_file0_path = cache_path_.AppendASCII(
+ disk_cache::simple_util::GetFilenameFromKeyAndFileIndex(key, 0));
+ int flags = base::PLATFORM_FILE_READ | base::PLATFORM_FILE_OPEN;
+ base::PlatformFile entry_file0 =
+ base::CreatePlatformFile(entry_file0_path, flags, NULL, NULL);
+ ASSERT_TRUE(entry_file0 != base::kInvalidPlatformFileValue);
+
+ int data_size[disk_cache::kSimpleEntryStreamCount] = {kSize, stream1_size, 0};
+ disk_cache::SimpleEntryStat entry_stat(
+ base::Time::Now(), base::Time::Now(), data_size);
+ int eof_offset = entry_stat.GetEOFOffsetInFile(key, 0);
+ disk_cache::SimpleFileEOF eof_record;
+ ASSERT_EQ(static_cast<int>(sizeof(eof_record)), base::ReadPlatformFile(
+ entry_file0,
+ eof_offset,
+ reinterpret_cast<char*>(&eof_record),
+ sizeof(eof_record)));
+ EXPECT_EQ(disk_cache::kSimpleFinalMagicNumber, eof_record.final_magic_number);
+ EXPECT_TRUE((eof_record.flags & disk_cache::SimpleFileEOF::FLAG_HAS_CRC32) ==
+ disk_cache::SimpleFileEOF::FLAG_HAS_CRC32);
+
+ buffer_read = new net::IOBuffer(kSize);
+ EXPECT_EQ(kSize, ReadData(entry, 0, 0, buffer_read.get(), kSize));
+ EXPECT_EQ(0, memcmp(buffer->data(), buffer_read->data(), kSize));
+
+ // Shrink stream1.
+ stream1_size = 50;
+ EXPECT_EQ(0, WriteData(entry, 1, stream1_size, buffer.get(), 0, true));
+ EXPECT_EQ(stream1_size, entry->GetDataSize(1));
+ entry->Close();
+
+ // Check that stream0 data has not been modified.
+ buffer_read = new net::IOBuffer(kSize);
+ ASSERT_EQ(net::OK, OpenEntry(key, &entry));
+ EXPECT_EQ(kSize, ReadData(entry, 0, 0, buffer_read.get(), kSize));
+ EXPECT_EQ(0, memcmp(buffer->data(), buffer_read->data(), kSize));
+ entry->Close();
+ entry = NULL;
+}
+
#endif // defined(OS_POSIX)
diff --git a/net/disk_cache/simple/simple_entry_format.h b/net/disk_cache/simple/simple_entry_format.h
index d06ab11..cfd598db 100644
--- a/net/disk_cache/simple/simple_entry_format.h
+++ b/net/disk_cache/simple/simple_entry_format.h
@@ -19,17 +19,23 @@ namespace disk_cache {
const uint64 kSimpleInitialMagicNumber = GG_UINT64_C(0xfcfb6d1ba7725c30);
const uint64 kSimpleFinalMagicNumber = GG_UINT64_C(0xf4fa6f45970d41d8);
-// A file in the Simple cache consists of a SimpleFileHeader followed
-// by data.
+// A file containing stream 0 and stream 1 in the Simple cache consists of:
+// - a SimpleFileHeader.
+// - the key.
+// - the data from stream 1.
+// - a SimpleFileEOF record for stream 1.
+// - the data from stream 0.
+// - a SimpleFileEOF record for stream 0.
-// A file in the Simple cache consists of:
+// A file containing stream 2 in the Simple cache consists of:
// - a SimpleFileHeader.
// - the key.
// - the data.
// - at the end, a SimpleFileEOF record.
-const uint32 kSimpleVersion = 4;
+const uint32 kSimpleVersion = 5;
-static const int kSimpleEntryFileCount = 3;
+static const int kSimpleEntryFileCount = 2;
+static const int kSimpleEntryStreamCount = 3;
struct NET_EXPORT_PRIVATE SimpleFileHeader {
SimpleFileHeader();
@@ -40,7 +46,7 @@ struct NET_EXPORT_PRIVATE SimpleFileHeader {
uint32 key_hash;
};
-struct SimpleFileEOF {
+struct NET_EXPORT_PRIVATE SimpleFileEOF {
enum Flags {
FLAG_HAS_CRC32 = (1U << 0),
};
@@ -50,6 +56,8 @@ struct SimpleFileEOF {
uint64 final_magic_number;
uint32 flags;
uint32 data_crc32;
+ // |stream_size| is only used in the EOF record for stream 0.
+ uint32 stream_size;
};
} // namespace disk_cache
diff --git a/net/disk_cache/simple/simple_entry_impl.cc b/net/disk_cache/simple/simple_entry_impl.cc
index c4cd1d3..5f24564 100644
--- a/net/disk_cache/simple/simple_entry_impl.cc
+++ b/net/disk_cache/simple/simple_entry_impl.cc
@@ -174,7 +174,8 @@ SimpleEntryImpl::SimpleEntryImpl(net::CacheType cache_type,
state_(STATE_UNINITIALIZED),
synchronous_entry_(NULL),
net_log_(net::BoundNetLog::Make(
- net_log, net::NetLog::SOURCE_DISK_CACHE_ENTRY)) {
+ net_log, net::NetLog::SOURCE_DISK_CACHE_ENTRY)),
+ stream_0_data_(new net::GrowableIOBuffer()) {
COMPILE_ASSERT(arraysize(data_size_) == arraysize(crc32s_end_offset_),
arrays_should_be_same_size);
COMPILE_ASSERT(arraysize(data_size_) == arraysize(crc32s_),
@@ -337,7 +338,7 @@ int SimpleEntryImpl::ReadData(int stream_index,
false));
}
- if (stream_index < 0 || stream_index >= kSimpleEntryFileCount ||
+ if (stream_index < 0 || stream_index >= kSimpleEntryStreamCount ||
buf_len < 0) {
if (net_log_.IsLoggingAllEvents()) {
net_log_.AddEvent(net::NetLog::TYPE_SIMPLE_CACHE_ENTRY_READ_END,
@@ -358,6 +359,8 @@ int SimpleEntryImpl::ReadData(int stream_index,
return 0;
}
+ // TODO(clamy): return immediatly when reading from stream 0.
+
// TODO(felipeg): Optimization: Add support for truly parallel read
// operations.
bool alone_in_queue =
@@ -383,8 +386,8 @@ int SimpleEntryImpl::WriteData(int stream_index,
truncate));
}
- if (stream_index < 0 || stream_index >= kSimpleEntryFileCount || offset < 0 ||
- buf_len < 0) {
+ if (stream_index < 0 || stream_index >= kSimpleEntryStreamCount ||
+ offset < 0 || buf_len < 0) {
if (net_log_.IsLoggingAllEvents()) {
net_log_.AddEvent(
net::NetLog::TYPE_SIMPLE_CACHE_ENTRY_WRITE_END,
@@ -404,16 +407,11 @@ int SimpleEntryImpl::WriteData(int stream_index,
}
ScopedOperationRunner operation_runner(this);
- // Currently, Simple Cache is only used for HTTP, which stores the headers in
- // stream 0 and always writes them with a single, truncating write. Detect
- // these writes and record the size and size changes of the headers. Also,
- // note writes to stream 0 that violate those assumptions.
- if (stream_index == 0) {
- if (offset == 0 && truncate)
- RecordHeaderSizeChange(cache_type_, data_size_[0], buf_len);
- else
- RecordUnexpectedStream0Write(cache_type_);
- }
+ // Stream 0 data is kept in memory, so can be written immediatly if there are
+ // no IO operations pending.
+ if (stream_index == 0 && state_ == STATE_READY &&
+ pending_operations_.size() == 0)
+ return SetStream0Data(buf, offset, buf_len, truncate);
// We can only do optimistic Write if there is no pending operations, so
// that we are sure that the next call to RunNextOperationIfNeeded will
@@ -694,7 +692,7 @@ void SimpleEntryImpl::CreateEntryInternal(bool have_index,
last_used_ = last_modified_ = base::Time::Now();
// If creation succeeds, we should mark all streams to be saved on close.
- for (int i = 0; i < kSimpleEntryFileCount; ++i)
+ for (int i = 0; i < kSimpleEntryStreamCount; ++i)
have_written_[i] = true;
const base::TimeTicks start_time = base::TimeTicks::Now();
@@ -729,7 +727,7 @@ void SimpleEntryImpl::CloseInternal() {
if (state_ == STATE_READY) {
DCHECK(synchronous_entry_);
state_ = STATE_IO_PENDING;
- for (int i = 0; i < kSimpleEntryFileCount; ++i) {
+ for (int i = 0; i < kSimpleEntryStreamCount; ++i) {
if (have_written_[i]) {
if (GetDataSize(i) == crc32s_end_offset_[i]) {
int32 crc = GetDataSize(i) == 0 ? crc32(0, Z_NULL, 0) : crc32s_[i];
@@ -748,12 +746,13 @@ void SimpleEntryImpl::CloseInternal() {
base::Bind(&SimpleSynchronousEntry::Close,
base::Unretained(synchronous_entry_),
SimpleEntryStat(last_used_, last_modified_, data_size_),
- base::Passed(&crc32s_to_write));
+ base::Passed(&crc32s_to_write),
+ stream_0_data_);
Closure reply = base::Bind(&SimpleEntryImpl::CloseOperationComplete, this);
synchronous_entry_ = NULL;
worker_pool_->PostTaskAndReply(FROM_HERE, task, reply);
- for (int i = 0; i < kSimpleEntryFileCount; ++i) {
+ for (int i = 0; i < kSimpleEntryStreamCount; ++i) {
if (!have_written_[i]) {
SIMPLE_CACHE_UMA(ENUMERATION,
"CheckCRCResult", cache_type_,
@@ -808,20 +807,31 @@ void SimpleEntryImpl::ReadDataInternal(int stream_index,
buf_len = std::min(buf_len, GetDataSize(stream_index) - offset);
+ // Since stream 0 data is kept in memory, it is read immediately.
+ if (stream_index == 0) {
+ int ret_value = ReadStream0Data(buf, offset, buf_len);
+ if (!callback.is_null()) {
+ MessageLoopProxy::current()->PostTask(FROM_HERE,
+ base::Bind(callback, ret_value));
+ }
+ return;
+ }
+
state_ = STATE_IO_PENDING;
if (!doomed_ && backend_.get())
backend_->index()->UseIfExists(entry_hash_);
scoped_ptr<uint32> read_crc32(new uint32());
scoped_ptr<int> result(new int());
- scoped_ptr<base::Time> last_used(new base::Time());
+ scoped_ptr<SimpleEntryStat> entry_stat(
+ new SimpleEntryStat(last_used_, last_modified_, data_size_));
Closure task = base::Bind(
&SimpleSynchronousEntry::ReadData,
base::Unretained(synchronous_entry_),
SimpleSynchronousEntry::EntryOperationData(stream_index, offset, buf_len),
make_scoped_refptr(buf),
read_crc32.get(),
- last_used.get(),
+ entry_stat.get(),
result.get());
Closure reply = base::Bind(&SimpleEntryImpl::ReadOperationComplete,
this,
@@ -829,7 +839,7 @@ void SimpleEntryImpl::ReadDataInternal(int stream_index,
offset,
callback,
base::Passed(&read_crc32),
- base::Passed(&last_used),
+ base::Passed(&entry_stat),
base::Passed(&result));
worker_pool_->PostTaskAndReply(FROM_HERE, task, reply);
}
@@ -866,24 +876,22 @@ void SimpleEntryImpl::WriteDataInternal(int stream_index,
}
DCHECK_EQ(STATE_READY, state_);
+
+ // Since stream 0 data is kept in memory, it will be written immediatly.
+ if (stream_index == 0) {
+ int ret_value = SetStream0Data(buf, offset, buf_len, truncate);
+ if (!callback.is_null()) {
+ MessageLoopProxy::current()->PostTask(FROM_HERE,
+ base::Bind(callback, ret_value));
+ }
+ return;
+ }
+
state_ = STATE_IO_PENDING;
if (!doomed_ && backend_.get())
backend_->index()->UseIfExists(entry_hash_);
- // It is easy to incrementally compute the CRC from [0 .. |offset + buf_len|)
- // if |offset == 0| or we have already computed the CRC for [0 .. offset).
- // We rely on most write operations being sequential, start to end to compute
- // the crc of the data. When we write to an entry and close without having
- // done a sequential write, we don't check the CRC on read.
- if (offset == 0 || crc32s_end_offset_[stream_index] == offset) {
- uint32 initial_crc = (offset != 0) ? crc32s_[stream_index]
- : crc32(0, Z_NULL, 0);
- if (buf_len > 0) {
- crc32s_[stream_index] = crc32(initial_crc,
- reinterpret_cast<const Bytef*>(buf->data()),
- buf_len);
- }
- crc32s_end_offset_[stream_index] = offset + buf_len;
- }
+
+ AdvanceCrc(buf, offset, buf_len, stream_index);
// |entry_stat| needs to be initialized before modifying |data_size_|.
scoped_ptr<SimpleEntryStat> entry_stat(
@@ -900,6 +908,10 @@ void SimpleEntryImpl::WriteDataInternal(int stream_index,
last_used_ = last_modified_ = base::Time::Now();
have_written_[stream_index] = true;
+ // Writing on stream 1 affects the placement of stream 0 in the file, the EOF
+ // record will have to be rewritten.
+ if (stream_index == 1)
+ have_written_[0] = true;
scoped_ptr<int> result(new int());
Closure task = base::Bind(&SimpleSynchronousEntry::WriteData,
@@ -956,6 +968,13 @@ void SimpleEntryImpl::CreationOperationComplete(
state_ = STATE_READY;
synchronous_entry_ = in_results->sync_entry;
+ if (in_results->stream_0_data) {
+ stream_0_data_ = in_results->stream_0_data;
+ // The crc was read in SimpleSynchronousEntry.
+ crc_check_state_[0] = CRC_CHECK_DONE;
+ crc32s_[0] = in_results->stream_0_crc32;
+ crc32s_end_offset_[0] = in_results->entry_stat.data_size(0);
+ }
if (key_.empty()) {
SetKey(synchronous_entry_->key());
} else {
@@ -1003,7 +1022,7 @@ void SimpleEntryImpl::ReadOperationComplete(
int offset,
const CompletionCallback& completion_callback,
scoped_ptr<uint32> read_crc32,
- scoped_ptr<base::Time> last_used,
+ scoped_ptr<SimpleEntryStat> entry_stat,
scoped_ptr<int> result) {
DCHECK(io_thread_checker_.CalledOnValidThread());
DCHECK(synchronous_entry_);
@@ -1039,7 +1058,7 @@ void SimpleEntryImpl::ReadOperationComplete(
Closure task = base::Bind(&SimpleSynchronousEntry::CheckEOFRecord,
base::Unretained(synchronous_entry_),
stream_index,
- data_size_[stream_index],
+ *entry_stat,
crc32s_[stream_index],
new_result.get());
Closure reply = base::Bind(&SimpleEntryImpl::ChecksumOperationComplete,
@@ -1068,10 +1087,7 @@ void SimpleEntryImpl::ReadOperationComplete(
}
EntryOperationComplete(
- stream_index,
- completion_callback,
- SimpleEntryStat(*last_used, last_modified_, data_size_),
- result.Pass());
+ stream_index, completion_callback, *entry_stat, result.Pass());
}
void SimpleEntryImpl::WriteOperationComplete(
@@ -1158,10 +1174,10 @@ void SimpleEntryImpl::UpdateDataFromEntryStat(
DCHECK(synchronous_entry_);
DCHECK_EQ(STATE_READY, state_);
- last_used_ = entry_stat.last_used;
- last_modified_ = entry_stat.last_modified;
- for (int i = 0; i < kSimpleEntryFileCount; ++i) {
- data_size_[i] = entry_stat.data_size[i];
+ last_used_ = entry_stat.last_used();
+ last_modified_ = entry_stat.last_modified();
+ for (int i = 0; i < kSimpleEntryStreamCount; ++i) {
+ data_size_[i] = entry_stat.data_size(i);
}
if (!doomed_ && backend_.get())
backend_->index()->UpdateEntrySize(entry_hash_, GetDiskUsage());
@@ -1169,7 +1185,7 @@ void SimpleEntryImpl::UpdateDataFromEntryStat(
int64 SimpleEntryImpl::GetDiskUsage() const {
int64 file_size = 0;
- for (int i = 0; i < kSimpleEntryFileCount; ++i) {
+ for (int i = 0; i < kSimpleEntryStreamCount; ++i) {
file_size +=
simple_util::GetFileSizeFromKeyAndDataSize(key_, data_size_[i]);
}
@@ -1247,4 +1263,76 @@ void SimpleEntryImpl::RecordWriteDependencyType(
type, WRITE_DEPENDENCY_TYPE_MAX);
}
+int SimpleEntryImpl::ReadStream0Data(net::IOBuffer* buf,
+ int offset,
+ int buf_len) {
+ if (buf_len < 0) {
+ RecordReadResult(cache_type_, READ_RESULT_SYNC_READ_FAILURE);
+ return 0;
+ }
+ memcpy(buf->data(), stream_0_data_->data() + offset, buf_len);
+ UpdateDataFromEntryStat(
+ SimpleEntryStat(base::Time::Now(), last_modified_, data_size_));
+ RecordReadResult(cache_type_, READ_RESULT_SUCCESS);
+ return buf_len;
+}
+
+int SimpleEntryImpl::SetStream0Data(net::IOBuffer* buf,
+ int offset,
+ int buf_len,
+ bool truncate) {
+ // Currently, stream 0 is only used for HTTP headers, and always writes them
+ // with a single, truncating write. Detect these writes and record the size
+ // changes of the headers. Also, support writes to stream 0 that have
+ // different access patterns, as required by the API contract.
+ // All other clients of the Simple Cache are encouraged to use stream 1.
+ have_written_[0] = true;
+ int data_size = GetDataSize(0);
+ if (offset == 0 && truncate) {
+ RecordHeaderSizeChange(cache_type_, data_size, buf_len);
+ stream_0_data_->SetCapacity(buf_len);
+ memcpy(stream_0_data_->data(), buf->data(), buf_len);
+ data_size_[0] = buf_len;
+ } else {
+ RecordUnexpectedStream0Write(cache_type_);
+ const int buffer_size =
+ truncate ? offset + buf_len : std::max(offset + buf_len, data_size);
+ stream_0_data_->SetCapacity(buffer_size);
+ // If |stream_0_data_| was extended, the extension until offset needs to be
+ // zero-filled.
+ const int fill_size = offset <= data_size ? 0 : offset - data_size;
+ if (fill_size > 0)
+ memset(stream_0_data_->data() + data_size, 0, fill_size);
+ if (buf)
+ memcpy(stream_0_data_->data() + offset, buf->data(), buf_len);
+ data_size_[0] = buffer_size;
+ }
+ base::Time modification_time = base::Time::Now();
+ AdvanceCrc(buf, offset, buf_len, 0);
+ UpdateDataFromEntryStat(
+ SimpleEntryStat(modification_time, modification_time, data_size_));
+ RecordWriteResult(cache_type_, WRITE_RESULT_SUCCESS);
+ return buf_len;
+}
+
+void SimpleEntryImpl::AdvanceCrc(net::IOBuffer* buffer,
+ int offset,
+ int length,
+ int stream_index) {
+ // It is easy to incrementally compute the CRC from [0 .. |offset + buf_len|)
+ // if |offset == 0| or we have already computed the CRC for [0 .. offset).
+ // We rely on most write operations being sequential, start to end to compute
+ // the crc of the data. When we write to an entry and close without having
+ // done a sequential write, we don't check the CRC on read.
+ if (offset == 0 || crc32s_end_offset_[stream_index] == offset) {
+ uint32 initial_crc =
+ (offset != 0) ? crc32s_[stream_index] : crc32(0, Z_NULL, 0);
+ if (length > 0) {
+ crc32s_[stream_index] = crc32(
+ initial_crc, reinterpret_cast<const Bytef*>(buffer->data()), length);
+ }
+ crc32s_end_offset_[stream_index] = offset + length;
+ }
+}
+
} // namespace disk_cache
diff --git a/net/disk_cache/simple/simple_entry_impl.h b/net/disk_cache/simple/simple_entry_impl.h
index d7d9b8f..e2f0c63 100644
--- a/net/disk_cache/simple/simple_entry_impl.h
+++ b/net/disk_cache/simple/simple_entry_impl.h
@@ -25,6 +25,7 @@ class TaskRunner;
}
namespace net {
+class GrowableIOBuffer;
class IOBuffer;
}
@@ -32,7 +33,7 @@ namespace disk_cache {
class SimpleBackendImpl;
class SimpleSynchronousEntry;
-struct SimpleEntryStat;
+class SimpleEntryStat;
struct SimpleEntryCreationResults;
// SimpleEntryImpl is the IO thread interface to an entry in the very simple
@@ -217,7 +218,7 @@ class NET_EXPORT_PRIVATE SimpleEntryImpl : public Entry,
int offset,
const CompletionCallback& completion_callback,
scoped_ptr<uint32> read_crc32,
- scoped_ptr<base::Time> last_used,
+ scoped_ptr<SimpleEntryStat> entry_stat,
scoped_ptr<int> result);
// Called after an asynchronous write completes.
@@ -251,6 +252,23 @@ class NET_EXPORT_PRIVATE SimpleEntryImpl : public Entry,
void RecordReadIsParallelizable(const SimpleEntryOperation& operation) const;
void RecordWriteDependencyType(const SimpleEntryOperation& operation) const;
+ // Reads from the stream 0 data kept in memory.
+ int ReadStream0Data(net::IOBuffer* buf, int offset, int buf_len);
+
+ // Copies data from |buf| to the internal in-memory buffer for stream 0. If
+ // |truncate| is set to true, the target buffer will be truncated at |offset|
+ // + |buf_len| before being written.
+ int SetStream0Data(net::IOBuffer* buf,
+ int offset, int buf_len,
+ bool truncate);
+
+ // Updates |crc32s_| and |crc32s_end_offset_| for a write of the data in
+ // |buffer| on |stream_index|, starting at |offset| and of length |length|.
+ void AdvanceCrc(net::IOBuffer* buffer,
+ int offset,
+ int length,
+ int stream_index);
+
// All nonstatic SimpleEntryImpl methods should always be called on the IO
// thread, in all cases. |io_thread_checker_| documents and enforces this.
base::ThreadChecker io_thread_checker_;
@@ -268,7 +286,7 @@ class NET_EXPORT_PRIVATE SimpleEntryImpl : public Entry,
// TODO(clamy): Unify last_used_ with data in the index.
base::Time last_used_;
base::Time last_modified_;
- int32 data_size_[kSimpleEntryFileCount];
+ int32 data_size_[kSimpleEntryStreamCount];
// Number of times this object has been returned from Backend::OpenEntry() and
// Backend::CreateEntry() without subsequent Entry::Close() calls. Used to
@@ -283,15 +301,16 @@ class NET_EXPORT_PRIVATE SimpleEntryImpl : public Entry,
// write. For each stream, |crc32s_[index]| is the crc32 of that stream from
// [0 .. |crc32s_end_offset_|). If |crc32s_end_offset_[index] == 0| then the
// value of |crc32s_[index]| is undefined.
- int32 crc32s_end_offset_[kSimpleEntryFileCount];
- uint32 crc32s_[kSimpleEntryFileCount];
+ int32 crc32s_end_offset_[kSimpleEntryStreamCount];
+ uint32 crc32s_[kSimpleEntryStreamCount];
- // If |have_written_[index]| is true, we have written to the stream |index|.
- bool have_written_[kSimpleEntryFileCount];
+ // If |have_written_[index]| is true, we have written to the file that
+ // contains stream |index|.
+ bool have_written_[kSimpleEntryStreamCount];
// Reflects how much CRC checking has been done with the entry. This state is
// reported on closing each entry stream.
- CheckCrcResult crc_check_state_[kSimpleEntryFileCount];
+ CheckCrcResult crc_check_state_[kSimpleEntryStreamCount];
// The |synchronous_entry_| is the worker thread object that performs IO on
// entries. It's owned by this SimpleEntryImpl whenever |executing_operation_|
@@ -306,6 +325,17 @@ class NET_EXPORT_PRIVATE SimpleEntryImpl : public Entry,
net::BoundNetLog net_log_;
scoped_ptr<SimpleEntryOperation> executing_operation_;
+
+ // Unlike other streams, stream 0 data is read from the disk when the entry is
+ // opened, and then kept in memory. All read/write operations on stream 0
+ // affect the |stream_0_data_| buffer. When the entry is closed,
+ // |stream_0_data_| is written to the disk.
+ // Stream 0 is kept in memory because it is stored in the same file as stream
+ // 1 on disk, to reduce the number of file descriptors and save disk space.
+ // This strategy allows stream 1 to change size easily. Since stream 0 is only
+ // used to write HTTP headers, the memory consumption of keeping it in memory
+ // is acceptable.
+ scoped_refptr<net::GrowableIOBuffer> stream_0_data_;
};
} // namespace disk_cache
diff --git a/net/disk_cache/simple/simple_index_file.cc b/net/disk_cache/simple/simple_index_file.cc
index 61318c8..488d645 100644
--- a/net/disk_cache/simple/simple_index_file.cc
+++ b/net/disk_cache/simple/simple_index_file.cc
@@ -117,7 +117,7 @@ void ProcessEntryFile(SimpleIndex::EntrySet* entries,
EntryMetadata(last_used_time, file_size),
entries);
} else {
- // Summing up the total size of the entry through all the *_[0-2] files
+ // Summing up the total size of the entry through all the *_[0-1] files
it->second.SetEntrySize(it->second.GetEntrySize() + file_size);
}
}
@@ -411,10 +411,6 @@ void SimpleIndexFile::SyncRestoreFromDisk(
out_result->Reset();
SimpleIndex::EntrySet* entries = &out_result->entries;
- // TODO(felipeg,gavinp): Fix this once we have a one-file per entry format.
- COMPILE_ASSERT(kSimpleEntryFileCount == 3,
- file_pattern_must_match_file_count);
-
const bool did_succeed = TraverseCacheDirectory(
cache_directory, base::Bind(&ProcessEntryFile, entries));
if (!did_succeed) {
diff --git a/net/disk_cache/simple/simple_synchronous_entry.cc b/net/disk_cache/simple/simple_synchronous_entry.cc
index a1ff1ac..502264d 100644
--- a/net/disk_cache/simple/simple_synchronous_entry.cc
+++ b/net/disk_cache/simple/simple_synchronous_entry.cc
@@ -141,27 +141,55 @@ void RecordCloseResult(net::CacheType cache_type, CloseResult result) {
namespace disk_cache {
-using simple_util::ConvertEntryHashKeyToHexString;
using simple_util::GetEntryHashKey;
-using simple_util::GetFilenameFromEntryHashAndIndex;
+using simple_util::GetFilenameFromEntryHashAndFileIndex;
using simple_util::GetDataSizeFromKeyAndFileSize;
using simple_util::GetFileSizeFromKeyAndDataSize;
-using simple_util::GetFileOffsetFromKeyAndDataOffset;
+using simple_util::GetFileIndexFromStreamIndex;
+
+SimpleEntryStat::SimpleEntryStat(base::Time last_used,
+ base::Time last_modified,
+ const int32 data_size[])
+ : last_used_(last_used),
+ last_modified_(last_modified) {
+ memcpy(data_size_, data_size, sizeof(data_size_));
+}
-SimpleEntryStat::SimpleEntryStat() {}
+int SimpleEntryStat::GetOffsetInFile(const std::string& key,
+ int offset,
+ int stream_index) const {
+ const int64 headers_size = sizeof(SimpleFileHeader) + key.size();
+ const int64 additional_offset =
+ stream_index == 0 ? data_size_[1] + sizeof(SimpleFileEOF) : 0;
+ return headers_size + offset + additional_offset;
+}
-SimpleEntryStat::SimpleEntryStat(base::Time last_used_p,
- base::Time last_modified_p,
- const int32 data_size_p[])
- : last_used(last_used_p),
- last_modified(last_modified_p) {
- memcpy(data_size, data_size_p, sizeof(data_size));
+int SimpleEntryStat::GetEOFOffsetInFile(const std::string& key,
+ int stream_index) const {
+ return GetOffsetInFile(key, data_size_[stream_index], stream_index);
+}
+
+int SimpleEntryStat::GetLastEOFOffsetInFile(const std::string& key,
+ int stream_index) const {
+ const int file_index = GetFileIndexFromStreamIndex(stream_index);
+ const int eof_data_offset =
+ file_index == 0 ? data_size_[0] + data_size_[1] + sizeof(SimpleFileEOF)
+ : data_size_[2];
+ return GetOffsetInFile(key, eof_data_offset, stream_index);
+}
+
+int SimpleEntryStat::GetFileSize(const std::string& key, int file_index) const {
+ const int total_data_size =
+ file_index == 0 ? data_size_[0] + data_size_[1] + sizeof(SimpleFileEOF)
+ : data_size_[2];
+ return GetFileSizeFromKeyAndDataSize(key, total_data_size);
}
SimpleEntryCreationResults::SimpleEntryCreationResults(
SimpleEntryStat entry_stat)
: sync_entry(NULL),
entry_stat(entry_stat),
+ stream_0_crc32(crc32(0, Z_NULL, 0)),
result(net::OK) {
}
@@ -205,12 +233,16 @@ void SimpleSynchronousEntry::OpenEntry(
SimpleEntryCreationResults *out_results) {
SimpleSynchronousEntry* sync_entry =
new SimpleSynchronousEntry(cache_type, path, "", entry_hash);
- out_results->result = sync_entry->InitializeForOpen(
- had_index, &out_results->entry_stat);
+ out_results->result =
+ sync_entry->InitializeForOpen(had_index,
+ &out_results->entry_stat,
+ &out_results->stream_0_data,
+ &out_results->stream_0_crc32);
if (out_results->result != net::OK) {
sync_entry->Doom();
delete sync_entry;
out_results->sync_entry = NULL;
+ out_results->stream_0_data = NULL;
return;
}
out_results->sync_entry = sync_entry;
@@ -246,8 +278,8 @@ bool SimpleSynchronousEntry::DeleteFilesForEntryHash(
const uint64 entry_hash) {
bool result = true;
for (int i = 0; i < kSimpleEntryFileCount; ++i) {
- FilePath to_delete = path.AppendASCII(
- GetFilenameFromEntryHashAndIndex(entry_hash, i));
+ FilePath to_delete =
+ path.AppendASCII(GetFilenameFromEntryHashAndFileIndex(entry_hash, i));
if (!base::DeleteFile(to_delete, false)) {
result = false;
DLOG(ERROR) << "Could not delete " << to_delete.MaybeAsASCII();
@@ -279,17 +311,17 @@ int SimpleSynchronousEntry::DoomEntrySet(
void SimpleSynchronousEntry::ReadData(const EntryOperationData& in_entry_op,
net::IOBuffer* out_buf,
uint32* out_crc32,
- base::Time* out_last_used,
+ SimpleEntryStat* entry_stat,
int* out_result) const {
DCHECK(initialized_);
- int64 file_offset =
- GetFileOffsetFromKeyAndDataOffset(key_, in_entry_op.offset);
- int bytes_read = ReadPlatformFile(files_[in_entry_op.index],
- file_offset,
- out_buf->data(),
- in_entry_op.buf_len);
+ DCHECK_NE(0, in_entry_op.index);
+ const int64 file_offset =
+ entry_stat->GetOffsetInFile(key_, in_entry_op.offset, in_entry_op.index);
+ int file_index = GetFileIndexFromStreamIndex(in_entry_op.index);
+ int bytes_read = ReadPlatformFile(
+ files_[file_index], file_offset, out_buf->data(), in_entry_op.buf_len);
if (bytes_read > 0) {
- *out_last_used = Time::Now();
+ entry_stat->set_last_used(Time::Now());
*out_crc32 = crc32(crc32(0L, Z_NULL, 0),
reinterpret_cast<const Bytef*>(out_buf->data()),
bytes_read);
@@ -307,27 +339,30 @@ void SimpleSynchronousEntry::WriteData(const EntryOperationData& in_entry_op,
SimpleEntryStat* out_entry_stat,
int* out_result) const {
DCHECK(initialized_);
+ DCHECK_NE(0, in_entry_op.index);
int index = in_entry_op.index;
+ int file_index = GetFileIndexFromStreamIndex(index);
int offset = in_entry_op.offset;
int buf_len = in_entry_op.buf_len;
int truncate = in_entry_op.truncate;
-
- bool extending_by_write = offset + buf_len > out_entry_stat->data_size[index];
+ const int64 file_offset = out_entry_stat->GetOffsetInFile(
+ key_, in_entry_op.offset, in_entry_op.index);
+ bool extending_by_write = offset + buf_len > out_entry_stat->data_size(index);
if (extending_by_write) {
- // We are extending the file, and need to insure the EOF record is zeroed.
- const int64 file_eof_offset = GetFileOffsetFromKeyAndDataOffset(
- key_, out_entry_stat->data_size[index]);
- if (!TruncatePlatformFile(files_[index], file_eof_offset)) {
+ // The EOF record and the eventual stream afterward need to be zeroed out.
+ const int64 file_eof_offset =
+ out_entry_stat->GetEOFOffsetInFile(key_, index);
+ if (!TruncatePlatformFile(files_[file_index], file_eof_offset)) {
RecordWriteResult(cache_type_, WRITE_RESULT_PRETRUNCATE_FAILURE);
Doom();
*out_result = net::ERR_CACHE_WRITE_FAILURE;
return;
}
}
- const int64 file_offset = GetFileOffsetFromKeyAndDataOffset(key_, offset);
if (buf_len > 0) {
if (WritePlatformFile(
- files_[index], file_offset, in_buf->data(), buf_len) != buf_len) {
+ files_[file_index], file_offset, in_buf->data(), buf_len) !=
+ buf_len) {
RecordWriteResult(cache_type_, WRITE_RESULT_WRITE_FAILURE);
Doom();
*out_result = net::ERR_CACHE_WRITE_FAILURE;
@@ -335,79 +370,89 @@ void SimpleSynchronousEntry::WriteData(const EntryOperationData& in_entry_op,
}
}
if (!truncate && (buf_len > 0 || !extending_by_write)) {
- out_entry_stat->data_size[index] =
- std::max(out_entry_stat->data_size[index], offset + buf_len);
+ out_entry_stat->set_data_size(
+ index, std::max(out_entry_stat->data_size(index), offset + buf_len));
} else {
- if (!TruncatePlatformFile(files_[index], file_offset + buf_len)) {
+ out_entry_stat->set_data_size(index, offset + buf_len);
+ int file_eof_offset = out_entry_stat->GetLastEOFOffsetInFile(key_, index);
+ if (!TruncatePlatformFile(files_[file_index], file_eof_offset)) {
RecordWriteResult(cache_type_, WRITE_RESULT_TRUNCATE_FAILURE);
Doom();
*out_result = net::ERR_CACHE_WRITE_FAILURE;
return;
}
- out_entry_stat->data_size[index] = offset + buf_len;
}
RecordWriteResult(cache_type_, WRITE_RESULT_SUCCESS);
- out_entry_stat->last_used = out_entry_stat->last_modified = Time::Now();
+ base::Time modification_time = Time::Now();
+ out_entry_stat->set_last_used(modification_time);
+ out_entry_stat->set_last_modified(modification_time);
*out_result = buf_len;
}
void SimpleSynchronousEntry::CheckEOFRecord(int index,
- int32 data_size,
+ const SimpleEntryStat& entry_stat,
uint32 expected_crc32,
int* out_result) const {
DCHECK(initialized_);
-
- SimpleFileEOF eof_record;
- int64 file_offset = GetFileOffsetFromKeyAndDataOffset(key_, data_size);
- if (ReadPlatformFile(files_[index],
- file_offset,
- reinterpret_cast<char*>(&eof_record),
- sizeof(eof_record)) != sizeof(eof_record)) {
- RecordCheckEOFResult(cache_type_, CHECK_EOF_RESULT_READ_FAILURE);
- Doom();
- *out_result = net::ERR_CACHE_CHECKSUM_READ_FAILURE;
- return;
- }
-
- if (eof_record.final_magic_number != kSimpleFinalMagicNumber) {
- RecordCheckEOFResult(cache_type_, CHECK_EOF_RESULT_MAGIC_NUMBER_MISMATCH);
- DLOG(INFO) << "eof record had bad magic number.";
+ uint32 crc32;
+ bool has_crc32;
+ int stream_size;
+ *out_result =
+ GetEOFRecordData(index, entry_stat, &has_crc32, &crc32, &stream_size);
+ if (*out_result != net::OK) {
Doom();
- *out_result = net::ERR_CACHE_CHECKSUM_READ_FAILURE;
return;
}
-
- const bool has_crc = (eof_record.flags & SimpleFileEOF::FLAG_HAS_CRC32) ==
- SimpleFileEOF::FLAG_HAS_CRC32;
- SIMPLE_CACHE_UMA(BOOLEAN, "SyncCheckEOFHasCrc", cache_type_, has_crc);
- if (has_crc && eof_record.data_crc32 != expected_crc32) {
+ if (has_crc32 && crc32 != expected_crc32) {
+ DLOG(INFO) << "EOF record had bad crc.";
+ *out_result = net::ERR_CACHE_CHECKSUM_MISMATCH;
RecordCheckEOFResult(cache_type_, CHECK_EOF_RESULT_CRC_MISMATCH);
- DLOG(INFO) << "eof record had bad crc.";
Doom();
- *out_result = net::ERR_CACHE_CHECKSUM_MISMATCH;
return;
}
-
RecordCheckEOFResult(cache_type_, CHECK_EOF_RESULT_SUCCESS);
- *out_result = net::OK;
}
void SimpleSynchronousEntry::Close(
const SimpleEntryStat& entry_stat,
- scoped_ptr<std::vector<CRCRecord> > crc32s_to_write) {
+ scoped_ptr<std::vector<CRCRecord> > crc32s_to_write,
+ net::GrowableIOBuffer* stream_0_data) {
+ DCHECK(stream_0_data);
+ // Write stream 0 data.
+ int stream_0_offset = entry_stat.GetOffsetInFile(key_, 0, 0);
+ if (WritePlatformFile(files_[0],
+ stream_0_offset,
+ stream_0_data->data(),
+ entry_stat.data_size(0)) != entry_stat.data_size(0)) {
+ RecordCloseResult(cache_type_, CLOSE_RESULT_WRITE_FAILURE);
+ DLOG(INFO) << "Could not write stream 0 data.";
+ Doom();
+ }
+
for (std::vector<CRCRecord>::const_iterator it = crc32s_to_write->begin();
it != crc32s_to_write->end(); ++it) {
SimpleFileEOF eof_record;
+ int index = it->index;
+ eof_record.stream_size = entry_stat.data_size(index);
eof_record.final_magic_number = kSimpleFinalMagicNumber;
eof_record.flags = 0;
if (it->has_crc32)
eof_record.flags |= SimpleFileEOF::FLAG_HAS_CRC32;
eof_record.data_crc32 = it->data_crc32;
- int64 file_offset = GetFileOffsetFromKeyAndDataOffset(
- key_, entry_stat.data_size[it->index]);
- if (WritePlatformFile(files_[it->index],
- file_offset,
+ int file_index = GetFileIndexFromStreamIndex(index);
+ int eof_offset = entry_stat.GetEOFOffsetInFile(key_, index);
+ // If stream 0 changed size, the file needs to be resized, otherwise the
+ // next open will yield wrong stream sizes. On stream 1 and stream 2 proper
+ // resizing of the file is handled in SimpleSynchronousEntry::WriteData().
+ if (index == 0 && !TruncatePlatformFile(files_[file_index], eof_offset)) {
+ RecordCloseResult(cache_type_, CLOSE_RESULT_WRITE_FAILURE);
+ DLOG(INFO) << "Could not truncate stream 0 file.";
+ Doom();
+ break;
+ }
+ if (WritePlatformFile(files_[file_index],
+ eof_offset,
reinterpret_cast<const char*>(&eof_record),
sizeof(eof_record)) != sizeof(eof_record)) {
RecordCloseResult(cache_type_, CLOSE_RESULT_WRITE_FAILURE);
@@ -415,7 +460,11 @@ void SimpleSynchronousEntry::Close(
Doom();
break;
}
- const int64 file_size = file_offset + sizeof(eof_record);
+ }
+ for (int i = 0; i < kSimpleEntryFileCount; ++i) {
+ bool did_close_file = ClosePlatformFile(files_[i]);
+ DCHECK(did_close_file);
+ const int64 file_size = entry_stat.GetFileSize(key_, i);
SIMPLE_CACHE_UMA(CUSTOM_COUNTS,
"LastClusterSize", cache_type_,
file_size % 4096, 0, 4097, 50);
@@ -424,11 +473,6 @@ void SimpleSynchronousEntry::Close(
"LastClusterLossPercent", cache_type_,
cluster_loss * 100 / (cluster_loss + file_size));
}
-
- for (int i = 0; i < kSimpleEntryFileCount; ++i) {
- bool did_close_file = ClosePlatformFile(files_[i]);
- CHECK(did_close_file);
- }
RecordCloseResult(cache_type_, CLOSE_RESULT_SUCCESS);
have_open_files_ = false;
delete this;
@@ -460,8 +504,8 @@ bool SimpleSynchronousEntry::OpenOrCreateFiles(
bool had_index,
SimpleEntryStat* out_entry_stat) {
for (int i = 0; i < kSimpleEntryFileCount; ++i) {
- FilePath filename = path_.AppendASCII(
- GetFilenameFromEntryHashAndIndex(entry_hash_, i));
+ FilePath filename =
+ path_.AppendASCII(GetFilenameFromEntryHashAndFileIndex(entry_hash_, i));
int flags = PLATFORM_FILE_READ | PLATFORM_FILE_WRITE;
if (create)
flags |= PLATFORM_FILE_CREATE;
@@ -516,9 +560,11 @@ bool SimpleSynchronousEntry::OpenOrCreateFiles(
have_open_files_ = true;
if (create) {
- out_entry_stat->last_modified = out_entry_stat->last_used = Time::Now();
- for (int i = 0; i < kSimpleEntryFileCount; ++i)
- out_entry_stat->data_size[i] = 0;
+ base::Time creation_time = Time::Now();
+ out_entry_stat->set_last_modified(creation_time);
+ out_entry_stat->set_last_used(creation_time);
+ for (int i = 0; i < kSimpleEntryStreamCount; ++i)
+ out_entry_stat->set_data_size(i, 0);
} else {
base::TimeDelta entry_age = base::Time::Now() - base::Time::UnixEpoch();
for (int i = 0; i < kSimpleEntryFileCount; ++i) {
@@ -529,20 +575,31 @@ bool SimpleSynchronousEntry::OpenOrCreateFiles(
DLOG(WARNING) << "Could not get platform file info.";
continue;
}
- out_entry_stat->last_used = file_info.last_accessed;
+ out_entry_stat->set_last_used(file_info.last_accessed);
if (simple_util::GetMTime(path_, &file_last_modified))
- out_entry_stat->last_modified = file_last_modified;
+ out_entry_stat->set_last_modified(file_last_modified);
else
- out_entry_stat->last_modified = file_info.last_modified;
+ out_entry_stat->set_last_modified(file_info.last_modified);
base::TimeDelta stream_age =
- base::Time::Now() - out_entry_stat->last_modified;
+ base::Time::Now() - out_entry_stat->last_modified();
if (stream_age < entry_age)
entry_age = stream_age;
- // Keep the file size in |data size_| briefly until the key is initialized
- // properly.
- out_entry_stat->data_size[i] = file_info.size;
+ // Two things prevent from knowing the right values for |data_size|:
+ // 1) The key is not known, hence its length is unknown.
+ // 2) Stream 0 and stream 1 are in the same file, and the exact size for
+ // each will only be known when reading the EOF record for stream 0.
+ //
+ // The size for file 0 and 1 is temporarily kept in
+ // |data_size(1)| and |data_size(2)| respectively. Reading the key in
+ // InitializeForOpen yields the data size for each file. In the case of
+ // file hash_1, this is the total size of stream 2, and is assigned to
+ // data_size(2). In the case of file 0, it is the combined size of stream
+ // 0, stream 1 and one EOF record. The exact distribution of sizes between
+ // stream 1 and stream 0 is only determined after reading the EOF record
+ // for stream 0 in ReadAndValidateStream0.
+ out_entry_stat->set_data_size(i + 1, file_info.size);
}
SIMPLE_CACHE_UMA(CUSTOM_COUNTS,
"SyncOpenEntryAge", cache_type_,
@@ -560,12 +617,14 @@ void SimpleSynchronousEntry::CloseFiles() {
}
}
-int SimpleSynchronousEntry::InitializeForOpen(bool had_index,
- SimpleEntryStat* out_entry_stat) {
+int SimpleSynchronousEntry::InitializeForOpen(
+ bool had_index,
+ SimpleEntryStat* out_entry_stat,
+ scoped_refptr<net::GrowableIOBuffer>* stream_0_data,
+ uint32* out_stream_0_crc32) {
DCHECK(!initialized_);
if (!OpenOrCreateFiles(false, had_index, out_entry_stat))
return net::ERR_FAILED;
-
for (int i = 0; i < kSimpleEntryFileCount; ++i) {
SimpleFileHeader header;
int header_read_result =
@@ -602,12 +661,19 @@ int SimpleSynchronousEntry::InitializeForOpen(bool had_index,
}
key_ = std::string(key.get(), header.key_length);
- out_entry_stat->data_size[i] =
- GetDataSizeFromKeyAndFileSize(key_, out_entry_stat->data_size[i]);
- if (out_entry_stat->data_size[i] < 0) {
- // This entry can't possibly be valid, as it does not have enough space to
- // store a valid SimpleFileEOF record.
- return net::ERR_FAILED;
+ if (i == 0) {
+ // File size for stream 0 has been stored temporarily in data_size[1].
+ int total_data_size =
+ GetDataSizeFromKeyAndFileSize(key_, out_entry_stat->data_size(1));
+ int ret_value_stream_0 = ReadAndValidateStream0(
+ total_data_size, out_entry_stat, stream_0_data, out_stream_0_crc32);
+ if (ret_value_stream_0 != net::OK)
+ return ret_value_stream_0;
+ } else {
+ out_entry_stat->set_data_size(
+ 2, GetDataSizeFromKeyAndFileSize(key_, out_entry_stat->data_size(2)));
+ if (out_entry_stat->data_size(2) < 0)
+ return net::ERR_FAILED;
}
if (base::Hash(key.get(), header.key_length) != header.key_hash) {
@@ -638,16 +704,18 @@ int SimpleSynchronousEntry::InitializeForCreate(
header.key_length = key_.size();
header.key_hash = base::Hash(key_);
- if (WritePlatformFile(files_[i], 0, reinterpret_cast<char*>(&header),
- sizeof(header)) != sizeof(header)) {
- DLOG(WARNING) << "Could not write headers to new cache entry.";
+ if (WritePlatformFile(
+ files_[i], 0, reinterpret_cast<char*>(&header), sizeof(header)) !=
+ sizeof(header)) {
+ DLOG(WARNING) << "Could not write cache file header to cache entry.";
RecordSyncCreateResult(
cache_type_, CREATE_ENTRY_CANT_WRITE_HEADER, had_index);
return net::ERR_FAILED;
}
- if (WritePlatformFile(files_[i], sizeof(header), key_.data(),
- key_.size()) != implicit_cast<int>(key_.size())) {
+ if (WritePlatformFile(
+ files_[i], sizeof(SimpleFileHeader), key_.data(), key_.size()) !=
+ implicit_cast<int>(key_.size())) {
DLOG(WARNING) << "Could not write keys to new cache entry.";
RecordSyncCreateResult(
cache_type_, CREATE_ENTRY_CANT_WRITE_KEY, had_index);
@@ -659,6 +727,88 @@ int SimpleSynchronousEntry::InitializeForCreate(
return net::OK;
}
+int SimpleSynchronousEntry::ReadAndValidateStream0(
+ int total_data_size,
+ SimpleEntryStat* out_entry_stat,
+ scoped_refptr<net::GrowableIOBuffer>* stream_0_data,
+ uint32* out_stream_0_crc32) const {
+ // Temporarily assign all the data size to stream 1 in order to read the
+ // EOF record for stream 0, which contains the size of stream 0.
+ out_entry_stat->set_data_size(0, 0);
+ out_entry_stat->set_data_size(1, total_data_size - sizeof(SimpleFileEOF));
+
+ bool has_crc32;
+ uint32 read_crc32;
+ int stream_0_size;
+ int ret_value_crc32 = GetEOFRecordData(
+ 0, *out_entry_stat, &has_crc32, &read_crc32, &stream_0_size);
+ if (ret_value_crc32 != net::OK)
+ return ret_value_crc32;
+
+ if (stream_0_size > out_entry_stat->data_size(1))
+ return net::ERR_FAILED;
+
+ // These are the real values of data size.
+ out_entry_stat->set_data_size(0, stream_0_size);
+ out_entry_stat->set_data_size(
+ 1, out_entry_stat->data_size(1) - stream_0_size);
+
+ // Put stream 0 data in memory.
+ *stream_0_data = new net::GrowableIOBuffer();
+ (*stream_0_data)->SetCapacity(stream_0_size);
+ int file_offset = out_entry_stat->GetOffsetInFile(key_, 0, 0);
+ int bytes_read = ReadPlatformFile(
+ files_[0], file_offset, (*stream_0_data)->data(), stream_0_size);
+ if (bytes_read != stream_0_size)
+ return net::ERR_FAILED;
+
+ // Check the CRC32.
+ uint32 expected_crc32 =
+ stream_0_size == 0
+ ? crc32(0, Z_NULL, 0)
+ : crc32(crc32(0, Z_NULL, 0),
+ reinterpret_cast<const Bytef*>((*stream_0_data)->data()),
+ stream_0_size);
+ if (has_crc32 && read_crc32 != expected_crc32) {
+ DLOG(INFO) << "EOF record had bad crc.";
+ RecordCheckEOFResult(cache_type_, CHECK_EOF_RESULT_CRC_MISMATCH);
+ return net::ERR_FAILED;
+ }
+ *out_stream_0_crc32 = read_crc32;
+ RecordCheckEOFResult(cache_type_, CHECK_EOF_RESULT_SUCCESS);
+ return net::OK;
+}
+
+int SimpleSynchronousEntry::GetEOFRecordData(int index,
+ const SimpleEntryStat& entry_stat,
+ bool* out_has_crc32,
+ uint32* out_crc32,
+ int* out_data_size) const {
+ SimpleFileEOF eof_record;
+ int file_offset = entry_stat.GetEOFOffsetInFile(key_, index);
+ int file_index = GetFileIndexFromStreamIndex(index);
+ if (ReadPlatformFile(files_[file_index],
+ file_offset,
+ reinterpret_cast<char*>(&eof_record),
+ sizeof(eof_record)) != sizeof(eof_record)) {
+ RecordCheckEOFResult(cache_type_, CHECK_EOF_RESULT_READ_FAILURE);
+ return net::ERR_CACHE_CHECKSUM_READ_FAILURE;
+ }
+
+ if (eof_record.final_magic_number != kSimpleFinalMagicNumber) {
+ RecordCheckEOFResult(cache_type_, CHECK_EOF_RESULT_MAGIC_NUMBER_MISMATCH);
+ DLOG(INFO) << "EOF record had bad magic number.";
+ return net::ERR_CACHE_CHECKSUM_READ_FAILURE;
+ }
+
+ *out_has_crc32 = (eof_record.flags & SimpleFileEOF::FLAG_HAS_CRC32) ==
+ SimpleFileEOF::FLAG_HAS_CRC32;
+ *out_crc32 = eof_record.data_crc32;
+ *out_data_size = eof_record.stream_size;
+ SIMPLE_CACHE_UMA(BOOLEAN, "SyncCheckEOFHasCrc", cache_type_, *out_has_crc32);
+ return net::OK;
+}
+
void SimpleSynchronousEntry::Doom() const {
// TODO(gavinp): Consider if we should guard against redundant Doom() calls.
DeleteFilesForEntryHash(path_, entry_hash_);
diff --git a/net/disk_cache/simple/simple_synchronous_entry.h b/net/disk_cache/simple/simple_synchronous_entry.h
index f4b5ed8..ae270c3 100644
--- a/net/disk_cache/simple/simple_synchronous_entry.h
+++ b/net/disk_cache/simple/simple_synchronous_entry.h
@@ -11,13 +11,16 @@
#include <vector>
#include "base/files/file_path.h"
+#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/platform_file.h"
#include "base/time/time.h"
#include "net/base/cache_type.h"
+#include "net/base/net_export.h"
#include "net/disk_cache/simple/simple_entry_format.h"
namespace net {
+class GrowableIOBuffer;
class IOBuffer;
}
@@ -25,15 +28,38 @@ namespace disk_cache {
class SimpleSynchronousEntry;
-struct SimpleEntryStat {
- SimpleEntryStat();
- SimpleEntryStat(base::Time last_used_p,
- base::Time last_modified_p,
- const int32 data_size_p[]);
+// This class handles the passing of data about the entry between
+// SimpleEntryImplementation and SimpleSynchronousEntry and the computation of
+// file offsets based on the data size for all streams.
+class NET_EXPORT_PRIVATE SimpleEntryStat {
+ public:
+ SimpleEntryStat(base::Time last_used,
+ base::Time last_modified,
+ const int32 data_size[]);
+
+ int GetOffsetInFile(const std::string& key,
+ int offset,
+ int stream_index) const;
+ int GetEOFOffsetInFile(const std::string& key, int stream_index) const;
+ int GetLastEOFOffsetInFile(const std::string& key, int file_index) const;
+ int GetFileSize(const std::string& key, int file_index) const;
+
+ base::Time last_used() const { return last_used_; }
+ base::Time last_modified() const { return last_modified_; }
+ void set_last_used(base::Time last_used) { last_used_ = last_used; }
+ void set_last_modified(base::Time last_modified) {
+ last_modified_ = last_modified;
+ }
+
+ int32 data_size(int stream_index) const { return data_size_[stream_index]; }
+ void set_data_size(int stream_index, int data_size) {
+ data_size_[stream_index] = data_size;
+ }
- base::Time last_used;
- base::Time last_modified;
- int32 data_size[kSimpleEntryFileCount];
+ private:
+ base::Time last_used_;
+ base::Time last_modified_;
+ int32 data_size_[kSimpleEntryStreamCount];
};
struct SimpleEntryCreationResults {
@@ -41,7 +67,9 @@ struct SimpleEntryCreationResults {
~SimpleEntryCreationResults();
SimpleSynchronousEntry* sync_entry;
+ scoped_refptr<net::GrowableIOBuffer> stream_0_data;
SimpleEntryStat entry_stat;
+ uint32 stream_0_crc32;
int result;
};
@@ -102,21 +130,22 @@ class SimpleSynchronousEntry {
void ReadData(const EntryOperationData& in_entry_op,
net::IOBuffer* out_buf,
uint32* out_crc32,
- base::Time* out_last_used,
+ SimpleEntryStat* entry_stat,
int* out_result) const;
void WriteData(const EntryOperationData& in_entry_op,
net::IOBuffer* in_buf,
SimpleEntryStat* out_entry_stat,
int* out_result) const;
void CheckEOFRecord(int index,
- int data_size,
+ const SimpleEntryStat& entry_stat,
uint32 expected_crc32,
int* out_result) const;
// Close all streams, and add write EOF records to streams indicated by the
// CRCRecord entries in |crc32s_to_write|.
void Close(const SimpleEntryStat& entry_stat,
- scoped_ptr<std::vector<CRCRecord> > crc32s_to_write);
+ scoped_ptr<std::vector<CRCRecord> > crc32s_to_write,
+ net::GrowableIOBuffer* stream_0_data);
const base::FilePath& path() const { return path_; }
std::string key() const { return key_; }
@@ -140,7 +169,10 @@ class SimpleSynchronousEntry {
// Returns a net error, i.e. net::OK on success. |had_index| is passed
// from the main entry for metrics purposes, and is true if the index was
// initialized when the open operation began.
- int InitializeForOpen(bool had_index, SimpleEntryStat* out_entry_stat);
+ int InitializeForOpen(bool had_index,
+ SimpleEntryStat* out_entry_stat,
+ scoped_refptr<net::GrowableIOBuffer>* stream_0_data,
+ uint32* out_stream_0_crc32);
// Returns a net error, including net::OK on success and net::FILE_EXISTS
// when the entry already exists. |had_index| is passed from the main entry
@@ -148,6 +180,19 @@ class SimpleSynchronousEntry {
// create operation began.
int InitializeForCreate(bool had_index, SimpleEntryStat* out_entry_stat);
+ // Allocates and fills a buffer with stream 0 data in |stream_0_data|, then
+ // checks its crc32.
+ int ReadAndValidateStream0(
+ int total_data_size,
+ SimpleEntryStat* out_entry_stat,
+ scoped_refptr<net::GrowableIOBuffer>* stream_0_data,
+ uint32* out_stream_0_crc32) const;
+
+ int GetEOFRecordData(int index,
+ const SimpleEntryStat& entry_stat,
+ bool* out_has_crc32,
+ uint32* out_crc32,
+ int* out_data_size) const;
void Doom() const;
static bool DeleteFilesForEntryHash(const base::FilePath& path,
diff --git a/net/disk_cache/simple/simple_test_util.cc b/net/disk_cache/simple/simple_test_util.cc
index 9f09974..9a27558 100644
--- a/net/disk_cache/simple/simple_test_util.cc
+++ b/net/disk_cache/simple/simple_test_util.cc
@@ -13,7 +13,7 @@ namespace simple_util {
bool CreateCorruptFileForTests(const std::string& key,
const base::FilePath& cache_path) {
base::FilePath entry_file_path = cache_path.AppendASCII(
- disk_cache::simple_util::GetFilenameFromKeyAndIndex(key, 0));
+ disk_cache::simple_util::GetFilenameFromKeyAndFileIndex(key, 0));
int flags = base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_WRITE;
base::PlatformFile entry_file =
base::CreatePlatformFile(entry_file_path, flags, NULL, NULL);
diff --git a/net/disk_cache/simple/simple_util.cc b/net/disk_cache/simple/simple_util.cc
index 4a89f9b..4291b1f 100644
--- a/net/disk_cache/simple/simple_util.cc
+++ b/net/disk_cache/simple/simple_util.cc
@@ -77,13 +77,15 @@ uint64 GetEntryHashKey(const std::string& key) {
return u.key_hash;
}
-std::string GetFilenameFromEntryHashAndIndex(uint64 entry_hash,
- int index) {
- return base::StringPrintf("%016" PRIx64 "_%1d", entry_hash, index);
+std::string GetFilenameFromEntryHashAndFileIndex(uint64 entry_hash,
+ int file_index) {
+ return base::StringPrintf("%016" PRIx64 "_%1d", entry_hash, file_index);
}
-std::string GetFilenameFromKeyAndIndex(const std::string& key, int index) {
- return GetEntryHashKeyAsHexString(key) + base::StringPrintf("_%1d", index);
+std::string GetFilenameFromKeyAndFileIndex(const std::string& key,
+ int file_index) {
+ return GetEntryHashKeyAsHexString(key) +
+ base::StringPrintf("_%1d", file_index);
}
int32 GetDataSizeFromKeyAndFileSize(const std::string& key, int64 file_size) {
@@ -98,10 +100,8 @@ int64 GetFileSizeFromKeyAndDataSize(const std::string& key, int32 data_size) {
sizeof(SimpleFileEOF);
}
-int64 GetFileOffsetFromKeyAndDataOffset(const std::string& key,
- int data_offset) {
- const int64 headers_size = sizeof(disk_cache::SimpleFileHeader) + key.size();
- return headers_size + data_offset;
+int GetFileIndexFromStreamIndex(int stream_index) {
+ return (stream_index == 2) ? 1 : 0;
}
// TODO(clamy, gavinp): this should go in base
diff --git a/net/disk_cache/simple/simple_util.h b/net/disk_cache/simple/simple_util.h
index ef51869..60a237e 100644
--- a/net/disk_cache/simple/simple_util.h
+++ b/net/disk_cache/simple/simple_util.h
@@ -40,12 +40,13 @@ NET_EXPORT_PRIVATE bool GetEntryHashKeyFromHexString(
// Given a |key| for a (potential) entry in the simple backend and the |index|
// of a stream on that entry, returns the filename in which that stream would be
// stored.
-NET_EXPORT_PRIVATE std::string GetFilenameFromKeyAndIndex(
+NET_EXPORT_PRIVATE std::string GetFilenameFromKeyAndFileIndex(
const std::string& key,
- int index);
+ int file_index);
// Same as |GetFilenameFromKeyAndIndex| above, but using a hex string.
-std::string GetFilenameFromEntryHashAndIndex(uint64 entry_hash, int index);
+std::string GetFilenameFromEntryHashAndFileIndex(uint64 entry_hash,
+ int file_index);
// Given the size of a file holding a stream in the simple backend and the key
// to an entry, returns the number of bytes in the stream.
@@ -57,11 +58,9 @@ NET_EXPORT_PRIVATE int32 GetDataSizeFromKeyAndFileSize(const std::string& key,
NET_EXPORT_PRIVATE int64 GetFileSizeFromKeyAndDataSize(const std::string& key,
int32 data_size);
-// Given the key to an entry, and an offset into a stream on that entry, returns
-// the file offset corresponding to |data_offset|.
-NET_EXPORT_PRIVATE int64 GetFileOffsetFromKeyAndDataOffset(
- const std::string& key,
- int data_offset);
+// Given the stream index, returns the number of the file the stream is stored
+// in.
+NET_EXPORT_PRIVATE int GetFileIndexFromStreamIndex(int stream_index);
// Fills |out_time| with the time the file last modified time. Unlike the
// functions in platform_file.h, the time resolution is milliseconds.