BadgerDB
 All Classes Namespaces Functions Variables Typedefs Enumerations Friends Pages
file.cpp
1 
8 #include "file.h"
9 
10 #include <fstream>
11 #include <iostream>
12 #include <memory>
13 #include <string>
14 #include <cstdio>
15 #include <cassert>
16 
17 #include "exceptions/file_exists_exception.h"
18 #include "exceptions/file_not_found_exception.h"
19 #include "exceptions/file_open_exception.h"
20 #include "exceptions/invalid_page_exception.h"
21 #include "file_iterator.h"
22 #include "page.h"
23 
24 namespace badgerdb {
25 
26 File::StreamMap File::open_streams_;
27 File::CountMap File::open_counts_;
28 
29 void File::remove(const std::string& filename) {
30  if (!exists(filename)) {
31  throw FileNotFoundException(filename);
32  }
33  if (isOpen(filename)) {
34  throw FileOpenException(filename);
35  }
36  std::remove(filename.c_str());
37 }
38 
39 bool File::isOpen(const std::string& filename) {
40  if (!exists(filename)) {
41  return false;
42  }
43  return open_counts_.find(filename) != open_counts_.end();
44 }
45 
46 bool File::exists(const std::string& filename) {
47  std::fstream file(filename);
48  if(file)
49  {
50  file.close();
51  return true;
52  }
53 
54  return false;
55 }
56 
58  close();
59 }
60 
61 
63  const FileHeader& header = readHeader();
64  return header.first_used_page;
65 }
66 
67 File::File(const std::string& name, const bool create_new) : filename_(name) {
68  openIfNeeded(create_new);
69 
70  if (create_new) {
71  // File starts with 1 page (the header).
72  FileHeader header = {1 /* num_pages */, 0 /* first_used_page */,
73  0 /* num_free_pages */, 0 /* first_free_page */};
74  writeHeader(header);
75  }
76 }
77 
78 void File::openIfNeeded(const bool create_new) {
79  if (open_counts_.find(filename_) != open_counts_.end()) { //exists an entry already
82  } else {
83  std::ios_base::openmode mode =
84  std::fstream::in | std::fstream::out | std::fstream::binary;
85  const bool already_exists = exists(filename_);
86  if (create_new) {
87  // Error if we try to overwrite an existing file.
88  if (already_exists) {
90  }
91  // New files have to be truncated on open.
92  mode = mode | std::fstream::trunc;
93  } else {
94  // Error if we try to open a file that doesn't exist.
95  if (!already_exists) {
97  }
98  }
99  stream_.reset(new std::fstream(filename_, mode));
101  open_counts_[filename_] = 1;
102  }
103 }
104 
105 void File::close() {
106  if(open_counts_[filename_] > 0)
108 
109  stream_.reset();
110  assert(open_counts_[filename_] >= 0);
111 
112  if (open_counts_[filename_] == 0) {
113  open_streams_.erase(filename_);
114  open_counts_.erase(filename_);
115  }
116 }
117 
119  FileHeader header;
120  stream_->seekg(0 /* pos */, std::ios::beg);
121  stream_->read(reinterpret_cast<char*>(&header), sizeof(FileHeader));
122  return header;
123 }
124 
125 void File::writeHeader(const FileHeader& header) {
126  stream_->seekp(0 /* pos */, std::ios::beg);
127  stream_->write(reinterpret_cast<const char*>(&header), sizeof(FileHeader));
128  stream_->flush();
129 }
130 
131 
132 
133 
134 
135 PageFile PageFile::create(const std::string& filename) {
136  return PageFile(filename, true /* create_new */);
137 }
138 
139 PageFile PageFile::open(const std::string& filename) {
140  return PageFile(filename, false /* create_new */);
141 }
142 
143 PageFile::PageFile(const std::string& name, const bool create_new)
144 : File(name, create_new)
145 {
146 }
147 
149 }
150 
152 : File(other.filename_, false /* create_new */)
153 {
154 }
155 
157  // This accounts for self-assignment and assignment of a File object for the
158  // same file.
159  close(); //close my file and associate me with the new one
160  filename_ = rhs.filename_;
161  openIfNeeded(false /* create_new */);
162  return *this;
163 }
164 
165 Page PageFile::allocatePage(PageId &new_page_number) {
166  FileHeader header = readHeader();
167  Page new_page;
168  Page existing_page;
169  if (header.num_free_pages > 0) {
170  new_page = readPage(header.first_free_page, true /* allow_free */);
171  new_page.set_page_number(header.first_free_page);
172  new_page_number = new_page.page_number();
173  header.first_free_page = new_page.next_page_number();
174  --header.num_free_pages;
175 
176  if (header.first_used_page == Page::INVALID_NUMBER ||
177  header.first_used_page > new_page.page_number()) {
178  // Either have no pages used or the head of the used list is a page later
179  // than the one we just allocated, so add the new page to the head.
180  if (header.first_used_page > new_page.page_number()) {
181  new_page.set_next_page_number(header.first_used_page);
182  }
183  header.first_used_page = new_page.page_number();
184  } else {
185  // New page is reused from somewhere after the beginning, so we need to
186  // find where in the used list to insert it.
187  PageId next_page_number = Page::INVALID_NUMBER;
188  for (FileIterator iter = begin(); iter != end(); ++iter) {
189  next_page_number = (*iter).next_page_number();
190  if (next_page_number > new_page.page_number() ||
191  next_page_number == Page::INVALID_NUMBER) {
192  existing_page = *iter;
193  break;
194  }
195  }
196  existing_page.set_next_page_number(new_page.page_number());
197  new_page.set_next_page_number(next_page_number);
198  }
199 
200  assert((header.num_free_pages == 0) ==
202  }
203  else
204  {
205  new_page.set_page_number(header.num_pages);
206  new_page_number = new_page.page_number();
207 
209  {
210  header.first_used_page = new_page.page_number();
211  }
212  else
213  {
214  // If we have pages allocated, we need to add the new page to the tail
215  // of the linked list.
216  for (FileIterator iter = begin(); iter != end(); ++iter) {
217  if ((*iter).next_page_number() == Page::INVALID_NUMBER) {
218  existing_page = *iter;
219  break;
220  }
221  }
222  assert(existing_page.isUsed());
223  existing_page.set_next_page_number(new_page.page_number());
224  }
225  ++header.num_pages;
226  }
227  writePage(new_page_number, new_page.header_, new_page);
228  if (existing_page.page_number() != Page::INVALID_NUMBER) {
229  // If we updated an existing page by inserting the new page into the
230  // used list, we need to write it out.
231  writePage(existing_page.page_number(), existing_page.header_, existing_page);
232  }
233  writeHeader(header);
234 
235  return new_page;
236 }
237 
238 Page PageFile::readPage(const PageId page_number) const {
239  FileHeader header = readHeader();
240 
241  if (page_number >= header.num_pages)
242  {
243  throw InvalidPageException(page_number, filename_);
244  }
245  return readPage(page_number, false /* allow_free */);
246 }
247 
248 Page PageFile::readPage(const PageId page_number, const bool allow_free) const {
249  Page page;
250  stream_->seekg(pagePosition(page_number), std::ios::beg);
251  stream_->read(reinterpret_cast<char*>(&page.header_), sizeof(PageHeader));
252  stream_->read(reinterpret_cast<char*>(&page.data_[0]), Page::DATA_SIZE);
253  if (!allow_free && !page.isUsed()) {
254  throw InvalidPageException(page_number, filename_);
255  }
256 
257  return page;
258 }
259 
260 void PageFile::writePage(const PageId new_page_number, const Page& new_page) {
261  PageHeader header = readPageHeader(new_page_number);
263  {
264  // Page has been deleted since it was read.
265  throw InvalidPageException(new_page_number, filename_);
266  }
267  // Page on disk may have had its next page pointer updated since it was read;
268  // we don't modify that, but we do keep all the other modifications to the
269  // page header.
270  const PageId next_page_number = header.next_page_number;
271  header = new_page.header_;
272  header.next_page_number = next_page_number;
273  writePage(new_page_number, header, new_page);
274 }
275 
276 void PageFile::deletePage(const PageId page_number) {
277  FileHeader header = readHeader();
278 
279  Page existing_page = readPage(page_number);
280  Page previous_page;
281  // If this page is the head of the used list, update the header to point to
282  // the next page in line.
283  if (page_number == header.first_used_page) {
284  header.first_used_page = existing_page.next_page_number();
285  } else {
286  // Walk the used list so we can update the page that points to this one.
287  for (FileIterator iter = begin(); iter != end(); ++iter) {
288  previous_page = *iter;
289  if (previous_page.next_page_number() == existing_page.page_number()) {
290  previous_page.set_next_page_number(existing_page.next_page_number());
291  break;
292  }
293  }
294  }
295  // Clear the page and add it to the head of the free list.
296  existing_page.initialize();
297  existing_page.set_next_page_number(header.first_free_page);
298  header.first_free_page = page_number;
299  ++header.num_free_pages;
300  if (previous_page.isUsed()) {
301  writePage(previous_page.page_number(), previous_page.header_, previous_page);
302  }
303  writePage(page_number, existing_page.header_, existing_page);
304  writeHeader(header);
305 }
306 
308  const FileHeader& header = readHeader();
309  return FileIterator(this, header.first_used_page);
310 }
311 
313  return FileIterator(this, Page::INVALID_NUMBER);
314 }
315 
316 void PageFile::writePage(const PageId page_number, const PageHeader& header,
317  const Page& new_page) {
318  stream_->seekp(pagePosition(page_number), std::ios::beg);
319  stream_->write(reinterpret_cast<const char*>(&header), sizeof(PageHeader));
320  stream_->write(reinterpret_cast<const char*>(&new_page.data_[0]),
322  stream_->flush();
323 }
324 
325 PageHeader PageFile::readPageHeader(PageId page_number) const {
326  PageHeader header;
327  stream_->seekg(pagePosition(page_number), std::ios::beg);
328  stream_->read(reinterpret_cast<char*>(&header), sizeof(PageHeader));
329  return header;
330 }
331 
332 
333 
334 
335 BlobFile BlobFile::create(const std::string& filename) {
336  return BlobFile(filename, true /* create_new */);
337 }
338 
339 BlobFile BlobFile::open(const std::string& filename) {
340  return BlobFile(filename, false /* create_new */);
341 }
342 
343 BlobFile::BlobFile(const std::string& name, const bool create_new)
344 : File(name, create_new) {
345 }
346 
348 }
349 
351 : File(other.filename_, false /* create_new */)
352 {
353 }
354 
356  // This accounts for self-assignment and assignment of a File object for the
357  // same file.
358  close(); //close my file and associate me with the new one
359  filename_ = rhs.filename_;
360  openIfNeeded(false /* create_new */);
361  return *this;
362 }
363 
364 Page BlobFile::allocatePage(PageId &new_page_number) {
365  FileHeader header = readHeader();
366  Page new_page;
367 
368  new_page_number = header.num_pages;
369 
370  if (header.first_used_page == Page::INVALID_NUMBER) {
371  header.first_used_page = header.num_pages;
372  }
373 
374  ++header.num_pages;
375  //Fix set the 'new_page's page number to new_page_number before writing it to the disk
376  new_page.set_page_number(new_page_number);
377  writePage(new_page_number, new_page);
378  writeHeader(header);
379 
380  return new_page;
381 }
382 
383 Page BlobFile::readPage(const PageId page_number) const {
384  Page page;
385  stream_->seekg(pagePosition(page_number), std::ios::beg);
386  stream_->read(reinterpret_cast<char*>(&page), Page::SIZE);
387  return page;
388 }
389 
390 void BlobFile::writePage(const PageId new_page_number, const Page& new_page) {
391  stream_->seekp(pagePosition(new_page_number), std::ios::beg);
392  stream_->write(reinterpret_cast<const char*>(&new_page), Page::SIZE);
393  stream_->flush();
394 }
395 
396 //delePage should not be called for a blob_file, not supported
397 void BlobFile::deletePage(const PageId page_number) {
398  throw InvalidPageException(page_number, filename_);
399 }
400 
401 }
static std::streampos pagePosition(const PageId page_number)
Definition: file.h:175
static bool isOpen(const std::string &filename)
Definition: file.cpp:39
PageId first_free_page
Definition: file.h:43
static CountMap open_counts_
Definition: file.h:224
void deletePage(const PageId page_number)
Definition: file.cpp:276
std::string filename_
Definition: file.h:229
std::shared_ptr< std::fstream > stream_
Definition: file.h:234
PageId next_page_number() const
Definition: page.h:201
File(const std::string &name, const bool create_new)
Definition: file.cpp:67
Header metadata for files on disk which contain pages.
Definition: file.h:24
Page allocatePage(PageId &new_page_number)
Definition: file.cpp:364
void writePage(const PageId page_number, const Page &new_page)
Definition: file.cpp:390
void deletePage(const PageId page_number)
Definition: file.cpp:397
PageId next_page_number
Definition: page.h:59
static PageFile open(const std::string &filename)
Definition: file.cpp:139
BlobFile(const std::string &name, const bool create_new)
Definition: file.cpp:343
An exception that is thrown when a file creation is requested for a filename that already exists...
Class which represents a file in the filesystem containing database pages.
Definition: file.h:75
static const std::size_t DATA_SIZE
Definition: page.h:119
virtual ~File()
Definition: file.cpp:57
void writeHeader(const FileHeader &header)
Definition: file.cpp:125
Page readPage(const PageId page_number) const
Definition: file.cpp:383
std::uint32_t PageId
Identifier for a page in a file.
Definition: types.h:15
PageId num_pages
Definition: file.h:28
static const std::size_t SIZE
Definition: page.h:114
An exception that is thrown when a file operation is requested for a filename that doesn't exist...
An exception that is thrown when an attempt is made to access an invalid page in a file...
Class which represents a fixed-size database page containing records.
Definition: page.h:108
void writePage(const PageId page_number, const Page &new_page)
Definition: file.cpp:260
An exception that is thrown when a file deletion is requested for a filename that's currently open...
void openIfNeeded(const bool create_new)
Definition: file.cpp:78
Page readPage(const PageId page_number) const
Definition: file.cpp:238
BlobFile & operator=(const BlobFile &rhs)
Definition: file.cpp:355
FileIterator end()
Definition: file.cpp:312
PageId getFirstPageNo()
Definition: file.cpp:62
PageId page_number() const
Definition: page.h:194
Iterator for iterating over the pages in a file.
Definition: file_iterator.h:23
static void remove(const std::string &filename)
Definition: file.cpp:29
PageFile(const std::string &name, const bool create_new)
Definition: file.cpp:143
static PageFile create(const std::string &filename)
Definition: file.cpp:135
void close()
Definition: file.cpp:105
PageId num_free_pages
Definition: file.h:38
PageId first_used_page
Definition: file.h:33
static BlobFile open(const std::string &filename)
Definition: file.cpp:339
Page allocatePage(PageId &new_page_number)
Definition: file.cpp:165
static BlobFile create(const std::string &filename)
Definition: file.cpp:335
static const PageId INVALID_NUMBER
Definition: page.h:124
PageFile & operator=(const PageFile &rhs)
Definition: file.cpp:156
static StreamMap open_streams_
Definition: file.h:219
FileIterator begin()
Definition: file.cpp:307
FileHeader readHeader() const
Definition: file.cpp:118
PageId current_page_number
Definition: page.h:54
Header metadata in a page.
Definition: page.h:26
static bool exists(const std::string &filename)
Definition: file.cpp:46