1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "net/disk_cache/memory/mem_entry_impl.h"
8 #include "base/logging.h"
9 #include "base/strings/stringprintf.h"
10 #include "base/values.h"
11 #include "net/base/io_buffer.h"
12 #include "net/base/net_errors.h"
13 #include "net/disk_cache/memory/mem_backend_impl.h"
14 #include "net/disk_cache/net_log_parameters.h"
20 const int kSparseData
= 1;
22 // Maximum size of a sparse entry is 2 to the power of this number.
23 const int kMaxSparseEntryBits
= 12;
25 // Sparse entry has maximum size of 4KB.
26 const int kMaxSparseEntrySize
= 1 << kMaxSparseEntryBits
;
28 // Convert global offset to child index.
29 inline int ToChildIndex(int64 offset
) {
30 return static_cast<int>(offset
>> kMaxSparseEntryBits
);
33 // Convert global offset to offset in child entry.
34 inline int ToChildOffset(int64 offset
) {
35 return static_cast<int>(offset
& (kMaxSparseEntrySize
- 1));
38 // Returns a name for a child entry given the base_name of the parent and the
39 // child_id. This name is only used for logging purposes.
40 // If the entry is called entry_name, child entries will be named something
41 // like Range_entry_name:YYY where YYY is the number of the particular child.
42 std::string
GenerateChildName(const std::string
& base_name
, int child_id
) {
43 return base::StringPrintf("Range_%s:%i", base_name
.c_str(), child_id
);
46 // Returns NetLog parameters for the creation of a child MemEntryImpl. Separate
47 // function needed because child entries don't suppport GetKey().
48 scoped_ptr
<base::Value
> NetLogChildEntryCreationCallback(
49 const disk_cache::MemEntryImpl
* parent
,
51 net::NetLogCaptureMode
/* capture_mode */) {
52 scoped_ptr
<base::DictionaryValue
> dict(new base::DictionaryValue());
53 dict
->SetString("key", GenerateChildName(parent
->GetKey(), child_id
));
54 dict
->SetBoolean("created", true);
60 namespace disk_cache
{
62 MemEntryImpl::MemEntryImpl(MemBackendImpl
* backend
) {
71 for (int i
= 0; i
< NUM_STREAMS
; i
++)
75 // ------------------------------------------------------------------------
77 bool MemEntryImpl::CreateEntry(const std::string
& key
, net::NetLog
* net_log
) {
79 Time current
= Time::Now();
80 last_modified_
= current
;
83 net_log_
= net::BoundNetLog::Make(net_log
,
84 net::NetLog::SOURCE_MEMORY_CACHE_ENTRY
);
85 // Must be called after |key_| is set, so GetKey() works.
87 net::NetLog::TYPE_DISK_CACHE_MEM_ENTRY_IMPL
,
88 CreateNetLogEntryCreationCallback(this, true));
91 backend_
->ModifyStorageSize(0, static_cast<int32
>(key
.size()));
95 void MemEntryImpl::InternalDoom() {
96 net_log_
.AddEvent(net::NetLog::TYPE_ENTRY_DOOM
);
99 if (type() == kParentEntry
) {
100 // If this is a parent entry, we need to doom all the child entries.
101 if (children_
.get()) {
103 children
.swap(*children_
);
104 for (EntryMap::iterator i
= children
.begin();
105 i
!= children
.end(); ++i
) {
106 // Since a pointer to this object is also saved in the map, avoid
108 if (i
->second
!= this)
111 DCHECK(children_
->empty());
114 // If this is a child entry, detach it from the parent.
115 parent_
->DetachChild(child_id_
);
121 void MemEntryImpl::Open() {
122 // Only a parent entry can be opened.
123 // TODO(hclam): make sure it's correct to not apply the concept of ref
124 // counting to child entry.
125 DCHECK(type() == kParentEntry
);
127 DCHECK_GE(ref_count_
, 0);
131 bool MemEntryImpl::InUse() {
132 if (type() == kParentEntry
) {
133 return ref_count_
> 0;
135 // A child entry is always not in use. The consequence is that a child entry
136 // can always be evicted while the associated parent entry is currently in
137 // used (i.e. opened).
142 // ------------------------------------------------------------------------
144 void MemEntryImpl::Doom() {
147 if (type() == kParentEntry
) {
148 // Perform internal doom from the backend if this is a parent entry.
149 backend_
->InternalDoomEntry(this);
151 // Manually detach from the backend and perform internal doom.
152 backend_
->RemoveFromRankingList(this);
157 void MemEntryImpl::Close() {
158 // Only a parent entry can be closed.
159 DCHECK(type() == kParentEntry
);
161 DCHECK_GE(ref_count_
, 0);
162 if (!ref_count_
&& doomed_
)
166 std::string
MemEntryImpl::GetKey() const {
167 // A child entry doesn't have key so this method should not be called.
168 DCHECK(type() == kParentEntry
);
172 Time
MemEntryImpl::GetLastUsed() const {
176 Time
MemEntryImpl::GetLastModified() const {
177 return last_modified_
;
180 int32
MemEntryImpl::GetDataSize(int index
) const {
181 if (index
< 0 || index
>= NUM_STREAMS
)
183 return data_size_
[index
];
186 int MemEntryImpl::ReadData(int index
, int offset
, IOBuffer
* buf
, int buf_len
,
187 const CompletionCallback
& callback
) {
188 if (net_log_
.IsCapturing()) {
190 net::NetLog::TYPE_ENTRY_READ_DATA
,
191 CreateNetLogReadWriteDataCallback(index
, offset
, buf_len
, false));
194 int result
= InternalReadData(index
, offset
, buf
, buf_len
);
196 if (net_log_
.IsCapturing()) {
198 net::NetLog::TYPE_ENTRY_READ_DATA
,
199 CreateNetLogReadWriteCompleteCallback(result
));
204 int MemEntryImpl::WriteData(int index
, int offset
, IOBuffer
* buf
, int buf_len
,
205 const CompletionCallback
& callback
, bool truncate
) {
206 if (net_log_
.IsCapturing()) {
208 net::NetLog::TYPE_ENTRY_WRITE_DATA
,
209 CreateNetLogReadWriteDataCallback(index
, offset
, buf_len
, truncate
));
212 int result
= InternalWriteData(index
, offset
, buf
, buf_len
, truncate
);
214 if (net_log_
.IsCapturing()) {
216 net::NetLog::TYPE_ENTRY_WRITE_DATA
,
217 CreateNetLogReadWriteCompleteCallback(result
));
222 int MemEntryImpl::ReadSparseData(int64 offset
, IOBuffer
* buf
, int buf_len
,
223 const CompletionCallback
& callback
) {
224 if (net_log_
.IsCapturing()) {
226 net::NetLog::TYPE_SPARSE_READ
,
227 CreateNetLogSparseOperationCallback(offset
, buf_len
));
229 int result
= InternalReadSparseData(offset
, buf
, buf_len
);
230 if (net_log_
.IsCapturing())
231 net_log_
.EndEvent(net::NetLog::TYPE_SPARSE_READ
);
235 int MemEntryImpl::WriteSparseData(int64 offset
, IOBuffer
* buf
, int buf_len
,
236 const CompletionCallback
& callback
) {
237 if (net_log_
.IsCapturing()) {
239 net::NetLog::TYPE_SPARSE_WRITE
,
240 CreateNetLogSparseOperationCallback(offset
, buf_len
));
242 int result
= InternalWriteSparseData(offset
, buf
, buf_len
);
243 if (net_log_
.IsCapturing())
244 net_log_
.EndEvent(net::NetLog::TYPE_SPARSE_WRITE
);
248 int MemEntryImpl::GetAvailableRange(int64 offset
, int len
, int64
* start
,
249 const CompletionCallback
& callback
) {
250 if (net_log_
.IsCapturing()) {
252 net::NetLog::TYPE_SPARSE_GET_RANGE
,
253 CreateNetLogSparseOperationCallback(offset
, len
));
255 int result
= GetAvailableRange(offset
, len
, start
);
256 if (net_log_
.IsCapturing()) {
258 net::NetLog::TYPE_SPARSE_GET_RANGE
,
259 CreateNetLogGetAvailableRangeResultCallback(*start
, result
));
264 bool MemEntryImpl::CouldBeSparse() const {
265 DCHECK_EQ(kParentEntry
, type());
266 return (children_
.get() != NULL
);
269 int MemEntryImpl::ReadyForSparseIO(const CompletionCallback
& callback
) {
273 // ------------------------------------------------------------------------
275 MemEntryImpl::~MemEntryImpl() {
276 for (int i
= 0; i
< NUM_STREAMS
; i
++)
277 backend_
->ModifyStorageSize(data_size_
[i
], 0);
278 backend_
->ModifyStorageSize(static_cast<int32
>(key_
.size()), 0);
279 net_log_
.EndEvent(net::NetLog::TYPE_DISK_CACHE_MEM_ENTRY_IMPL
);
282 int MemEntryImpl::InternalReadData(int index
, int offset
, IOBuffer
* buf
,
284 DCHECK(type() == kParentEntry
|| index
== kSparseData
);
286 if (index
< 0 || index
>= NUM_STREAMS
)
287 return net::ERR_INVALID_ARGUMENT
;
289 int entry_size
= GetDataSize(index
);
290 if (offset
>= entry_size
|| offset
< 0 || !buf_len
)
294 return net::ERR_INVALID_ARGUMENT
;
296 if (offset
+ buf_len
> entry_size
)
297 buf_len
= entry_size
- offset
;
301 memcpy(buf
->data(), &(data_
[index
])[offset
], buf_len
);
305 int MemEntryImpl::InternalWriteData(int index
, int offset
, IOBuffer
* buf
,
306 int buf_len
, bool truncate
) {
307 DCHECK(type() == kParentEntry
|| index
== kSparseData
);
309 if (index
< 0 || index
>= NUM_STREAMS
)
310 return net::ERR_INVALID_ARGUMENT
;
312 if (offset
< 0 || buf_len
< 0)
313 return net::ERR_INVALID_ARGUMENT
;
315 int max_file_size
= backend_
->MaxFileSize();
317 // offset of buf_len could be negative numbers.
318 if (offset
> max_file_size
|| buf_len
> max_file_size
||
319 offset
+ buf_len
> max_file_size
) {
320 return net::ERR_FAILED
;
323 // Read the size at this point.
324 int entry_size
= GetDataSize(index
);
326 PrepareTarget(index
, offset
, buf_len
);
328 if (entry_size
< offset
+ buf_len
) {
329 backend_
->ModifyStorageSize(entry_size
, offset
+ buf_len
);
330 data_size_
[index
] = offset
+ buf_len
;
331 } else if (truncate
) {
332 if (entry_size
> offset
+ buf_len
) {
333 backend_
->ModifyStorageSize(entry_size
, offset
+ buf_len
);
334 data_size_
[index
] = offset
+ buf_len
;
343 memcpy(&(data_
[index
])[offset
], buf
->data(), buf_len
);
347 int MemEntryImpl::InternalReadSparseData(int64 offset
, IOBuffer
* buf
,
349 DCHECK(type() == kParentEntry
);
351 if (!InitSparseInfo())
352 return net::ERR_CACHE_OPERATION_NOT_SUPPORTED
;
354 if (offset
< 0 || buf_len
< 0)
355 return net::ERR_INVALID_ARGUMENT
;
357 // We will keep using this buffer and adjust the offset in this buffer.
358 scoped_refptr
<net::DrainableIOBuffer
> io_buf(
359 new net::DrainableIOBuffer(buf
, buf_len
));
361 // Iterate until we have read enough.
362 while (io_buf
->BytesRemaining()) {
363 MemEntryImpl
* child
= OpenChild(offset
+ io_buf
->BytesConsumed(), false);
365 // No child present for that offset.
369 // We then need to prepare the child offset and len.
370 int child_offset
= ToChildOffset(offset
+ io_buf
->BytesConsumed());
372 // If we are trying to read from a position that the child entry has no data
374 if (child_offset
< child
->child_first_pos_
)
376 if (net_log_
.IsCapturing()) {
378 net::NetLog::TYPE_SPARSE_READ_CHILD_DATA
,
379 CreateNetLogSparseReadWriteCallback(child
->net_log().source(),
380 io_buf
->BytesRemaining()));
382 int ret
= child
->ReadData(kSparseData
, child_offset
, io_buf
.get(),
383 io_buf
->BytesRemaining(), CompletionCallback());
384 if (net_log_
.IsCapturing()) {
385 net_log_
.EndEventWithNetErrorCode(
386 net::NetLog::TYPE_SPARSE_READ_CHILD_DATA
, ret
);
389 // If we encounter an error in one entry, return immediately.
395 // Increment the counter by number of bytes read in the child entry.
396 io_buf
->DidConsume(ret
);
401 return io_buf
->BytesConsumed();
404 int MemEntryImpl::InternalWriteSparseData(int64 offset
, IOBuffer
* buf
,
406 DCHECK(type() == kParentEntry
);
408 if (!InitSparseInfo())
409 return net::ERR_CACHE_OPERATION_NOT_SUPPORTED
;
411 if (offset
< 0 || buf_len
< 0)
412 return net::ERR_INVALID_ARGUMENT
;
414 scoped_refptr
<net::DrainableIOBuffer
> io_buf(
415 new net::DrainableIOBuffer(buf
, buf_len
));
417 // This loop walks through child entries continuously starting from |offset|
418 // and writes blocks of data (of maximum size kMaxSparseEntrySize) into each
419 // child entry until all |buf_len| bytes are written. The write operation can
420 // start in the middle of an entry.
421 while (io_buf
->BytesRemaining()) {
422 MemEntryImpl
* child
= OpenChild(offset
+ io_buf
->BytesConsumed(), true);
423 int child_offset
= ToChildOffset(offset
+ io_buf
->BytesConsumed());
425 // Find the right amount to write, this evaluates the remaining bytes to
426 // write and remaining capacity of this child entry.
427 int write_len
= std::min(static_cast<int>(io_buf
->BytesRemaining()),
428 kMaxSparseEntrySize
- child_offset
);
430 // Keep a record of the last byte position (exclusive) in the child.
431 int data_size
= child
->GetDataSize(kSparseData
);
433 if (net_log_
.IsCapturing()) {
435 net::NetLog::TYPE_SPARSE_WRITE_CHILD_DATA
,
436 CreateNetLogSparseReadWriteCallback(child
->net_log().source(),
440 // Always writes to the child entry. This operation may overwrite data
441 // previously written.
442 // TODO(hclam): if there is data in the entry and this write is not
443 // continuous we may want to discard this write.
444 int ret
= child
->WriteData(kSparseData
, child_offset
, io_buf
.get(),
445 write_len
, CompletionCallback(), true);
446 if (net_log_
.IsCapturing()) {
447 net_log_
.EndEventWithNetErrorCode(
448 net::NetLog::TYPE_SPARSE_WRITE_CHILD_DATA
, ret
);
455 // Keep a record of the first byte position in the child if the write was
456 // not aligned nor continuous. This is to enable witting to the middle
457 // of an entry and still keep track of data off the aligned edge.
458 if (data_size
!= child_offset
)
459 child
->child_first_pos_
= child_offset
;
461 // Adjust the offset in the IO buffer.
462 io_buf
->DidConsume(ret
);
467 return io_buf
->BytesConsumed();
470 int MemEntryImpl::GetAvailableRange(int64 offset
, int len
, int64
* start
) {
471 DCHECK(type() == kParentEntry
);
474 if (!InitSparseInfo())
475 return net::ERR_CACHE_OPERATION_NOT_SUPPORTED
;
477 if (offset
< 0 || len
< 0 || !start
)
478 return net::ERR_INVALID_ARGUMENT
;
480 MemEntryImpl
* current_child
= NULL
;
482 // Find the first child and record the number of empty bytes.
483 int empty
= FindNextChild(offset
, len
, ¤t_child
);
485 *start
= offset
+ empty
;
488 // Counts the number of continuous bytes.
491 // This loop scan for continuous bytes.
492 while (len
&& current_child
) {
493 // Number of bytes available in this child.
494 int data_size
= current_child
->GetDataSize(kSparseData
) -
495 ToChildOffset(*start
+ continuous
);
499 // We have found more continuous bytes so increment the count. Also
500 // decrement the length we should scan.
501 continuous
+= data_size
;
504 // If the next child is discontinuous, break the loop.
505 if (FindNextChild(*start
+ continuous
, len
, ¤t_child
))
514 void MemEntryImpl::PrepareTarget(int index
, int offset
, int buf_len
) {
515 int entry_size
= GetDataSize(index
);
517 if (entry_size
>= offset
+ buf_len
)
518 return; // Not growing the stored data.
520 if (static_cast<int>(data_
[index
].size()) < offset
+ buf_len
)
521 data_
[index
].resize(offset
+ buf_len
);
523 if (offset
<= entry_size
)
524 return; // There is no "hole" on the stored data.
526 // Cleanup the hole not written by the user. The point is to avoid returning
527 // random stuff later on.
528 memset(&(data_
[index
])[entry_size
], 0, offset
- entry_size
);
531 void MemEntryImpl::UpdateRank(bool modified
) {
532 Time current
= Time::Now();
533 last_used_
= current
;
536 last_modified_
= current
;
539 backend_
->UpdateRank(this);
542 bool MemEntryImpl::InitSparseInfo() {
543 DCHECK(type() == kParentEntry
);
545 if (!children_
.get()) {
546 // If we already have some data in sparse stream but we are being
547 // initialized as a sparse entry, we should fail.
548 if (GetDataSize(kSparseData
))
550 children_
.reset(new EntryMap());
552 // The parent entry stores data for the first block, so save this object to
554 (*children_
)[0] = this;
559 bool MemEntryImpl::InitChildEntry(MemEntryImpl
* parent
, int child_id
,
560 net::NetLog
* net_log
) {
564 net_log_
= net::BoundNetLog::Make(net_log
,
565 net::NetLog::SOURCE_MEMORY_CACHE_ENTRY
);
567 net::NetLog::TYPE_DISK_CACHE_MEM_ENTRY_IMPL
,
568 base::Bind(&NetLogChildEntryCreationCallback
, parent
, child_id_
));
571 child_id_
= child_id
;
572 Time current
= Time::Now();
573 last_modified_
= current
;
574 last_used_
= current
;
575 // Insert this to the backend's ranking list.
576 backend_
->InsertIntoRankingList(this);
580 MemEntryImpl
* MemEntryImpl::OpenChild(int64 offset
, bool create
) {
581 DCHECK(type() == kParentEntry
);
582 int index
= ToChildIndex(offset
);
583 EntryMap::iterator i
= children_
->find(index
);
584 if (i
!= children_
->end()) {
587 MemEntryImpl
* child
= new MemEntryImpl(backend_
);
588 child
->InitChildEntry(this, index
, net_log_
.net_log());
589 (*children_
)[index
] = child
;
595 int MemEntryImpl::FindNextChild(int64 offset
, int len
, MemEntryImpl
** child
) {
600 // This loop tries to find the first existing child.
601 while (scanned_len
< len
) {
602 // This points to the current offset in the child.
603 int current_child_offset
= ToChildOffset(offset
+ scanned_len
);
604 MemEntryImpl
* current_child
= OpenChild(offset
+ scanned_len
, false);
606 int child_first_pos
= current_child
->child_first_pos_
;
608 // This points to the first byte that we should be reading from, we need
609 // to take care of the filled region and the current offset in the child.
610 int first_pos
= std::max(current_child_offset
, child_first_pos
);
612 // If the first byte position we should read from doesn't exceed the
613 // filled region, we have found the first child.
614 if (first_pos
< current_child
->GetDataSize(kSparseData
)) {
615 *child
= current_child
;
617 // We need to advance the scanned length.
618 scanned_len
+= first_pos
- current_child_offset
;
622 scanned_len
+= kMaxSparseEntrySize
- current_child_offset
;
627 void MemEntryImpl::DetachChild(int child_id
) {
628 children_
->erase(child_id
);
631 } // namespace disk_cache