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.
4
5#include "base/logging.h"
6#include "base/memory/scoped_ptr.h"
7#include "base/stl_util.h"
8#include "net/disk_cache/flash/format.h"
9#include "net/disk_cache/flash/log_store.h"
10#include "net/disk_cache/flash/segment.h"
11#include "net/disk_cache/flash/storage.h"
12
13namespace disk_cache {
14
15LogStore::LogStore(const base::FilePath& path, int32 size)
16    : storage_(path, size),
17      num_segments_(size / kFlashSegmentSize),
18      open_segments_(num_segments_),
19      write_index_(0),
20      current_entry_id_(-1),
21      current_entry_num_bytes_left_to_write_(0),
22      init_(false),
23      closed_(false) {
24  DCHECK(size % kFlashSegmentSize == 0);
25}
26
27LogStore::~LogStore() {
28  DCHECK(!init_ || closed_);
29  STLDeleteElements(&open_segments_);
30}
31
32bool LogStore::Init() {
33  DCHECK(!init_);
34  if (!storage_.Init())
35    return false;
36
37  // TODO(agayev): Once we start persisting segment metadata to disk, we will
38  // start from where we left off during the last shutdown.
39  scoped_ptr<Segment> segment(new Segment(write_index_, false, &storage_));
40  if (!segment->Init())
41    return false;
42
43  segment->AddUser();
44  open_segments_[write_index_] = segment.release();
45  init_ = true;
46  return true;
47}
48
49bool LogStore::Close() {
50  DCHECK(init_ && !closed_);
51  open_segments_[write_index_]->ReleaseUser();
52  if (!open_segments_[write_index_]->Close())
53    return false;
54  closed_ = true;
55  return true;
56  // TODO(agayev): persist metadata to disk.
57}
58
59bool LogStore::CreateEntry(int32 size, int32* id) {
60  DCHECK(init_ && !closed_);
61  DCHECK(current_entry_id_ == -1 && size <= disk_cache::kFlashSegmentFreeSpace);
62
63  // TODO(agayev): Avoid large entries from leaving the segments almost empty.
64  if (!open_segments_[write_index_]->CanHold(size)) {
65    if (!open_segments_[write_index_]->Close())
66      return false;
67
68    open_segments_[write_index_]->ReleaseUser();
69    if (open_segments_[write_index_]->HasNoUsers()) {
70      delete open_segments_[write_index_];
71      open_segments_[write_index_] = NULL;
72    }
73
74    write_index_ = GetNextSegmentIndex();
75    scoped_ptr<Segment> segment(new Segment(write_index_, false, &storage_));
76    if (!segment->Init())
77      return false;
78
79    segment->AddUser();
80    open_segments_[write_index_] = segment.release();
81  }
82
83  *id = open_segments_[write_index_]->write_offset();
84  open_segments_[write_index_]->StoreOffset(*id);
85  current_entry_id_ = *id;
86  current_entry_num_bytes_left_to_write_ = size;
87  open_entries_.insert(current_entry_id_);
88  return true;
89}
90
91void LogStore::DeleteEntry(int32 id, int32 size) {
92  DCHECK(init_ && !closed_);
93  DCHECK(open_entries_.find(id) == open_entries_.end());
94  // TODO(agayev): Increment the number of dead bytes in the segment metadata
95  // for the segment identified by |index|.
96}
97
98bool LogStore::WriteData(const void* buffer, int32 size) {
99  DCHECK(init_ && !closed_);
100  DCHECK(current_entry_id_ != -1 &&
101         size <= current_entry_num_bytes_left_to_write_);
102  if (open_segments_[write_index_]->WriteData(buffer, size)) {
103    current_entry_num_bytes_left_to_write_ -= size;
104    return true;
105  }
106  return false;
107}
108
109bool LogStore::OpenEntry(int32 id) {
110  DCHECK(init_ && !closed_);
111  if (open_entries_.find(id) != open_entries_.end())
112    return false;
113
114  // Segment is already open.
115  int32 index = id / disk_cache::kFlashSegmentSize;
116  if (open_segments_[index]) {
117    if (!open_segments_[index]->HaveOffset(id))
118      return false;
119    open_segments_[index]->AddUser();
120    open_entries_.insert(id);
121    return true;
122  }
123
124  // Segment is not open.
125  scoped_ptr<Segment> segment(new Segment(index, true, &storage_));
126  if (!segment->Init() || !segment->HaveOffset(id))
127    return false;
128
129  segment->AddUser();
130  open_segments_[index] = segment.release();
131  open_entries_.insert(id);
132  return true;
133}
134
135bool LogStore::ReadData(int32 id, void* buffer, int32 size,
136                                  int32 offset) const {
137  DCHECK(init_ && !closed_);
138  DCHECK(open_entries_.find(id) != open_entries_.end());
139
140  int32 index = id / disk_cache::kFlashSegmentSize;
141  DCHECK(open_segments_[index] && open_segments_[index]->HaveOffset(id));
142  return open_segments_[index]->ReadData(buffer, size, id + offset);
143}
144
145void LogStore::CloseEntry(int32 id) {
146  DCHECK(init_ && !closed_);
147  std::set<int32>::iterator entry_iter = open_entries_.find(id);
148  DCHECK(entry_iter != open_entries_.end());
149
150  if (current_entry_id_ != -1) {
151    DCHECK(id == current_entry_id_ && !current_entry_num_bytes_left_to_write_);
152    open_entries_.erase(entry_iter);
153    current_entry_id_ = -1;
154    return;
155  }
156
157  int32 index = id / disk_cache::kFlashSegmentSize;
158  DCHECK(open_segments_[index]);
159  open_entries_.erase(entry_iter);
160
161  open_segments_[index]->ReleaseUser();
162  if (open_segments_[index]->HasNoUsers()) {
163    delete open_segments_[index];
164    open_segments_[index] = NULL;
165  }
166}
167
168int32 LogStore::GetNextSegmentIndex() {
169  DCHECK(init_ && !closed_);
170  int32 next_index = (write_index_ + 1) % num_segments_;
171
172  while (InUse(next_index)) {
173    next_index = (next_index + 1) % num_segments_;
174    DCHECK_NE(next_index, write_index_);
175  }
176  return next_index;
177}
178
179bool LogStore::InUse(int32 index) const {
180  DCHECK(init_ && !closed_);
181  DCHECK(index >= 0 && index < num_segments_);
182  return open_segments_[index] != NULL;
183}
184
185}  // namespace disk_cache
186